Why are these two datetimes not equal?

107 Views Asked by At
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

chi = ZoneInfo("America/Chicago")
nyc = ZoneInfo("America/New_York")
dt1 = datetime(2024, 3, 10, 3, 30, tzinfo=chi)
dt2 = datetime(2024, 3, 10, 2, 30, tzinfo=chi)
print(dt1 == dt1.astimezone(nyc))
print(dt2 == dt2.astimezone(nyc))

Actual result:

True
False

Expected result:

True
True

Why is False returned in one of those cases? In both cases it's comparing the same datetime, only adjusted to a different zone.

The same thing also happens in the fold:

from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

chi = ZoneInfo("America/Chicago")
nyc = ZoneInfo("America/New_York")
dt1 = datetime(2024, 11, 3, 2, 30, tzinfo=chi)
dt2 = datetime(2024, 11, 3, 1, 30, tzinfo=chi)
print(dt1 == dt1.astimezone(nyc))
print(dt2 == dt2.astimezone(nyc))
1

There are 1 best solutions below

5
FObersteiner On

Python's datetime by design allows to create non-existing datetime, like 2:30 am on Mar 10 2024 Chicago time in your example. That's the root of the evil here. You cannot do a correct conversion to another tz without (silently) shifting to a valid datetime (3:30 am in this case). This shift is what's causing the comparison to be False in the second case. To illustrate, you can do a conversion roundtrip:

from datetime import datetime
from zoneinfo import ZoneInfo

chi = ZoneInfo("America/Chicago")
nyc = ZoneInfo("America/New_York")

dt1 = datetime(2024, 3, 10, 3, 30, tzinfo=chi)
dt2 = datetime(2024, 3, 10, 2, 30, tzinfo=chi) # non-existent !

print(dt1 == dt2) # False

print(dt1.astimezone(nyc)) # 2024-03-10 04:30:00-04:00
print(dt2.astimezone(nyc)) # also 2024-03-10 04:30:00-04:00 !

print(dt1.astimezone(nyc).astimezone(chi)) # 2024-03-10 03:30:00-05:00
print(dt2.astimezone(nyc).astimezone(chi)) # also 2024-03-10 03:30:00-05:00 !

dt2 converted to NY time converted back to Chicago time comes back with different (now valid) fields - 2 am became 3 am. Those times are not equal and the comparison correctly returns False.


For completeness, here's what happens if you put this example on the other side of a DST transition, DST active to inactive:

dt1 = datetime(2024, 11, 3, 1, 30, tzinfo=chi)  # ambiguous; exists twice (UTC-5 and UTC-6)
dt2 = datetime(2024, 11, 3, 2, 30, tzinfo=chi)

print(dt1, dt1.astimezone(nyc)) # 2024-11-03 01:30:00-05:00 2024-11-03 01:30:00-05:00
print(dt1.fold, dt1.astimezone(nyc).fold)  # 0, 1

print(dt1 == dt1.astimezone(nyc)) # False
print(dt2 == dt2.astimezone(nyc)) # True

You notice for dt1, both Chicago and NY have the same wall time and UTC offset - but those datetimes fall on different sides of the "DST fold" since the time zones are not the same. NY time is ahead of Chicago's, so it's already experiencing Winter time, while Chicago is still on the Summer side. Therefore, the datetimes are not identical and the comparison returns False.