1

While trying the new zoneinfo support in python3.9.1, I noticed that time differences of datetime aware objects differ from those produced by pytz as shown by the output of the below program:

import datetime,zoneinfo,pytz
from sys import version_info
print(f'Python{version_info.major}.{version_info.minor}{version_info.micro}'
f' pytz{pytz.__version__}')
Athens=zoneinfo.ZoneInfo('Europe/Athens')
f='%Y-%m-%d %H:%M:%S'
d=[datetime.datetime.strptime('2020-10-25 00:00:00',f),
   datetime.datetime.strptime('2020-10-25 23:59:59',f)]
print('naive   ',d[1]-d[0])
d=[x.astimezone(Athens) for x in d]
print('zoneinfo',d[1]-d[0])
d=[datetime.datetime.strptime('2020-10-25 00:00:00',f),
   datetime.datetime.strptime('2020-10-25 23:59:59',f)]
athens=pytz.timezone('Europe/Athens')
print('pytz as ',d[1].astimezone(athens)-d[0].astimezone(athens))
print('pytz loc',athens.localize(d[1])-athens.localize(d[0]))

Python3.91 pytz2020.4
naive    23:59:59
zoneinfo 23:59:59
pytz as  1 day, 0:59:59
pytz loc 1 day, 0:59:59

It appears that the native timezone supports ignores the fact that 2020-10-25 was the day of changeover from summertime to winter time and therefore that day's duration was 25 hours. What am I missing?

NameOfTheRose
  • 819
  • 1
  • 8
  • 18
  • after closer inspection, I think [this](https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html) is what you're missing. The timedelta you get from aware datetime with a zoneinfo tzinfo is "DST aware"; it shows wall time. That's the same for dateutil btw. With pytz's timezones, this doesn't seem to be the case. It's a pain ^^ – FObersteiner Dec 03 '20 at 13:28
  • related: [Python 3.9: Construct DST valid timestamp using standard library](https://stackoverflow.com/q/64440016/10197418) – FObersteiner Dec 03 '20 at 13:33
  • @MrFuppes, thank you. I need to think about it. – NameOfTheRose Dec 03 '20 at 14:31
  • I hope this helps. Also note [Paul's answer](https://stackoverflow.com/a/64452687/10197418) to the linked question and the discussion I had with him in the comments. I still find this whole "wall time timedelta" thing hard to grasp. I like to think of time in a physical sense but maybe that's too naive in the real world ^^ – FObersteiner Dec 03 '20 at 14:45
  • 1
    @MrFuppes, his case is well presented but not convincing. Counter-intuitive is sometimes just an euphemism for wrong. Documenting a bug does not make it a feature. – NameOfTheRose Dec 04 '20 at 07:07
  • Not sure how well this is even documented, at least to me, it's not obvious in the [docs](https://docs.python.org/3/library/datetime.html#timedelta-objects). Furthermore, Python packages do it differently than the standard lib as you have noted. Another example, `pandas` treats naive datetime as UTC whilst Python's datetime assumes local time. Can be confusing... I guess some things could have been done differently from the start. – FObersteiner Dec 04 '20 at 08:22

1 Answers1

1

An illustration of my comment; aware datetime with tzinfo set with a ZoneInfo from zoneinfo returns a wall time timedelta. If you do the same with pytz.timezone aware datetime, you get absolute time timedelta.

from datetime import datetime
from zoneinfo import ZoneInfo
import pytz
from sys import version_info

print(f'Python {version_info.major}.{version_info.minor}{version_info.micro} pytz {pytz.__version__}')
# Python 3.90 pytz 2020.4

d=[datetime.fromisoformat('2020-10-25 00:00:00'), datetime.fromisoformat('2020-10-25 23:59:59')]

Athens = ZoneInfo('Europe/Athens')
print('wall time diff, zoneinfo:', d[1].replace(tzinfo=Athens)-d[0].replace(tzinfo=Athens))
# wall time diff, zoneinfo: 23:59:59

athens = pytz.timezone('Europe/Athens')
print('absolute time diff, pytz:', athens.localize(d[1])-athens.localize(d[0]))
# absolute time diff, pytz: 1 day, 0:59:59

# to get absolute time delta with zoneinfo:
utc = ZoneInfo('UTC')
print('absolute time diff, zoneinfo:', d[1].replace(tzinfo=Athens).astimezone(utc)
                                      -d[0].replace(tzinfo=Athens).astimezone(utc))
# absolute time diff, zoneinfo: 1 day, 0:59:59
FObersteiner
  • 22,500
  • 8
  • 42
  • 72