Python中使用datetime.astimezone的行为不一致

时间:2013-04-09 15:57:01

标签: python datetime pytz

我的用例只是将日期作为UTC存储在数据库中,并在安排和查看预定时间时在US / Central时间内实现用户调度的预期结果。下面的不一致行为让我感到头疼,并且在执行某些更新时,预定日期已经过了一个小时。

导致以下不一致行为的原因是什么?我是否可以指望我在最后两行中实现理智时所遵循的行为?有没有更好的方法,即。我没有正确使用日期时间吗?坦率地说,我有点困惑,所以感谢任何帮助!

# Instantiate a datetime in December and April and make them timezone aware
decutc = datetime.datetime(2013, 12, 12, 12, 12, 12).replace(tzinfo=pytz.UTC)
aprutc = datetime.datetime(2013, 4, 12, 12, 12, 12,).replace(tzinfo=pytz.UTC)

# Convert both to US/Central, April is STD and December is DST as expected
# NOTE is STD
decutc.astimezone(pytz.timezone('US/Central'))
Out[164]: datetime.datetime(2013, 12, 12, 6, 12, 12, tzinfo=<DstTzInfo 'US/Central' CST-1 day, 18:00:00 STD>)
# NOTE is DST
aprutc.astimezone(pytz.timezone('US/Central'))
Out[165]: datetime.datetime(2013, 4, 12, 7, 12, 12, tzinfo=<DstTzInfo 'US/Central' CDT-1 day, 19:00:00 DST>)
# Move an aware datetime to another month with a different daylight savings time
# NOTE This one DOES NOT change from STD to DST
decutc.astimezone(pytz.timezone('US/Central')).replace(month=4).astimezone(
    pytz.timezone('US/Central'))
Out[166]: datetime.datetime(2013, 4, 12, 6, 12, 12, tzinfo=<DstTzInfo 'US/Central' CST-1 day, 18:00:00 STD>)
# NOTE This one DOES change from DST to STD
aprutc.astimezone(pytz.timezone('US/Central')).replace(month=12).astimezone(
    pytz.timezone('US/Central'))
Out[167]: datetime.datetime(2013, 12, 12, 6, 12, 12, tzinfo=<DstTzInfo 'US/Central' CST-1 day, 18:00:00 STD>)

为了实现一致的行为,我最终做了以下事情:

# NOTE correctly goes from STD to DST
decutc.astimezone(pytz.timezone('US/Central')).replace(month=4).astimezone(
    pytz.timezone('US/Central')).astimezone(pytz.UTC).astimezone(pytz.timezone('US/Central'))
Out[172]: datetime.datetime(2013, 4, 12, 7, 12, 12, tzinfo=<DstTzInfo 'US/Central' CDT-1 day, 19:00:00 DST>)

# NOTE correctly goes from DST to STD
aprutc.astimezone(pytz.timezone('US/Central')).replace(month=12).astimezone(
    pytz.timezone('US/Central')).astimezone(pytz.UTC).astimezone(pytz.timezone('US/Central'))
Out[170]: datetime.datetime(2013, 12, 12, 6, 12, 12, tzinfo=<DstTzInfo 'US/Central' CST-1 day, 18:00:00 STD>)

2 个答案:

答案 0 :(得分:2)

通常我可以指望Python和Python库开箱即用:)但是pytz选择提供“规范化”功能,我们作为开发人员在做时间时需要注意的区域转换。问题是不一致的问题,它迫使我们(开发人员)自己决定如何处理所有的疯狂,因为图书馆作者无法为我们做出决定。

http://pytz.sourceforge.net/#localized-times-and-date-arithmetic似乎是必须阅读

  

请注意,此库与用于tzinfo实现的文档化Python API不同;如果你想创建本地挂钟时间   您需要使用本文档中记录的localize()方法。在   此外,如果您在跨越夏令时的当地时间执行日期算术   边界,结果可能是在一个不正确的时区(即减去1)   从美国东部时间2002-10-27 1:00分钟到美国东部时间2002-10-27 0:59   而不是正确的2002-10-27 1:59 EDT)。 normalize()方法是   提供纠正此事。不幸的是,这些问题不可能   解决后不修改Python日期时间实现。

这非常简单,因为有多种方法可以完成有效的工作。即。对于UTC normalizelocalize是没有必要的,我们可以观察一些工作,直到我们在与其他时区合作时实际跨越DST - STD边界,但在这种情况下,我不确定替代方案已经更好了(打破使用标准日期时间API的代码)。我当然更喜欢堆栈跟踪而不是无声的astimezone转换。

我真的把错误放在datetime文档上:

http://docs.python.org/2/library/datetime.html#datetime.datetime.astimezone

其中提到了pytz,这是获取一些tzinfo对象的地方,但没有提到你最好阅读他们的文档以获取警告。

答案 1 :(得分:0)

我能够重现您的结果并获得不一致的行为。为了使其保持一致,我所要做的就是删除您在转换调用中添加的.astimezone(pytz.timezone('US/Central'))个调用。在这样做之后,结果是一致的,即从STD变为DST,反之亦然。

换句话说,改变:

decutc.astimezone(pytz.timezone('US/Central')).replace(month=4).astimezone(
    pytz.timezone('US/Central'))
aprutc.astimezone(pytz.timezone('US/Central')).replace(month=12).astimezone(
    pytz.timezone('US/Central'))

致电:

decutc.astimezone(pytz.timezone('US/Central')).replace(month=4)
aprutc.astimezone(pytz.timezone('US/Central')).replace(month=12)

<强>更新
为了获得适当且一致的STD&lt; - &gt;在更改日期时进行DST转换,只需在<{em> .astimezone(pytz.timezone('US/Central')来电之后调用.replace()电话,如下所示:

decutc.replace(month=4).astimezone(pytz.timezone('US/Central'))
aprutc.replace(month=12).astimezone(pytz.timezone('US/Central'))

如果您考虑它,这是有道理的,因为发生的事情是首先更改UTC日期时间(doesn't observe daylight saving time)的月份,然后将该中间结果转换为时区。 / p>

总而言之,我建议您将所有日期时间存储在UTC中,在该时区内进行所有操作,并在绝对必要时仅转换为特定的当地时间。