## #591 TimeZoneInfo does not convert timezones correctly

closed
5
2012-05-05
2012-05-02
No

in the following example two dates are converted from Hawaii time zone to Pacific time zone. In both cases the time difference should be 3 hours.
The first is correct, the second is incorrect

>>> from win32timezone import TimeZoneInfo
>>> from datetime import datetime
>>> tzi = TimeZoneInfo('Hawaiian Standard Time')
>>> tzs = TimeZoneInfo('Pacific Standard Time')
>>> dthaw = datetime(2011, 11, 5, 15, 59, 59, 0, tzinfo=tzi)
>>> print dthaw.timetuple()
time.struct_time(tm_year=2011, tm_mon=11, tm_mday=5, tm_hour=15, tm_min=59, tm_sec=59, tm_wday=5, tm_yday=309, tm_isdst=0)
>>> dtpac = dthaw.astimezone(tzs)
>>> print dtpac.timetuple()
time.struct_time(tm_year=2011, tm_mon=11, tm_mday=5, tm_hour=18, tm_min=59, tm_sec=59, tm_wday=5, tm_yday=309, tm_isdst=1)
>>>
>>> dthaw = datetime(2011, 11, 5, 16, 0, 0, 0, tzinfo=tzi)
>>> print dthaw.timetuple()
time.struct_time(tm_year=2011, tm_mon=11, tm_mday=5, tm_hour=16, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=309, tm_isdst=0)
>>> dtpac = dthaw.astimezone(tzs)
>>> print dtpac.timetuple()
time.struct_time(tm_year=2011, tm_mon=11, tm_mday=5, tm_hour=17, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=309, tm_isdst=1)
>>>

## Discussion

• Brian Matthews - 2012-05-02

This was using the patched code from #3521185

• Jason R. Coombs - 2012-05-03

I've started looking into this, and I've expanded the tests you provided: http://a.libpa.st/mP7l7

According to the docs, astimezone should be converting from a very strange form (the time in UTC with the pacific time zone indicated), and fromutc should handle that correctly as long as the standard offset isn't changing on an hour-by-hour basis (which it is not for either of these time zones).

I'll continue to investigate, but this is beginning to look like a bug in the datetime implementation (or at least the docs).

• Brian Matthews - 2012-05-04

I think the problem is that TimeZoneInof only works with Naive datetime. That is, if you pass in a datetime in UTC, the utcoffset() returns as if it were localtime.
EG:

from win32timezone import TimeZoneInfo
from datetime import datetime
import calendar
import time
tzi = TimeZoneInfo('Pacific Standard Time')
tzutc = TimeZoneInfo('Coordinated Universal Time')
tz_time = datetime(2011, 11, 6, 4, 0, 0, tzinfo=tzutc)
tzi.utcoffset(tz_time).total_seconds()/60/60

shows -8:00 when it should be -7:00 because the datetime is UTC, not localtime.
We have a way around this rght now by doing a 2-pass calculation tto see if we crossed the pst/dst time.

win32timezone needs a method of showing the utcoffset for a utc timestamp in the specified timezone

• Jason R. Coombs - 2012-05-05
• status: open --> closed

• Jason R. Coombs - 2012-05-05

Brian, thanks for your help and patience. The problem is fixed in 085048997feb.

You want the utc offset for a utc timestamp in a specified timezone?

>>> timestamp = datetime(2011, 11, 6, 4, 0, tzinfo=TimeZoneInfo.utc())
>>> dt_pac = timestamp.astimezone(TimeZoneInfo('Pacific Standard Time'))
>>> dt_pac.utcoffset().total_seconds() / 3600
-7.0

Do you think win32timezone should have a single function that takes a naive timestamp in utc and a time zone name and returns the offset? Something like offset_for_utc_timestamp_in_zone(datetime, zone_name) ?

• Brian Matthews - 2012-05-05

Thanks again for your quick work!

A small discrepancy. I am running my PC in Pacific time, so I have a way to check the results. The script below converts a UTC timestamp into a datetime in the Pacific time zone. The result is different for the second timestamp when doing the same thing using localtime()

from win32timezone import TimeZoneInfo
from datetime import datetime
import calendar
import time

tzi = TimeZoneInfo('Pacific Standard Time')
tzutc = TimeZoneInfo('Coordinated Universal Time')

def fromtimestamp_to_timetuple(timestamp):
tm = time.gmtime(timestamp)
tz1 = datetime(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tzinfo=tzutc)
tz_time = tz1.astimezone(tzi)
return tz_time

print fromtimestamp_to_timetuple(1320573600).timetuple()
print fromtimestamp_to_timetuple(1320570000).timetuple()
print fromtimestamp_to_timetuple(1320566400).timetuple()

print time.strftime('%c', time.localtime(1320573600))
print time.strftime('%c', time.localtime(1320570000))
print time.strftime('%c', time.localtime(1320566400))

It would be better if utcoffset() accepted a datetime that was not Naive.
This example logically should show a utcoffset of -7, but it shows -8 as utcoffset ignores the tz in datetime

from win32timezone import TimeZoneInfo
from datetime import datetime
import calendar
import time
tzi = TimeZoneInfo('Pacific Standard Time')
tzutc = TimeZoneInfo('Coordinated Universal Time')
tz_time = datetime(2011, 11, 6, 4, 0, 0, tzinfo=tzutc)
tzi.utcoffset(tz_time).total_seconds()/60/60

But this is academic for me at this point, plus it might break existing code.
Maybe a new function that is accepts tz-aware datetimes?