| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
…ons to UTC via astimezone and add regression test for DST fold
There was a problem hiding this comment.
Fixes incorrect relational ordering of pendulum.DateTime values during DST “fold” transitions by normalizing operands to UTC before performing <, <=, >, and >= comparisons (aligning behavior with the documented “compare in UTC” semantics).
Changes:
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/pendulum/datetime.py | Implements UTC-normalized rich ordering operations intended to fix fold-related mis-ordering. |
| tests/datetime/test_comparison.py | Adds a regression test for fold-related < ordering behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Sorry, something went wrong.
| # Rich ordering operations | ||
| def __lt__(self, other: datetime.datetime) -> bool: # type: ignore[override] | ||
| if not isinstance(other, datetime.datetime): | ||
| return NotImplemented | ||
|
|
||
| # Normalize both operands to UTC stdlib datetimes using base implementation | ||
| self_utc = datetime.datetime.astimezone(self, UTC) | ||
| other_dt = other if isinstance(other, DateTime) else DateTime.instance(other) | ||
| other_utc = datetime.datetime.astimezone(other_dt, UTC) | ||
|
|
||
| return datetime.datetime.__lt__(self_utc, other_utc) | ||
|
|
||
| def __le__(self, other: datetime.datetime) -> bool: # type: ignore[override] | ||
| if not isinstance(other, datetime.datetime): | ||
| return NotImplemented | ||
|
|
||
| self_utc = datetime.datetime.astimezone(self, UTC) | ||
| other_dt = other if isinstance(other, DateTime) else DateTime.instance(other) | ||
| other_utc = datetime.datetime.astimezone(other_dt, UTC) | ||
|
|
||
| return datetime.datetime.__le__(self_utc, other_utc) |
| def __gt__(self, other: datetime.datetime) -> bool: # type: ignore[override] | ||
| if not isinstance(other, datetime.datetime): | ||
| return NotImplemented | ||
|
|
||
| self_utc = datetime.datetime.astimezone(self, UTC) | ||
| other_dt = other if isinstance(other, DateTime) else DateTime.instance(other) | ||
| other_utc = datetime.datetime.astimezone(other_dt, UTC) | ||
|
|
||
| return datetime.datetime.__gt__(self_utc, other_utc) | ||
|
|
||
| def __ge__(self, other: datetime.datetime) -> bool: # type: ignore[override] | ||
| if not isinstance(other, datetime.datetime): | ||
| return NotImplemented | ||
|
|
||
| self_utc = datetime.datetime.astimezone(self, UTC) | ||
| other_dt = other if isinstance(other, DateTime) else DateTime.instance(other) | ||
| other_utc = datetime.datetime.astimezone(other_dt, UTC) | ||
|
|
||
| return datetime.datetime.__ge__(self_utc, other_utc) |
| assert d1.timestamp() > d2.timestamp() | ||
| assert not (d1 < d2) |
| Back | FazBrowse Home | New Git URL |
Summary
Fix incorrect ordering comparisons for DateTime instances during DST fold transitions.
During the repeated hour after a daylight saving time (DST) rollback, two datetimes can represent different UTC instants while sharing the same timezone object. In this situation, ordering comparisons (<, <=, >, >=) can return results that do not reflect the actual chronological order.
This change normalizes both operands to UTC before performing ordering comparisons, ensuring that comparisons are based on the represented instant rather than the ambiguous local wall-clock time.
Problem
The issue can be reproduced with datetimes created during the DST fall-back transition:
Although d1 represents a later UTC instant than d2, the ordering comparison reports the opposite result.
After reproducing the issue locally, I verified that the problem only affects relational ordering operators. Equality behavior remains unchanged.
Solution
The ordering operators now normalize both operands to UTC before performing the comparison.
The implementation:
The change is intentionally limited to ordering comparisons and does not modify equality behavior.
Why this approach?
Pendulum's comparison documentation states that comparisons are performed in UTC. Normalizing both operands before ordering comparisons keeps the implementation aligned with that documented behavior while limiting the scope of the change to relational operators only.
Testing
Added a regression test covering the DST fold scenario:
Validation performed locally:
This change is intentionally scoped to ordering comparisons and does not modify equality semantics.
Fixes #855