59
>>> import pytz
>>> pytz.timezone('Asia/Hong_Kong')
<DstTzInfo 'Asia/Hong_Kong' LMT+7:37:00 STD>

A seven hour and 37 minute offset? This is a little strange, does anyone experience the same issue?

In fact I'm getting different behavior between

import pytz
from datetime import datetime
hk = pytz.timezone('Asia/Hong_Kong')

dt1 = datetime(2012,1,1,tzinfo=hk)
dt2 = hk.localize(datetime(2012,1,1))
if dt1 > dt2:
   print "Why?"
FObersteiner
  • 22,500
  • 8
  • 42
  • 72
Arthur B.
  • 3,445
  • 3
  • 21
  • 24
  • what's the problem with being precise? [`The Hong Kong Time was first set to Local Mean Time (GMT+7:36:41) on 1 January 1885 at 13:00 by the then Royal Observatory Hong Kong`](http://en.wikipedia.org/wiki/Hong_Kong_Time#Time_standards) – KurzedMetal Jul 13 '12 at 15:49
  • See http://stackoverflow.com/questions/11442183/pytz-timezone-shows-weird-results-for-asia-calcutta/11442571#11442571 – Mark Ransom Jul 13 '12 at 15:55
  • 1
    Possible duplicate of [Python datetime object show wrong timezone offset](https://stackoverflow.com/questions/6410971/python-datetime-object-show-wrong-timezone-offset) – congusbongus Sep 10 '19 at 23:33

3 Answers3

78

Time zones and offsets change over the years. The default zone name and offset delivered when pytz creates a timezone object are the earliest ones available for that zone, and sometimes they can seem kind of strange. When you use localize to attach the zone to a date, the proper zone name and offset are substituted. Simply using the datetime constructor to attach the zone to the date doesn't allow it to adjust properly.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
17

While I'm sure historic changes in timezones are a factor, passing pytz timezone object to the DateTime constructor results in odd behavior even for timezones that have experienced no changes since their inception.

import datetime
import pytz 

dt = datetime.datetime(2020, 7, 15, 0, 0, tzinfo= pytz.timezone('US/Eastern'))

produces

2020-07-15 00:00:00-04:56

Creating the datetime object then localizing it produced expected results

import datetime
import pytz 

dt = datetime.datetime(2020, 7, 15, 0, 0)
dt_local = timezone('US/Eastern').localize(dt)

produces

2020-07-15 00:00:00-04:00
rtphokie
  • 609
  • 1
  • 6
  • 14
  • Prior to November 1883, there weren't any standard time zones in the US; time was local: https://en.wikipedia.org/wiki/Time_in_the_United_States#History. I expect that the timezone database is returning New York time. – Mark Ransom Jul 16 '20 at 21:46
  • 1
    If you look at example data from the [Olson Database](https://en.wikipedia.org/wiki/Tz_database#Example_zone_and_rule_lines) you can see that New York had an offset of `4:56:02` prior to `1883 November 18, 12:03:58`. So it's incorrect that the New York time zone never changed. – Mark Ransom Jul 16 '20 at 21:53
  • I stand corrected. That would explain this behavior for datetimes in 1883, but not 2020 – rtphokie Jul 18 '20 at 15:53
  • See my answer. The tzinfo object doesn't know what date you're going to use with it unless you use `localize`. For some reason pytz assumes 1883 is just as likely as 2020. – Mark Ransom Jul 18 '20 at 15:59
  • 3
    True but it is still reasonable to expect datetime to use the passed timezone object appropriately with the data parameters passed to its constructor. – rtphokie Jul 19 '20 at 16:06
  • No, because the `tzinfo` definition doesn't include a setup function that can be called from the constructor. It is forced to use it in its default state. – Mark Ransom Jul 19 '20 at 17:36
  • Take utmost with the DateTime constructor. Check this: with `t_now_secs = time.time()` doing `float(datetime.datetime.fromtimestamp(t_now_secs, pytz.UTC).strftime('%s')) - t_now_secs` gives `-3600`. The local offset is dragged into this. – Ytsen de Boer Nov 23 '21 at 13:44
  • @YtsendeBoer yes the local offset is applied in your example, but it doesn't come from a `tzinfo` instance. – Mark Ransom May 04 '22 at 15:11
  • For us mere mortals this pytz behavior is not what one would expect. Wonder how many subtle timezone bugs exists out there due to this. – KlausCPH Feb 03 '23 at 13:42
11

Coming here nearly 10 years later, I think it's worth a note that we can now exclusively utilize the Python 3.9+ standard library to handle time zones, without a "localize trap".

Use the zoneinfo module to set and replace the tzinfo however you like, ex:

from datetime import datetime
from zoneinfo import ZoneInfo

hk = ZoneInfo('Asia/Hong_Kong')
print(repr(hk))
# zoneinfo.ZoneInfo(key='Asia/Hong_Kong')

dt1 = datetime(2012,1,1,tzinfo=hk)
print(dt1)
# 2012-01-01 00:00:00+08:00

Alternatives, if you're not able to use zoneinfo:

Note for pandas users:

  • pandas (v1.4.1) is still using pytz internally, and seems to have some trouble with ZoneInfo timezone objects
FObersteiner
  • 22,500
  • 8
  • 42
  • 72
  • I think the interface between `datetime` and `tzinfo` was extended to allow this to work. When `pytz` was created it was stuck working the way it did. – Mark Ransom May 04 '22 at 15:06
  • 1
    @MarkRansom right, I think I remember having looked into this in the context of another question. So it's not just an additional library and done, it's a bit of an evolution. – FObersteiner May 04 '22 at 15:22