My first guess was maybe this was an issue with specifying “CET” instead of “Europe/Paris” (or equivalent). But after playing around with it, that doesn’t seem to be the case, and datetime.datetime(2023, 10, 29, 2, 45, tzinfo=zoneinfo.ZoneInfo('CET')).tzname()
evaluates to CEST
anyway.
The relevant piece of information looks like the fold attribute, but math with timedelta ignores the fold. The only way I can figure out to do this correctly is convert to UTC, do your math, and then convert back.
from datetime import datetime, timedelta, UTC
from zoneinfo import ZoneInfo
CET = ZoneInfo('CET')
x = datetime(2023, 10, 29, 2, 45, tzinfo=CET)
y = (x.astimezone(UTC) + timedelta(minutes=15)).astimezone(CET)
x.fold # 0
y.fold # 1
x.isoformat() # '2023-10-29T02:45:00+02:00'
y.isoformat() # '2023-10-29T02:00:00+01:00'
PEP 495, which introduced the fold parameter, has this to say:
Users of pre-PEP implementations of tzinfo
will not see any changes in the behavior of their aware datetime instances. Two such instances that differ only by the value of the fold
attribute will not be distinguishable by any means other than an explicit access to the fold
value. (This is because these pre-PEP implementations are not using the fold
attribute.)
On the other hand, if an object’s tzinfo
is set to a fold-aware implementation, then in a fold or gap the value of fold
will affect the result of several methods: utcoffset()
, dst()
, tzname()
, astimezone()
, strftime()
(if the “%Z” or “%z” directive is used in the format specification), isoformat()
, and timetuple()
.
and also:
No new implementations of datetime.tzinfo
abstract class are proposed in this PEP. The existing (fixed offset) timezones do not introduce ambiguous local times and their utcoffset()
implementation will return the same constant value as they do now regardless of the value of fold
.
So it seems like this is basically as intended, and a third-party implementation would be needed to support fold-aware math. I checked, and dateutil seems to have the same behavior for math using their relativedelta
class.