Skip to content

Fix #807: handle pytz.FixedOffset in pendulum.instance()#981

Open
apoorvdarshan wants to merge 1 commit into
python-pendulum:masterfrom
apoorvdarshan:fix-807-pytz-fixed-offset
Open

Fix #807: handle pytz.FixedOffset in pendulum.instance()#981
apoorvdarshan wants to merge 1 commit into
python-pendulum:masterfrom
apoorvdarshan:fix-807-pytz-fixed-offset

Conversation

@apoorvdarshan

Copy link
Copy Markdown

Summary

Fixes #807. pendulum.instance() raised AttributeError: 'NoneType' object has no attribute 'lower' for a datetime whose tzinfo is a pytz.FixedOffset:

import datetime, pytz, pendulum
pendulum.instance(datetime.datetime(2021, 2, 3, tzinfo=pytz.FixedOffset(60)))
# AttributeError: 'NoneType' object has no attribute 'lower'

Root cause

_safe_timezone() detects pytz timezones by the presence of a localize() method and then uses their .zone name:

elif hasattr(obj, "localize"):
    obj = obj.zone

Named pytz zones (e.g. America/New_York) expose a zone string, but fixed-offset zones (pytz.FixedOffset) have zone = None. That None was passed to timezone(None), which does name.lower() and crashes.

Fix

When a pytz zone has no zone name, fall back to its UTC offset (the same approach already used for other offset-only tzinfo objects further down the same branch). Named zones are unaffected.

Verification

  • Added test_instance_with_pytz_fixed_offset (positive and negative offsets), guarded by pytest.importorskip("pytz") since pytz is not a test dependency. It reproduces the exact AttributeError on master and passes with this change.
  • Full test suite: 1834 passed, 3 skipped (unchanged from baseline).
  • Confirmed no regression for named pytz zones, pytz.UTC, zoneinfo, dateutil, and stdlib datetime.timezone.
  • ruff check, ruff format --check, and mypy (strict) clean on the changed files.

Disclosure: prepared with AI assistance; reviewed and verified locally against the test suite.

pendulum.instance() raised 'AttributeError: NoneType object has no
attribute lower' for datetimes whose tzinfo is a pytz.FixedOffset.

_safe_timezone() detects pytz zones via their localize() method and then
uses their .zone name. Named pytz zones carry a zone string, but fixed-offset
zones (pytz.FixedOffset) have zone = None, so that None was passed to
timezone(), which crashed. Fall back to the tzinfo's UTC offset when there is
no zone name, mirroring how other offset-only tzinfos are handled.

Added a regression test (skipped when pytz is unavailable).
@apoorvdarshan apoorvdarshan force-pushed the fix-807-pytz-fixed-offset branch from 87af05f to 47718d7 Compare July 6, 2026 10:01
Comment thread tests/test_main.py
assert now.timezone_name == "+02:00"


def test_instance_with_pytz_fixed_offset() -> None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand it correctly, without pytz, this test will never execute. We are not installing pytz in our tests/pipelines. Hence, this test is pointless.

Comment thread CHANGELOG.md

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please drop the changelog entry - we don't add do "unreleased" in the changelog.

@codspeed-hq

codspeed-hq Bot commented Jul 6, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 1 untouched benchmark


Comparing apoorvdarshan:fix-807-pytz-fixed-offset (47718d7) with master (cbab60c)

Open in CodSpeed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

instance() fails for datetimes with pytz.FixedOffsets

2 participants