I would like to contruct DST-valid timestamps only using the standard library in Python 3.9 and was hoping this was possible with this version.
In my timezone "Europe/Berlin", the DST crossings for 2020 are:
2020-03-29 at 02:00 the clock switches to 03:00 (there is no hour 2!)
2020-10-25 at 03:00 the clock switches back to 02:00 (the hour 2 exists two times!)
My script yields the following output:
MARCH
2020-03-29 01:59:00+01:00 CET plus 1 h: 2020-03-29 02:59:00+01:00 CET
(should be 03:59:00 CEST since there is no hour 2!)
OCTOBER
2020-10-25 02:00:00+02:00 CEST plus 1 h: 2020-10-25 03:00:00+01:00 CET
(seems OK- EDIT: should be 02:00 CET!!!)
Example code is provided below. Windows user may need to "pip install tzdata" to make it work.
Any advise would be greatly appreciated!
'''
Should work out of the box with Python 3.9
Got a fallback import statement.
BACKPORT (3.6+)
pip install backports.zoneinfo
WINDOWS (TM) needs:
pip install tzdata
'''
from datetime import datetime, timedelta
from time import tzname
try:
from zoneinfo import ZoneInfo
except ImportError:
from backports import zoneinfo
ZoneInfo = zoneinfo.ZoneInfo
tz = ZoneInfo("Europe/Berlin")
hour = timedelta(hours=1)
print("MARCH")
dt_01 = datetime(2020, 3, 29, 1, 59, tzinfo=tz)
dt_02 = dt_01 + hour
print(f"{dt_01} {dt_01.tzname()} plus 1 h: {dt_02} {dt_02.tzname()}")
print("\nOCTOBER")
dt_01 = datetime(2020, 10, 25, 2, 0, tzinfo=tz)
dt_02 = dt_01 + hour
print(f"{dt_01} {dt_01.tzname()} plus 1 h: {dt_02} {dt_02.tzname()}")
Although it is counter-intuitive, this is as expected. See this blog post for more details on how datetime arithmetic works. The reason for this is that adding a
timedeltato adatetimeshould be thought of as "advance the calendar/clock by X amount" rather than "what will the calendar/clock say after this amount of time has elapsed". Note that the first question might result in a time that doesn't even occur in the local time zone!If you want, "What
datetimerepresents what time it will be after the amount of time represented by thistimedeltahas elapsed?" (which it seems you do), you should do something equivalent to converting to UTC and back, like so:I believe you can create a
timedeltasubclass that overrides__add__to do this for you (I'd kind of like to introduce something like this to the standard library if I can).Note that if
dt.tzinfoisNone, this will use your system local time zone to determine how to do absolute addition, and it will return an aware time zone. Running this inAmerica/New_York:If you want this to do civil addition for naïve datetimes and absolute addition for aware datetimes, you can check whether or not it's naïve in the function:
Also, to be clear, this is not something to do with
zoneinfo. This has always been the semantics of datetimes in Python, and it wasn't something we could change in a backwards-compatible way.pytzdoes work a little differently, because addingpytz-aware datetimes does the wrong thing, and requires anormalizestep after the arithmetic has occurred, and thepytzauthor decided thatnormalizeshould use absolute-time semantics.absolute_addalso works withpytzanddateutil, since it uses operations that work well for all time zone libraries.