Python datetime object show wrong timezone offset

Question:

I am try creating a datetime object in python using datetime and pytz, the offset shown is wrong.

import datetime
from pytz import timezone

start = datetime.datetime(2011, 6, 20, 0, 0, 0, 0, timezone('Asia/Kolkata'))
print start

The output shown is

datetime.datetime(2011, 6, 20, 0, 0, tzinfo=<DstTzInfo 'Asia/Kolkata' HMT+5:53:00 STD>)

Note that ‘Asia/Kolkata’ is IST which is GMT+5:30 and not HMT+5:53. This is a standard linux timezone, why do I get this wrong, and how do I solve it?

Asked By: compbugs

||

Answers:

See: http://bytes.com/topic/python/answers/676275-pytz-giving-incorrect-offset-timezone

In the comments, someone proposes to use tzinfo.localize() instead of the datetime constructor, which does the trick.

>>> tz = timezone('Asia/Kolkata')
>>> dt = tz.localize(datetime.datetime(2011, 6, 20, 0, 0, 0, 0))
>>> dt
datetime.datetime(2011, 6, 20, 0, 0, tzinfo=<DstTzInfo 'Asia/Kolkata' IST+5:30:00 STD>)

UPDATE: Actually, the official pytz website states that you should always use localize or astimezone instead of passing a timezone object to datetime.datetime.

Answered By: Ferdinand Beyer

This has been fixed in python >=3.9 by the zoneinfo module in the standard library. The solution in >= 3.9 is probably to stop using pytz.

In [1]: import datetime

In [2]: from zoneinfo import ZoneInfo

In [3]: start = datetime.datetime(2011, 6, 20, 0, 0, 0, 0, ZoneInfo('Asia/Kolkata'))

In [4]: print(start)
2011-06-20 00:00:00+05:30

The reason for this extremely confusing behavior is that time zones used to not be standardized at :30 or :00 offsets. Around the turn of the 20th century most of them came into a standard offset. In the example in OP, the timezone switched in 1906. For US/Central, this happened in 1901.

from datetime import datetime, timedelta, date
from pytz import timezone

d = datetime.combine(date.today(), time.min)
for tz in ('Asia/Kolkata', "US/Central"):
    while d > datetime(1800, 1, 1):
        localized = timezone(tz).localize(d)
        if localized.isoformat()[-2:] not in ("00", "30"):
            print(tz)
            print(localized.isoformat())
            print(timezone(tz).localize(d + timedelta(days=1)).isoformat())
            break
        d -= timedelta(days=1)

That outputs:

Asia/Kolkata
1906-01-01T00:00:00+05:21
1906-01-02T00:00:00+05:30
US/Central
1901-12-13T00:00:00-05:51
1901-12-14T00:00:00-06:00

Pytz seems to just use the oldest offset when it doesn’t have date information, even if it was a very long time ago. In some very natural constructions like passing tzinfo to the datetime constructor, the timezone object is not given that data.

Answered By: Lucas Wiman
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.