[Python-Dev] Status on PEP-431 Timezones (original) (raw)

Akira Li 4kir4.1i at gmail.com
Wed Apr 15 21:53:16 CEST 2015


Lennart Regebro <regebro at gmail.com> writes:

OK, so I realized another thing today, and that is that arithmetic doesn't necessarily round trip.

For example, 2002-10-27 01:00 US/Eastern comes both in DST and STD. But 2002-10-27 01:00 US/Eastern STD minus two days is 2002-10-25 01:00 US/Eastern DST

"two days" is ambiguous here. It is incorrect if you mean 48 hours (the difference is 49 hours):

#!/usr/bin/env python3 from datetime import datetime, timedelta import pytz

tz = pytz.timezone('US/Eastern') then_isdst = False # STD then = tz.localize(datetime(2002, 10, 27, 1), is_dst=then_isdst) now = tz.localize(datetime(2002, 10, 25, 1), is_dst=None) # no utc transition print((then - now) // timedelta(hours=1))

-> 49

However, 2002-10-25 01:00 US/Eastern DST plus two days is 2002-10-27 01:00 US/Eastern, but it is ambiguous if you want DST or not DST.

It is not ambiguous if you know what "two days" in your particular application should mean (day+2 vs. +48h exactly):

print(tz.localize(now.replace(tzinfo=None) + timedelta(2), is_dst=then_isdst))

-> 2002-10-27 01:00:00-05:00 # +49h

print(tz.normalize(now + timedelta(2))) # +48h

-> 2002-10-27 01:00:00-04:00

Here's a simple mental model that can be used for date arithmetics:

"unknown" means that you can't tell without knowning the specific timezone.

It ignores leap seconds.

The 3rd case behaves as if the calculations are performed using these steps (the actual implementation may be different):

  1. convert an aware datetime object to utc (dt.astimezone(pytz.utc))
  2. do the simple arithmetics using utc time
  3. convert the result to the original pytz timezone (utc_dt.astimezone(tz))

you don't need .localize(), .normalize() calls here.

And you can't pass in a isdst flag to add, so the arithmatic must just pick one, and the sensible one is to keep to the same DST.

That means that: tz = gettimezone('US/Eastern') dt = datetimedatetime(2002, 10, 27, 1, 0, tz=tz, isdst=False) dt2 = dt - 420 + 420 assert dt == dt2 Will fail, which will be unexpected for most people. I think there is no way around this, but I thought I should flag for it. This is a good reason to do all your date time arithmetic in UTC. //Lennart

It won't fail:

from datetime import datetime, timedelta import pytz

tz = pytz.timezone('US/Eastern') dt = tz.localize(datetime(2002, 10, 27, 1), is_dst=False) delta = timedelta(seconds=420)

assert dt == tz.normalize(tz.normalize(dt - delta) + delta)

The only reason tz.normalize() is used so that tzinfo would be correct for the resulting datetime object; it does not affect the comparison otherwise:

assert dt == (dt - delta + delta) #XXX tzinfo may be incorrect assert dt == tz.normalize(dt - delta + delta) # correct tzinfo for the final result



More information about the Python-Dev mailing list