Python's datetime module is one of those bits of code that tends not to do what one would expect them to do.
I have come to adopt some extra usage guidelines in order to preserve my sanity:
- Avoid using
str(datetime_object)
orisoformat
to serialize a datetime: there is no function in the library that can parse all its possible outputs datetime.strptime
silently throws away all timezone information. If you look very closely, it even says so in its documentation- Timezones do not exist, all datetime objects have to be naive. aware means broken.
- datetime objects must always contain UTC information
datetime.now()
is never to be used. Always usedatetime.utcnow()
- Be careful of 3rd party python modules: people have a dangerous tendency to
use
datetime.now()
- If a conversion to some local time is needed, it shall be done via either
some ugly thing like
time.localtime(int(dt.strftime("%s")))
or via the pytz module - pytz must be used directly, and never via timezone aware datetime objects, because datetime objects fail in querying pytz:
That’s right, the datetime object created by a call to datetime.datetime constructor now seems to think that Finland uses the ancient “Helsinki Mean Time” which was obsoleted in the 1920s. The reason for this behaviour is clearly documented on the pytz page: it seems the Python datetime implementation never asks the tzinfo object what the offset to UTC on the given date would be. And without knowing it pytz seems to default to the first historical definition. Now, some of you fellow readers could insist on the problem going away simply by defaulting to the latest time zone definition. However, the problem would still persist: For example, Venezuela switched to GMT-04:30 on 9th December, 2007, causing the datetime objects representing dates either before, or after the change to become invalid.
- Timezone-aware datetime objects have other bugs: for example, they fail to compute Unix timestamps correctly. The following example shows two timezone-aware objects that represent the same instant but produce two different timestamps.
>>> import datetime as dt
>>> import pytz
>>> utc = pytz.timezone("UTC")
>>> italy = pytz.timezone("Europe/Rome")
>>> a = dt.datetime(2008, 7, 6, 5, 4, 3, tzinfo=utc)
>>> b = a.astimezone(italy)
>>> str(a)
'2008-07-06 05:04:03+00:00'
>>> a.strftime("%s")
'1215291843'
>>> str(b)
'2008-07-06 07:04:03+02:00'
>>> b.strftime("%s")
'1215299043'