dateutil和pytz给出了不同的结果。

18

我在使用dateutilpytz比较输出时遇到了问题。我正在创建一个意识到时区的日期时间对象(UTC),然后将其转换为给定的时区,但是我得到了不同的答案。我怀疑dateutil有时会给出错误的结果,因为它在考虑夏令时时存在问题(至少我读到了一条评论),但我找不到确认或解决该问题的方法。这是代码:

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

那么,这里可能出了什么问题呢?

更新:

我刚刚尝试了这个:

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

所以pytz需要使用normalize来考虑DST吗?

更新2:

似乎pytz和dateutil不能为America/Argentina/San_Luis提供答案,但是这个方法可以:

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

我有什么遗漏吗?


11:10变成12:57非常奇怪。你的America / Chicago时区出现了一些问题?在另一台机器上尝试一下?我无法复制这种行为。 - Celada
抱歉,打错了。最初我使用了另一个日期时间对象作为示例。现在已经修复了。请注意,在一个时区中它显示为05:00,在另一个时区中它显示为06:00。这就是问题所在。 - r_31415
哦,那样的话我就不知道了。 +1 好问题 :-) -05:00 确实看起来是正确的(标准时区为-06:00,加上夏令时)。 - Celada
感谢您确认哪个是正确的。刚才,我执行了以下操作:u2.normalize(date1.astimezone(u2)),并再次得到了-05:00。也许pytz需要normalize来考虑DST。我将在我的问题中更新这些信息。 - r_31415
如果你不使用localize,那么pytz在应用时会变得非常愚蠢。它只是使用数据库中该时区的第一个内容,而在某些情况下这完全是无意义的 - 参见https://dev59.com/ZWgu5IYBdhLWcg3wRlL0。它肯定*不会*考虑夏令时。 - Mark Ransom
1个回答

16

编辑:如下所讨论的差异在使用时不再存在

>>> dateutil.__version__
'1.5'

>>> pytz.__version__
'2012c'

pytz 模块警告说,这个库与文档中记录的 Python tzinfo 实现不同;如果您想创建本地时间,则需要使用 localize() 方法。

此外,进一步,此库仅支持两种构建本地化时间的方法。第一种方法是使用 pytz 库提供的 localize() 方法。

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

另一种方法是使用 astimezone 方法,该方法用于将时区感知的日期时间对象转换为另一个时区感知的日期时间对象。

为了严格明确,它警告说不要使用tzinfo参数构造时区感知的日期时间对象:

不幸的是,对于许多时区,使用标准日期时间构造函数的tzinfo参数与pytz一起‘’不起作用‘’。


让我们测试假设:

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

相等

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

使用这段代码:

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())           

代码的输出结果为:

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

因此我的假设是错误的:dateutil和pytz返回不同的结果。

那么哪一个是正确的?我不太确定,但根据这个网站,目前:

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

因此,看起来pytz是正确的。


非常好。dateutil 中是否有类似的警告?我看到很多人更喜欢 pytz,所以我认为它提供了更好的支持。 - r_31415
这很令人困惑。我在这个列表http://en.wikipedia.org/wiki/Time_in_the_United_States#List_of_time_zones_from_the_tz_database中找不到那个时区,在尝试了许多时区后,它们每一个在dateutil和pytz中都给出了相同的结果。America/Argentina/San_Luis被识别出来,确实存在差异。 - r_31415
是的,我也感到困惑。我认为dateutil和pytz都读取同一个数据库(在Ubuntu上为/usr/share/zoneinfo),所以为什么只有一个时区存在差异是相当奇怪的。 - unutbu
这里有一些关于美洲/阿根廷/圣路易斯的信息,链接在此(http://en.wikipedia.org/wiki/America/Argentina/San_Luis),也显示偏移量为“-03:00”。 - unutbu
注意:dateutil 可能会为过去使用不同 UTC 偏移量的任何时区(例如 2010-2015 年间的欧洲/莫斯科时区)返回错误的结果。 - jfs
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接