Skip to content

Commit

Permalink
Merge pull request mozilla#491 from bendk/mozillagh-490
Browse files Browse the repository at this point in the history
Fixed timestamp conversion during DST
  • Loading branch information
bendk authored Jun 24, 2021
2 parents 817123f + 13b35bb commit 10a26d6
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
### What's Changed

- Both python and ruby backends now handle U16 correctly.
- Python timestamps will now be in UTC and timezone-aware rather than naive.

## v0.12.0 (2021-06-14)

Expand Down
21 changes: 11 additions & 10 deletions fixtures/uniffi-fixture-time/tests/bindings/test_chronological.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,40 @@
import sys

# Test passing timestamp and duration while returning timestamp
assert add(datetime.fromtimestamp(100.000001), timedelta(seconds=1, microseconds=1)).timestamp() == 101.000002
assert add(datetime.fromtimestamp(100.000001, timezone.utc), timedelta(seconds=1, microseconds=1)).timestamp() == 101.000002

# Test passing timestamp while returning duration
assert diff(datetime.fromtimestamp(101.000002), datetime.fromtimestamp(100.000001)) == timedelta(seconds=1, microseconds=1)
assert diff(datetime.fromtimestamp(101.000002, timezone.utc), datetime.fromtimestamp(100.000001, timezone.utc)) == timedelta(seconds=1, microseconds=1)

# Test pre-epoch timestamps
assert add(datetime.fromisoformat('1955-11-05T00:06:00.283001'), timedelta(seconds=1, microseconds=1)) == datetime.fromisoformat('1955-11-05T00:06:01.283002')
assert add(datetime.fromisoformat('1955-11-05T00:06:00.283001+00:00'), timedelta(seconds=1, microseconds=1)) == datetime.fromisoformat('1955-11-05T00:06:01.283002+00:00')

# Test exceptions are propagated
try:
diff(datetime.fromtimestamp(100), datetime.fromtimestamp(101))
diff(datetime.fromtimestamp(100, timezone.utc), datetime.fromtimestamp(101, timezone.utc))
assert(not("Should have thrown a TimeDiffError exception!"))
except ChronologicalError.TimeDiffError:
# It's okay!
pass

# Test near max timestamp bound, no microseconds due to python floating point precision issues
assert add(datetime(MAXYEAR, 12, 31, 23, 59, 59, 0), timedelta(seconds=0)) == datetime(MAXYEAR, 12, 31, 23, 59, 59, 0)
assert add(datetime(MAXYEAR, 12, 31, 23, 59, 59, 0, tzinfo=timezone.utc), timedelta(seconds=0)) == datetime(MAXYEAR, 12, 31, 23, 59, 59, 0, tzinfo=timezone.utc)

# Test overflow at max timestamp bound
try:
add(datetime(MAXYEAR, 12, 31, 23, 59, 59, 0), timedelta(seconds=1))
add(datetime(MAXYEAR, 12, 31, 23, 59, 59, 0, tzinfo=timezone.utc), timedelta(seconds=1))
assert(not("Should have thrown a ValueError exception!"))
except OverflowError:
# It's okay!
pass

# Test that rust timestamps behave like kotlin timestamps
pythonBefore = datetime.now()
pythonBefore = datetime.now(timezone.utc)
rustNow = now()
pythonAfter = datetime.now()
pythonAfter = datetime.now(timezone.utc)

assert pythonBefore <= rustNow <= pythonAfter

# Test that uniffi returns naive datetime
assert now().tzinfo is None
# Test that uniffi returns UTC times
assert now().tzinfo is timezone.utc
assert abs(datetime.now(timezone.utc) - now()) <= timedelta(seconds=1)
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@ def writeString(self, v):
{% when Type::Timestamp -%}

def write{{ canonical_type_name }}(self, v):
if v >= datetime.datetime.fromtimestamp(0):
if v >= datetime.datetime.fromtimestamp(0, datetime.timezone.utc):
sign = 1
delta = v - datetime.datetime.fromtimestamp(0)
delta = v - datetime.datetime.fromtimestamp(0, datetime.timezone.utc)
else:
sign = -1
delta = datetime.datetime.fromtimestamp(0) - v
delta = datetime.datetime.fromtimestamp(0, datetime.timezone.utc) - v

seconds = delta.seconds + delta.days * 24 * 3600
nanoseconds = delta.microseconds * 1000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,13 @@ def readString(self):
def read{{ canonical_type_name }}(self):
seconds = self._unpack_from(8, ">q")
microseconds = self._unpack_from(4, ">I") / 1000
# Use fromtimestamp(0) then add the seconds using a timedelta. This
# ensures that we get OverflowError rather than ValueError when
# seconds is too large.
if seconds >= 0:
return datetime.datetime.fromtimestamp(0, tz=None) + datetime.timedelta(seconds=seconds, microseconds=microseconds)
return datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc) + datetime.timedelta(seconds=seconds, microseconds=microseconds)
else:
return datetime.datetime.fromtimestamp(0, tz=None) - datetime.timedelta(seconds=-seconds, microseconds=microseconds)
return datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc) - datetime.timedelta(seconds=-seconds, microseconds=microseconds)

{% when Type::Duration -%}
# The Duration type.
Expand Down

0 comments on commit 10a26d6

Please sign in to comment.