62

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?

4
  • pytz bug report tool Commented Jun 20, 2011 at 12:21
  • @kojiro: It's not a bug. See pytz.sourceforge.net Commented Jun 20, 2011 at 12:39
  • @FerdinandBeyer it wasn't a bug when you wrote your comment, but it is now. The datetime interface can work properly now, as evidenced by the zoneinfo module introduced in Python 3.9 - it doesn't require a localize function to work properly. Commented Nov 18, 2021 at 17:16
  • see also: Weird timezone issue with pytz Commented Oct 12, 2022 at 8:19

2 Answers 2

103

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.

Sign up to request clarification or add additional context in comments.

3 Comments

The explanation in the bytes link is superb, thank you for this!
This answer could be improved by adding a small reasoning why pytz as a default uses the extremely uncommon timezones with minutes offset and doesn't use them when using the method .localize(). This is weird as hell.
@Alfe I don't think anybody knows why pytz uses the defaults it does. My guess is that the default is the oldest entry in its table, rather than the newest which would be more likely to be correct. That may be deliberate to make it obvious when you didn't use localize. The reason localize can work correctly is because it is passed the datetime so it can use it to determine the correct historical context and make adjustments. The zoneinfo module added in Python 3.9 doesn't need localize, I think because datetime was changed to support it better.
13

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.