The crux of this post is, make sure to never use
datetime.replace(tzinfo=...) when working with PyTZ, use
tz.localize(...) instead, otherwise you’ll end up with some very strange times.
The PyTZ docs do mention this helper method as a way to fix incorrect conversion across timezones, but out of the box PyTZ timezones seem odd. Consider this simple code that takes both the native
datetime.replace approach, and the
import pytz import datetime lunchtime = datetime.datetime(2014, 11, 12, 12, 30) print 'lunchtime =', lunchtime local_tz = pytz.timezone('Australia/Brisbane') print 'local_tz =', repr(local_tz) lunchtime_local = lunchtime.replace(tzinfo=local_tz) print 'lunchtime_local =', lunchtime_local print 'lunchtime_local to UTC =', lunchtime_local.astimezone(pytz.utc) lunchtime_localize = local_tz.localize(lunchtime) print 'lunchtime_localize =', lunchtime_localize print 'lunchtime_localize to UTC =', lunchtime_localize.astimezone(pytz.utc)
The output is:
lunchtime = 2014-11-12 12:30:00 local_tz = <DstTzInfo 'Australia/Brisbane' LMT+10:12:00 STD> lunchtime_local = 2014-11-12 12:30:00+10:12 lunchtime_local to UTC = 2014-11-12 02:18:00+00:00 lunchtime_localize = 2014-11-12 12:30:00+10:00 lunchtime_localize to UTC = 2014-11-12 02:30:00+00:00
Off the bat, notice that the representation of the PyTZ timezone is strange: “LMT+10:12:00”. For a start, what is LMT? And why is there an extra 12 minutes in the offset? The correct format would be something like: AEST+10:00:00. i.e., the timezone abbreviation is AEST, and the offset is 10 hours.
When you apply this in a naive way you end up with the incorrect offset, and when you take this across to UTC the answer is wrong. But, when you apply the timezone using
localize method the correct offset.
Certainly something to be aware of.