Fix start_of("day") landing on the previous day when a zone springs forward at midnight - #994
Open
youdie006 wants to merge 1 commit into
Open
Conversation
…orward at midnight
_start_of_day resets to midnight via self.at(0, 0, 0, 0), which propagates the
receiver's fold. On a timezone that springs forward exactly at midnight (e.g.
Chile/Continental, where 2025-09-07 00:00:00 does not exist), reaching that day
through .add(days=1) yields fold=0. For a nonexistent (skipped) local time with
fold=0, the timezone resolver shifts the instant backward, so midnight lands at
23:00 on the previous day and start_of("day") is wrong and non-idempotent.
After resetting to midnight, if the calendar day changed then midnight was
skipped, so re-create the day's first valid instant resolving forward (fold=1).
The guard fires only when midnight actually moved to the previous day, so all
other zones/dates are byte-identical (verified against UTC, America/New_York,
Asia/Tokyo, Europe/London, Europe/Vienna's 02:00 gap, and America/Sao_Paulo's
midnight fall-back). start_of("week") is covered via delegation.
Fixes python-pendulum#915.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request Check List
Fixes #915.
Problem
On a timezone that springs forward exactly at midnight (e.g.
Chile/Continental, where2025-09-07 00:00:00does not exist),start_of("day")returns an instant on the previous calendar day and is not idempotent:Expected the first valid instant of Sep 7,
2025-09-07 01:00:00-03:00. Zones whose transition is not at midnight (e.g.Europe/Viennaat 02:00) are unaffected.Root cause
_start_of_dayresets to midnight viaself.at(0, 0, 0, 0), which propagates the receiver'sfold. Reaching Sep 7 through.add(days=1)yieldsfold=0. For a nonexistent (skipped) local time withfold=0, the timezone resolver (tz/timezone.py) shifts the instant backward (offset_before - offset_after), so midnight lands at23:00on the previous day.start_ofis therefore wrong and non-idempotent for that day.Fix
Scoped to
_start_of_day: after resetting to midnight, if the calendar day changed then midnight was skipped, so re-create the day's first instant resolving forward (fold=1). The guard fires only when midnight actually moved to the previous day, so all other zones/dates are byte-identical (verified against UTC, America/New_York, Asia/Tokyo, Europe/London, Europe/Vienna's 02:00 gap, and America/Sao_Paulo's midnight fall-back). Because_start_of_weekdelegates tostart_of("day"), this also coversstart_of("week").Note: the same latent midnight-gap could in principle affect
_start_of_month/year/decade/century; this PR deliberately keeps the change minimal and scoped to_start_of_day(the path reported in #915).Tests
Added
test_start_of_day_when_midnight_does_not_exist. Red without the fix (assert 6 == 7, result was2025-09-06 23:00), green with it. Full suite:1459 passed, 3 skipped(tests/datetime tests/tz tests/date).This change was prepared with AI assistance and reviewed by me before submission.