timezon.make_aware(d, TZ)
is equivalent to TZ.localize(d, is_dst=None)
that raises an error for ambiguous times: 2014-10-26 01:45:00
happens twice in Europe/Moscow timezone:
# Europe/Moscow UTC timestamp
2014-10-26 00:45:00 MSK+0400; 2014-10-25 20:45:00 UTC+0000; 1414269900
2014-10-26 01:00:00 MSK+0400; 2014-10-25 21:00:00 UTC+0000; 1414270800
2014-10-26 01:15:00 MSK+0400; 2014-10-25 21:15:00 UTC+0000; 1414271700
2014-10-26 01:30:00 MSK+0400; 2014-10-25 21:30:00 UTC+0000; 1414272600
2014-10-26 01:45:00 MSK+0400; 2014-10-25 21:45:00 UTC+0000; 1414273500
2014-10-26 01:15:00 MSK+0300; 2014-10-25 22:15:00 UTC+0000; 1414275300
2014-10-26 01:30:00 MSK+0300; 2014-10-25 22:30:00 UTC+0000; 1414276200
2014-10-26 01:45:00 MSK+0300; 2014-10-25 22:45:00 UTC+0000; 1414277100
2014-10-26 02:00:00 MSK+0300; 2014-10-25 23:00:00 UTC+0000; 1414278000
Notice: the utc offset is changed from +0400
to +0300
at 2am (Федеральный закон от 21 июля 2014 г. N 248-ФЗ).
To avoid the exception, you could call TZ.localize(d)
(note: no is_dst=None
) that works fine for existing non-ambiguous times but may fail (return wrong answer) for non-existing or ambiguous times.
If pytz Bug #1378150: Enhance support for end-of-DST-like ambiguous time is fixed then you could use TZ.localize(d, is_dst=True)
, TZ.localize(d, is_dst=False)
to get time before and after the transition correspondingly.
If the bug is not fixed you could use my answer from Parsing of Ordered Timestamps in Local Time (to UTC) While Observing Daylight Saving Time to get the time after the transition:
# `naive` is a naive datetime object in local (Europe/Moscow) time
if tz.localize(naive, is_dst=False) == tz.localize(naive, is_dst=True):
# Example: 2014/10/26 in Europe/Moscow timezone
# ambiguous time but is_dst=False/True yield the same result
# i.e., tz.localize() can't help, find UTC time manually
#NOTE: assume there is no other changes to UTC offset today (local.day)
new_offset = tz.localize(naive + timedelta(1), is_dst=None).utcoffset()
assert tz.localize(naive).utcoffset() != new_offset
utc = (naive - new_offset).replace(tzinfo=pytz.utc)
local = utc.astimezone(tz)