From 6ea9b5d92e5f077c75ed6ebc9f7c10ac1484a5a3 Mon Sep 17 00:00:00 2001 From: isra17 Date: Mon, 20 Jan 2020 14:26:31 -0500 Subject: [PATCH 1/3] Fix inconsistencies in freezegun while running timezone locale --- freezegun/api.py | 15 +++++++++++++++ tests/test_datetimes.py | 13 ++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/freezegun/api.py b/freezegun/api.py index 13484ca4..57332d1b 100644 --- a/freezegun/api.py +++ b/freezegun/api.py @@ -21,6 +21,8 @@ MayaDT = None _TIME_NS_PRESENT = hasattr(time, 'time_ns') +_EPOCH = datetime.datetime(1970, 1, 1) +_EPOCHTZ = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc) real_time = time.time real_localtime = time.localtime @@ -344,6 +346,19 @@ def astimezone(self, tz=None): tz = tzlocal() return datetime_to_fakedatetime(real_datetime.astimezone(self, tz)) + @classmethod + def fromtimestamp(cls, t, tz=None): + if tz is None: + return real_datetime.fromtimestamp( + t, tz=datetime.timezone(cls._tz_offset()) + ).replace(tzinfo=None) + return real_datetime.fromtimestamp(t, tz=tz) + + def timestamp(self): + if self.tzinfo is None: + return (self - _EPOCH).total_seconds() + return (self - _EPOCHTZ).total_seconds() + @classmethod def now(cls, tz=None): now = cls._time_to_freeze() or real_datetime.now() diff --git a/tests/test_datetimes.py b/tests/test_datetimes.py index 4689dcf0..8f2d1e69 100644 --- a/tests/test_datetimes.py +++ b/tests/test_datetimes.py @@ -456,9 +456,7 @@ def test_nested_context_manager(): def _assert_datetime_date_and_time_are_all_equal(expected_datetime): assert datetime.datetime.now() == expected_datetime assert datetime.date.today() == expected_datetime.date() - datetime_from_time = datetime.datetime.fromtimestamp(time.time()) - timezone_adjusted_datetime = datetime_from_time + datetime.timedelta(seconds=time.timezone) - assert timezone_adjusted_datetime == expected_datetime + assert datetime.datetime.fromtimestamp(time.time()) == expected_datetime def test_nested_context_manager_with_tz_offsets(): @@ -693,7 +691,6 @@ def test_time_ns(): assert time.time_ns() != expected_timestamp * 1e9 -@pytest.mark.skip("timezone handling is currently incorrect") def test_compare_datetime_and_time_with_timezone(monkeypatch): """ Compare the result of datetime.datetime.now() and time.time() in a non-UTC timezone. These @@ -704,9 +701,11 @@ def test_compare_datetime_and_time_with_timezone(monkeypatch): m.setenv("TZ", "Europe/Berlin") time.tzset() - dt1 = datetime.datetime.now() - dt2 = datetime.datetime.fromtimestamp(time.time()) - assert dt1 == dt2 + now = datetime.datetime.now() + assert now == datetime.datetime.fromtimestamp(time.time()) + assert now == datetime.datetime.utcfromtimestamp(time.time()) + assert now == datetime.datetime.utcnow() + assert now.timestamp() == time.time() finally: time.tzset() # set the timezone back to what is was before From 76f1cf0e3acb00ab12b95928f9189eac961bd9fd Mon Sep 17 00:00:00 2001 From: isra17 Date: Mon, 20 Jan 2020 14:31:50 -0500 Subject: [PATCH 2/3] Use tz_offsfet in timestamp operation --- freezegun/api.py | 2 +- tests/test_datetimes.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/freezegun/api.py b/freezegun/api.py index 57332d1b..3214ef72 100644 --- a/freezegun/api.py +++ b/freezegun/api.py @@ -356,7 +356,7 @@ def fromtimestamp(cls, t, tz=None): def timestamp(self): if self.tzinfo is None: - return (self - _EPOCH).total_seconds() + return (self - _EPOCH - self._tz_offset()).total_seconds() return (self - _EPOCHTZ).total_seconds() @classmethod diff --git a/tests/test_datetimes.py b/tests/test_datetimes.py index 8f2d1e69..ea21331a 100644 --- a/tests/test_datetimes.py +++ b/tests/test_datetimes.py @@ -710,6 +710,17 @@ def test_compare_datetime_and_time_with_timezone(monkeypatch): time.tzset() # set the timezone back to what is was before +def test_timestamp_with_tzoffset(): + with freeze_time("2000-01-01", tz_offset=6): + utcnow = datetime.datetime(2000, 1, 1, 0) + now = datetime.datetime(2000, 1, 1, 6) + assert now == datetime.datetime.now() + assert now == datetime.datetime.fromtimestamp(time.time()) + assert now.timestamp() == time.time() + + assert utcnow == datetime.datetime.utcfromtimestamp(time.time()) + assert utcnow == datetime.datetime.utcnow() + @pytest.mark.skip("timezone handling is currently incorrect") def test_datetime_in_timezone(monkeypatch): """ From b88d00426ee9d8fe727b7195d362eb90d4be1a56 Mon Sep 17 00:00:00 2001 From: isra17 Date: Mon, 20 Jan 2020 15:02:03 -0500 Subject: [PATCH 3/3] Make fix python2 compatible --- freezegun/api.py | 7 ++++--- tests/test_datetimes.py | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/freezegun/api.py b/freezegun/api.py index 3214ef72..773339c5 100644 --- a/freezegun/api.py +++ b/freezegun/api.py @@ -1,3 +1,4 @@ +import dateutil import datetime import functools import sys @@ -22,7 +23,7 @@ _TIME_NS_PRESENT = hasattr(time, 'time_ns') _EPOCH = datetime.datetime(1970, 1, 1) -_EPOCHTZ = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc) +_EPOCHTZ = datetime.datetime(1970, 1, 1, tzinfo=dateutil.tz.UTC) real_time = time.time real_localtime = time.localtime @@ -350,9 +351,9 @@ def astimezone(self, tz=None): def fromtimestamp(cls, t, tz=None): if tz is None: return real_datetime.fromtimestamp( - t, tz=datetime.timezone(cls._tz_offset()) + t, tz=dateutil.tz.tzoffset("freezegun", cls._tz_offset()) ).replace(tzinfo=None) - return real_datetime.fromtimestamp(t, tz=tz) + return datetime_to_fakedatetime(real_datetime.fromtimestamp(t, tz)) def timestamp(self): if self.tzinfo is None: diff --git a/tests/test_datetimes.py b/tests/test_datetimes.py index ea21331a..b59bd6dd 100644 --- a/tests/test_datetimes.py +++ b/tests/test_datetimes.py @@ -5,6 +5,7 @@ import locale import sys from unittest import SkipTest +from dateutil.tz import UTC import pytest from tests import utils @@ -713,10 +714,12 @@ def test_compare_datetime_and_time_with_timezone(monkeypatch): def test_timestamp_with_tzoffset(): with freeze_time("2000-01-01", tz_offset=6): utcnow = datetime.datetime(2000, 1, 1, 0) + nowtz = datetime.datetime(2000, 1, 1, 0, tzinfo=UTC) now = datetime.datetime(2000, 1, 1, 6) assert now == datetime.datetime.now() assert now == datetime.datetime.fromtimestamp(time.time()) assert now.timestamp() == time.time() + assert nowtz.timestamp() == time.time() assert utcnow == datetime.datetime.utcfromtimestamp(time.time()) assert utcnow == datetime.datetime.utcnow()