18

I have an issue comparing outputs with dateutil and pytz. I'm creating a aware datetime object (UTC) and then converting to a given time zone, but I get different answers. I suspect that dateutil sometimes gives wrong results because it has problems taking into account daylight saving time (at least, I read a comment about it) but I can't find confirmation or a fix to that issue. This is the code:

import dateutil

u = dateutil.tz.tzutc()
date1 = datetime.datetime(2010, 5, 2, 11, 10, tzinfo=u)
# 2010-05-02 11:10:00+00:00

u2 = dateutil.tz.gettz('America/Chicago')
date2 = datetime.datetime(2010, 5, 2, 11, 10, tzinfo=u2)
# 2010-05-02 11:10:00-05:00


import pytz
u = pytz.timezone('UTC')
date1 = datetime.datetime(2010, 5, 2, 11, 10, tzinfo=u)

# 2010-05-02 11:10:00+00:00
u2 = pytz.timezone('America/Chicago')
date2 = datetime.datetime(2010, 5, 2, 11, 10, tzinfo=u2)

# 2010-05-02 11:10:00-06:00

So, what could be the problem here?

UPDATE:

I just tried this:

print u2.normalize(date1.astimezone(u2))
# 2010-05-02 06:10:00-05:00

So pytz needs normalize to consider DST?

UPDATE 2:

It seemed as if pytz and dateutil don't give the answer for America/Argentina/San_Luis but this works:

import pytz, dateutil, datetime

now = datetime.datetime.now() 

for zone in pytz.all_timezones:
    utc_dateutil = dateutil.tz.tzutc()
    utcdate_dateutil = datetime.datetime(now.year, now.month, now.day, now.hour, now.minute, tzinfo=utc_dateutil)
    zone_dateutil = dateutil.tz.gettz(zone)
    newzone_dateutil = utcdate_dateutil.astimezone(zone_dateutil)
    
    utc_pytz = pytz.timezone('UTC')
    utcdate_pytz = datetime.datetime(now.year, now.month, now.day, now.hour, now.minute, tzinfo=utc_pytz)
    zone_pytz = pytz.timezone(zone)
    newzone_pytz = utcdate_pytz.astimezone(zone_pytz)
    assert newzone_dateutil == newzone_pytz

Am I missing something?

vvvvv
  • 25,404
  • 19
  • 49
  • 81
r_31415
  • 8,752
  • 17
  • 74
  • 121
  • 11:10 turning into 12:57 is mighty weird. Something corrupted with your `America/Chicago` time zone? Try it on another machine? I can't reproduce this behaviour. – Celada Dec 19 '12 at 02:32
  • Sorry. Typo. Initially I used another datetime object as example. Fixed now. Notice that in one timezone it says 05:00 and in the other it says 06:00. That's the problem. – r_31415 Dec 19 '12 at 02:34
  • Oh, well then in that case I don't know why. +1 good question :-) -05:00 definitely seems like the correct one (standard time zone -06:00 with DST added) – Celada Dec 19 '12 at 02:41
  • Thanks for verifying which one is correct. Just now, I did this: u2.normalize(date1.astimezone(u2)) and got again -05:00. Maybe pytz needs normalize to take into account DST. I will update my question with that info. – r_31415 Dec 19 '12 at 02:43
  • `pytz` is quite stupid when you apply it without using `localize`. It just uses the first thing in the database for that zone, which in some cases is complete nonsense - see http://stackoverflow.com/questions/11442183/pytz-timezone-shows-weird-results-for-asia-calcutta. It certainly *doesn't* account for DST. – Mark Ransom Dec 19 '12 at 04:41

1 Answers1

16

Edit: The discrepancy discussed below no longer exists when using

>>> dateutil.__version__
'1.5'

>>> pytz.__version__
'2012c'

The pytz module warns,

this library differs from the documented Python API for tzinfo implementations; if you want to create local wallclock times you need to use the localize() method

and further on

This library only supports two ways of building a localized time. The first is to use the localize() method provided by the pytz library.

In [61]: u4 = pytz.timezone('America/Chicago')
In [62]: print(u4.localize(datetime.datetime(2010, 5, 2, 11, 10)))
2010-05-02 11:10:00-05:00

The other way is to use the astimezone method, which is used to convert a timezone-aware datetime into another timezone-aware datetime.

And to be completely explicit, it warns against constructing a timezone-aware datetime using the tzinfo argument:

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


Let's test the hypothesis that

datetime.datetime(year, month, day, hour, minute, tzinfo = dateutil_tz)

equals

pytz_tz.localize(datetime.datetime(year, month, day, hour, minute))

with this code:

import dateutil.tz
import datetime
import pytz

now  = datetime.datetime.now()

for name in pytz.all_timezones:
    dateutil_tz = dateutil.tz.gettz(name)
    pytz_tz = pytz.timezone(name)
    dateutil_date = datetime.datetime(
        now.year, now.month, now.day, now.hour, now.minute, tzinfo = dateutil_tz)
    pytz_date = pytz_tz.localize(datetime.datetime(
        now.year, now.month, now.day, now.hour, now.minute))

    try:
        assert dateutil_date.isoformat() == pytz_date.isoformat()
    except AssertionError:
        print(name)
        print(dateutil_date.isoformat())
        print(pytz_date.isoformat())           

The code yields:

America/Argentina/San_Luis
2012-12-18T22:32:00-04:00 <-- dateutil datetime
2012-12-18T22:32:00-03:00 <-- pytz's datetime

So my hypothesis was wrong: dateutil and pytz return different results.

So which one is correct? I'm not really sure, but according to this website, currently,

America/Argentina/San_Luis time zone offset is: 
UTC / GMT -03:00 hours

so it appears pytz is correct.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • Excellent. Are there similar warnings in dateutil? I have seen that many people prefer pytz so I thought it provided better support. – r_31415 Dec 19 '12 at 03:02
  • This is confusing. I can't find that time zone in this list http://en.wikipedia.org/wiki/Time_in_the_United_States#List_of_time_zones_from_the_tz_database and after trying many of them, each of them gave the same result in dateutil and pytz. America/Argentina/San_Luis is recognized and indeed, it shows discrepancies. – r_31415 Dec 19 '12 at 03:47
  • Yes, I am confused too. I think both dateutil and pytz read the same database (`/usr/share/zoneinfo` on Ubuntu), so why there should be only one timezone with a discrepancy is rather odd. – unutbu Dec 19 '12 at 03:55
  • There is a bit of information on America/Argentina/San_Luis [here](http://en.wikipedia.org/wiki/America/Argentina/San_Luis), which also indicates the offset is `-03:00`. – unutbu Dec 19 '12 at 04:21
  • Yes, it looks like it's listed in the Olson database. Hmm, it gets weirder and weirder. – r_31415 Dec 19 '12 at 07:59
  • I think this could be due to some issue in some representation of dateutil or pytz because I tested all zones and all worked, including America/Argentina/San_Luis. Look at my update. – r_31415 Dec 19 '12 at 15:45
  • @RobertSmith: I think your code shows that working in UTC and using `astimezone` works as expected. That reminds me of the advice given on the [pytz doc page](http://pytz.sourceforge.net/#problems-with-localtime). "The best and simplest solution is to stick with using UTC." That link by the way, is a great read. It shows that creating an unambiguous localtime for all cases using `datetime`'s `tzinfo` argument is hopeless. So option 1 is to use UTC and astimezone, option 2 is to use pytz's localize method with is_dst set to avoid ambiguity. – unutbu Dec 19 '12 at 16:57
  • note: `dateutil` may return wrong results for any timezone that had different utc offset in the past e.g., Europe/Moscow in 2010-2015 period. – jfs Jul 23 '15 at 21:42