diff --git a/CHANGELOG.md b/CHANGELOG.md index 58bb1312..5c0d68a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## [Unreleased] + +### Changed +- Fixed `pendulum.instance()` raising `AttributeError` for datetimes with a `pytz.FixedOffset` timezone [#981](https://github.com/python-pendulum/pendulum/pull/981) + ## [3.2.0] - 2026-01-30 ### Added diff --git a/src/pendulum/__init__.py b/src/pendulum/__init__.py index 16ae0865..71425cd9 100644 --- a/src/pendulum/__init__.py +++ b/src/pendulum/__init__.py @@ -105,7 +105,17 @@ def _safe_timezone( obj = obj.key # pytz elif hasattr(obj, "localize"): - obj = obj.zone # type: ignore[attr-defined] + # Named pytz zones expose a ``zone`` name, but fixed-offset zones + # (``pytz.FixedOffset``) have ``zone = None``; use their offset. + if obj.zone is not None: # type: ignore[attr-defined] + obj = obj.zone # type: ignore[attr-defined] + else: + offset = obj.utcoffset(dt) + + if offset is None: + offset = _datetime.timedelta(0) + + obj = int(offset.total_seconds()) elif obj.tzname(None) == "UTC": return UTC else: diff --git a/tests/test_main.py b/tests/test_main.py index 76033450..e7a5ac29 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -5,6 +5,9 @@ from datetime import date from datetime import datetime from datetime import time +from datetime import timedelta + +import pytest from dateutil import tz @@ -36,6 +39,18 @@ def test_instance_with_aware_datetime_any_tzinfo() -> None: assert now.timezone_name == "+02:00" +def test_instance_with_pytz_fixed_offset() -> None: + # ``pytz.FixedOffset`` has a ``localize`` method but no ``zone`` name, + # which used to raise ``AttributeError`` in ``_safe_timezone`` (#807). + pytz = pytest.importorskip("pytz") + + dt = pendulum.instance(datetime(2021, 2, 3, tzinfo=pytz.FixedOffset(60))) + assert dt.utcoffset() == timedelta(minutes=60) + + dt = pendulum.instance(datetime(2021, 2, 3, tzinfo=pytz.FixedOffset(-330))) + assert dt.utcoffset() == timedelta(minutes=-330) + + def test_instance_with_date() -> None: dt = pendulum.instance(date(2022, 12, 23))