Skip to content

Commit

Permalink
Fix timestamp parsing (#415)
Browse files Browse the repository at this point in the history
Fixes #407
  • Loading branch information
Gobot1234 authored and cetanu committed Oct 16, 2023
1 parent 0e936eb commit 9b2d0e0
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 5 deletions.
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,13 @@ help = "Run tests"
cmd = "mypy src --ignore-missing-imports"
help = "Check types with mypy"

[tool.poe.tasks]
_black = "black . --exclude tests/output_ --target-version py310"
_isort = "isort . --extend-skip-glob 'tests/output_*/**/*'"

[tool.poe.tasks.format]
cmd = "black . --exclude tests/output_ --target-version py310"
help = "Apply black formatting to source code"
sequence = ["_black", "_isort"]
help = "Apply black and isort formatting to source code"

[tool.poe.tasks.docs]
cmd = "sphinx-build docs docs/build"
Expand Down
6 changes: 4 additions & 2 deletions src/betterproto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1885,13 +1885,15 @@ def delta_to_json(delta: timedelta) -> str:
class _Timestamp(Timestamp):
@classmethod
def from_datetime(cls, dt: datetime) -> "_Timestamp":
seconds = int(dt.timestamp())
# apparently 0 isn't a year in [0, 9999]??
seconds = int((dt - DATETIME_ZERO).total_seconds())
nanos = int(dt.microsecond * 1e3)
return cls(seconds, nanos)

def to_datetime(self) -> datetime:
ts = self.seconds + (self.nanos / 1e9)
return datetime.fromtimestamp(ts, tz=timezone.utc)
# if datetime.fromtimestamp ever supports -62135596800 use that instead see #407
return DATETIME_ZERO + timedelta(seconds=ts)

@staticmethod
def timestamp_to_json(dt: datetime) -> str:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
syntax = "proto3";

import "google/protobuf/timestamp.proto";
package google_impl_behavior_equivalence;

message Foo { int64 bar = 1; }
Expand All @@ -12,6 +13,10 @@ message Test {
}
}

message Spam {
google.protobuf.Timestamp ts = 1;
}

message Request { Empty foo = 1; }

message Empty {}
message Empty {}
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
from datetime import (
datetime,
timezone,
)

import pytest
from google.protobuf import json_format
from google.protobuf.timestamp_pb2 import Timestamp

import betterproto
from tests.output_betterproto.google_impl_behavior_equivalence import (
Empty,
Foo,
Request,
Spam,
Test,
)
from tests.output_reference.google_impl_behavior_equivalence.google_impl_behavior_equivalence_pb2 import (
Empty as ReferenceEmpty,
Foo as ReferenceFoo,
Request as ReferenceRequest,
Spam as ReferenceSpam,
Test as ReferenceTest,
)

Expand Down Expand Up @@ -59,6 +67,19 @@ def test_bytes_are_the_same_for_oneof():
assert isinstance(message_reference2.foo, ReferenceFoo)


@pytest.mark.parametrize("dt", (datetime.min.replace(tzinfo=timezone.utc),))
def test_datetime_clamping(dt): # see #407
ts = Timestamp()
ts.FromDatetime(dt)
assert bytes(Spam(dt)) == ReferenceSpam(ts=ts).SerializeToString()
message_bytes = bytes(Spam(dt))

assert (
Spam().parse(message_bytes).ts.timestamp()
== ReferenceSpam.FromString(message_bytes).ts.seconds
)


def test_empty_message_field():
message = Request()
reference_message = ReferenceRequest()
Expand Down

0 comments on commit 9b2d0e0

Please sign in to comment.