Monday, December 1, 2014

Python datetime and DST timezones

The datetime implementation in Python has an inherent problem. It does not perform well around Daylight Savings Time (DST) changes.

We were already using dateutil package, but it did not help. The problem lies in datetime.datetime and since dateutil is based upon the datetime package, it could not help.

Consider this code:

import datetime
import dateutil.tz
local = dateutil.tz.gettz('Europe/Athens')
d1 = datetime.datetime(2014, 3, 30, 2, 30, tzinfo=local)
d2 = datetime.datetime(2014, 3, 30, 4, 30, tzinfo=local)

02:30 on 2014-03-30 is 30 minutes before the DST change. At 03:00, the time "jumps" to 04:00 and the timezone changes from GMT+2 to GMT+3.

>>> print d1
2014-03-30 02:30:00+02:00
>>> print d2
2014-03-30 04:30:00+03:00

Normally, you would expect that adding 1 hour to d1 would give you 04:30 on the new timezone. Thus, you expect that d1 + datetime.timedelta(hours=1) == d2

Instead:

>>> print d1 + datetime.timedelta(hours=1)
2014-03-30 03:30:00+03:00
>>> print d2-d1
2:00:00

Event worse:

>>> print datetime.datetime(2014, 3, 30, 3, 30, tzinfo=local)
2014-03-30 03:30:00+03:00

This date in the specified timezone does not exist!

So, we needed a way to cope with these transitions. We needed a way to tell if a date is possible or nope. We ended up using the following snippet:

import datetime
import dateutil.tz

_utc = dateutil.tz.tzutc()

def localize_datetime(x, tz=_utc):
    tmp = x.replace(tzinfo=local)
    x = tmp.astimezone(_utc).astimezone(tz)
    if x != tmp:
        raise ValueError('Provided date is invalid in specified timezone!')
    return x

With this function, we check if a timezone-agnostic (or not) date is valid in a specific timezone. Not that we do not check the same issue on October, since we only care about the "gap" created by the DST change in March.

Not the best solution, but one that works for us.