From 13ebb6fe7a5cb5a76509773fea98d282e8317f7e Mon Sep 17 00:00:00 2001 From: Carey Metcalfe Date: Tue, 7 Nov 2017 21:30:52 -0500 Subject: [PATCH] Fix instance() behaviour with pytz offsets `pytz` has a class of timezone called a `FixedOffset`. This class only stores an offset, not a zone name. When calling `instance()` on a datetime that was using one of these `FixedOffset` objects as a timezone, Pendulum would totally ignore the offset and use the local timezone instead. This would happen even if a `tz` argument was directly supplied to the `instance()` call. Additionally, the fallback to a fixed offset would always use the datetime's timezone, even it if was `None` and a `tz` argument was supplied to the `instance()` call. This commit makes 2 small changes: - The `zone` property of the "effective timezone" (the datetime's `tzinfo` with a fallback to the passed in `tz`) is only used if it is non-null. This fixes using `FixedOffset` objects as timezones. - The fallback to a fixed offset now uses the offset of the "effective timezone" instead of the datetime's offset. This correctly handles cases where the timezone on the datetime is `None` and a fallback is provided. --- pendulum/pendulum.py | 4 ++-- tests/pendulum_tests/test_construct.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/pendulum/pendulum.py b/pendulum/pendulum.py index c73e8c6a..74a04f5b 100644 --- a/pendulum/pendulum.py +++ b/pendulum/pendulum.py @@ -205,13 +205,13 @@ def instance(cls, dt, tz=UTC): # Checking for pytz/tzinfo if isinstance(tz, datetime.tzinfo) and not isinstance(tz, (Timezone, TimezoneInfo)): # pytz - if hasattr(tz, 'localize'): + if hasattr(tz, 'localize') and tz.zone: tz = tz.zone else: # We have no sure way to figure out # the timezone name, we fallback # on a fixed offset - tz = dt.utcoffset().total_seconds() / 3600 + tz = tz.utcoffset(dt).total_seconds() / 3600 return cls( dt.year, dt.month, dt.day, diff --git a/tests/pendulum_tests/test_construct.py b/tests/pendulum_tests/test_construct.py index a1036383..ffb0059d 100644 --- a/tests/pendulum_tests/test_construct.py +++ b/tests/pendulum_tests/test_construct.py @@ -135,6 +135,25 @@ def test_instance_timezone_aware_datetime_pytz(self): ) self.assertEqual('Europe/Paris', now.timezone_name) + def test_instance_timezone_aware_datetime_pytz_offset(self): + # Impossible timezone of +21 (won't accidentally match the local offset) + fixed_offset = pytz.FixedOffset(21 * 60) + + now = Pendulum.instance( + datetime.now(fixed_offset) + ) + self.assertEqual(21, now.offset_hours) + + now = Pendulum.instance( + datetime.now(), fixed_offset + ) + self.assertEqual(21, now.offset_hours) + + now = Pendulum.instance( + datetime.now(fixed_offset), pytz.timezone('Europe/Paris') + ) + self.assertEqual(21, now.offset_hours) + def test_instance_timezone_aware_datetime_any_tzinfo(self): dt = datetime(2016, 8, 7, 12, 34, 56, tzinfo=tz.gettz('Europe/Paris')) now = Pendulum.instance(dt)