2

I ran across an interesting situation today. Can anyone explain why the offsets for ts1 and ts2 are different? ts1 is a datetime object that is timezone-aware right off the bat. ts2 is a datetime object that starts off timezone-naive and has its tzinfo replaced. However, they end up with different offsets.

>>> from pytz import timezone
>>> EST = timezone('America/New_York')
>>> ts1 = datetime.datetime.now(tz=EST)
>>> ts2 = datetime.datetime.now()
>>> ts2 = ts2.replace(tzinfo=EST)
>>> print ts1
2014-05-16 11:25:16.749748-04:00
>>> print ts2
2014-05-16 11:25:19.581710-05:00
Duke Silver
  • 1,307
  • 1
  • 13
  • 20

2 Answers2

10

When you call ts2.replace(tzinfo=EST), the tzinfo object you're getting doesn't match the one you get with ts1:

>>> ts1
datetime.datetime(2014, 5, 16, 11, 51, 7, 916090, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)
>>> ts2
datetime.datetime(2014, 5, 16, 11, 51, 30, 922692, tzinfo=<DstTzInfo 'America/New_York' LMT-1 day, 19:04:00 STD>)

You end up with LMT instead of EDT.

The pytz documentation actually notes that using pytz with the tzinfo argument of standard datetime objects simply doesn't work for many timezones:

Unfortunately using the tzinfo argument of the standard datetime constructors ''does not work'' with pytz for many timezones.

>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt) '2002-10-27 12:00:00 LMT+0020'

It is safe for timezones without daylight saving transitions though, such as UTC:

>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=pytz.utc).strftime(fmt) '2002-10-27 12:00:00 UTC+0000'

I'm not exactly sure why the first one works; perhaps because it doesn't actually have to convert anything when the object is initially constructed with the tzinfo object.

Edit:

Ah, the Python documentation notes that using datetime.datetime.now() with the tz arg is equivalent to:

EST.fromutc(datetime.utcnow().replace(tzinfo=EST))

Which means you're converting from UTC, which is safe with pytz. So that's why the first one works.

dano
  • 78,755
  • 12
  • 192
  • 204
  • `pytz` provides the custom implementation for `.fromutc()` method that uses `._utc_transition_times` list to get the correct tzinfo object. – jfs Sep 13 '14 at 18:12
5

According to the documentation, the correct way to apply a time zone to a naive datetime is with the localize method.

ts1 = eastern.localize(datetime.datetime.now())

Also, I recommend you use avoid EST as a variable name, since it typically standard for "Eastern Standard Time", and America/New_York comprises both "Eastern Standard Time" (EST) and "Eastern Daylight Time" (EDT).

Matt Johnson-Pint
  • 197,368
  • 66
  • 382
  • 508
  • 2
    it may return an incorrect result during DST transitions, use `datetime.now(eastern)` instead. – jfs Sep 13 '14 at 18:01