Skip to content

Commit 47b0fa8

Browse files
committed
fix: keep naive datetimes naive when building an Interval
`DateTime.diff()` constructs an `Interval`, whose `__init__` normalised the start and end values via `pendulum.instance()`. That call used the default `tz=UTC`, so a naive stdlib `datetime` was turned into a UTC-aware `DateTime`. When the receiver was itself naive, the subsequent comparison mixed an offset-naive value with an offset-aware one and raised: TypeError: can't compare offset-naive and offset-aware datetimes `Interval.__new__` already validates that both endpoints share the same awareness, so forcing a timezone here was both unnecessary and the source of the bug. Pass each value's own `tzinfo` to `instance()` instead: naive inputs stay naive and aware inputs keep their timezone (`DateTime.instance` resolves `tz` as `dt.tzinfo or tz`), so behaviour for aware datetimes is unchanged. Fixes #880
1 parent b99bd14 commit 47b0fa8

2 files changed

Lines changed: 14 additions & 2 deletions

File tree

src/pendulum/interval.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def __init__(self, start: _T, end: _T, absolute: bool = False) -> None:
122122
_start: _T
123123
if not isinstance(start, pendulum.Date):
124124
if isinstance(start, datetime):
125-
start = cast("_T", pendulum.instance(start))
125+
start = cast("_T", pendulum.instance(start, tz=start.tzinfo))
126126
else:
127127
start = cast("_T", pendulum.date(start.year, start.month, start.day))
128128

@@ -148,7 +148,7 @@ def __init__(self, start: _T, end: _T, absolute: bool = False) -> None:
148148
_end: _T
149149
if not isinstance(end, pendulum.Date):
150150
if isinstance(end, datetime):
151-
end = cast("_T", pendulum.instance(end))
151+
end = cast("_T", pendulum.instance(end, tz=end.tzinfo))
152152
else:
153153
end = cast("_T", pendulum.date(end.year, end.month, end.day))
154154

tests/datetime/test_diff.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,18 @@ def test_diff_in_seconds_with_timezones():
198198
assert dt_ottawa.diff(dt_vancouver).in_seconds() == 3 * 60 * 60
199199

200200

201+
def test_diff_naive_with_naive_stdlib_datetime():
202+
# A naive DateTime diffed against a naive stdlib datetime should not
203+
# force the latter to UTC, which used to make it offset-aware and raise
204+
# "can't compare offset-naive and offset-aware datetimes". See #880.
205+
dt = pendulum.naive(2016, 7, 5, 12, 0, 0)
206+
other = datetime(2016, 7, 5, 13, 0, 0)
207+
208+
assert dt.diff(other).in_seconds() == 3600
209+
assert dt.diff(other, False).in_seconds() == 3600
210+
assert other.tzinfo is None
211+
212+
201213
def test_diff_for_humans_now_and_second():
202214
with pendulum.travel_to(pendulum.datetime(2012, 1, 1, 1, 2, 3), freeze=True):
203215
assert pendulum.now().diff_for_humans() == "a few seconds ago"

0 commit comments

Comments
 (0)