Skip to content

fix(tz): reset fold to 0 for unambiguous local times - #1057

Open
saadkhantareen wants to merge 1 commit into
python-pendulum:masterfrom
saadkhantareen:fix/unambiguous-fold-zero
Open

saadkhantareen wants to merge 1 commit into
python-pendulum:masterfrom
saadkhantareen:fix/unambiguous-fold-zero

Conversation

@saadkhantareen

Copy link
Copy Markdown

Summary

Fixes #853.

Datetimes created for ordinary, unambiguous local times reported a bogus fold value of 1, and the value changed inconsistently depending on the operation performed:

>>> dt = pendulum.datetime(2024, 5, 1, tz="CET")
>>> dt.fold
1
>>> dt.add(minutes=1).fold
0
>>> dt.add(days=1).fold
1

Per PEP 495, fold must be 0 for local times that are not ambiguous.

Changes

  • Timezone.convert(): after normalizing a naive datetime, reset fold to 0 when the resulting local time is not ambiguous (its UTC offset is the same for fold=0 and fold=1). Ambiguous times keep the fold they were resolved with, so the existing default of resolving repeated times to their second occurrence is unchanged, as is the fold-based resolution of skipped times.
  • DateTime.set(): only carry the current fold over to the new time when the current time is actually ambiguous. Otherwise the meaningless fold of an unambiguous time decided how a new time falling in a DST gap or overlap was resolved; the default resolution of DateTime.create() now applies, which preserves the existing behavior of fluent setters such as at() around transitions.
  • Added regression tests covering Timezone.convert(), pendulum.datetime() with add(), ambiguous times keeping their fold, and set() on an ambiguous time.

Tests

$ PENDULUM_EXTENSIONS=0 python -m pytest tests/tz/test_timezone.py tests/datetime/test_fluent_setters.py -q
117 passed, 2 skipped

$ PENDULUM_EXTENSIONS=0 python -m pytest tests/ -q
1875 passed, 3 skipped

Pull Request Check List

  • Added tests for changed code.
  • Updated documentation for changed code.

DateTime.create() resolves new instances with fold=1 so that
ambiguous times default to their second occurrence, but
Timezone.convert() kept that fold value for every local time.
As a result, ordinary datetimes reported fold=1, and the value
changed inconsistently depending on the operation performed
(minute additions reset it, day additions kept it).

Timezone.convert() now sets fold to 0 when the converted local
time is not ambiguous, per PEP 495. DateTime.set() only carries
the current fold over to the new time when the current time is
actually ambiguous, so fluent setters keep resolving DST gaps
and overlaps with the default rules otherwise.
Copilot AI balanced review requested due to automatic review settings October 17, 2026 10:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

This branch has not been deployed

No deployments
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.

fold value behaves differently for different timedelta addition values

2 participants