【问题标题】:Inconsistent Behavior with datetime.astimezone in PythonPython 中的 datetime.astimezone 行为不一致
【发布时间】:2013-04-01 04:01:54
【问题描述】:

我的用例只是将日期作为 UTC 存储在数据库中,并在计划和查看计划时间时为用户在美国/中部时间计划中实现预期结果。以下不一致的行为让我有些头疼,并且在执行某些更新时预定的日期错了一个小时。

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

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

【问题讨论】:

    标签: python datetime pytz


    【解决方案1】:

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

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

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

    这是非常不合常规的,因为有不止一种方法可以做有效的事情。 IE。对于 UTC 而言,normalizelocalize 不是必需的,我们可以观察到工作正常,直到我们真正跨越 DST - 与其他时区一起工作时的 STD 边界,但在这种情况下,我不确定替代方案会更好(破坏使用标准日期时间 API 的代码)。我当然更喜欢堆栈跟踪,而不是默默地糟糕的 astimezone 转换。

    我真的把错误归咎于datetime 文档:

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

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

    【讨论】:

    • 我认为期望 Python 文档告诉您 RTFM 他们碰巧提到的第三方模块是不现实的。除此之外,astimezone() 部分中根本没有关于 pytz 的内容。
    • 引自 datetime 页面:“标准库没有 tzinfo 实例,但存在将 IANA 时区数据库(也称为 Olson 数据库)引入 Python 的第三方库:pytz。 pytz 包含最新信息,建议使用它。” - 他们还可以提到它实际上并没有使用上面页面中解释的 datetime API...
    【解决方案2】:

    我能够重现您的结果并得到不一致的行为。为了使其保持一致,我所要做的就是删除您添加到转换调用的.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 DST 转换,只需将 .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)的月份,然后将中间结果转换为时区。

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

    【讨论】:

    • 是的,替换是一致的,因为没有进行转换,但是当采用由于夏令时而应该是不同时区的美国/中部时间并将其保存为 UTC 时会出现问题.如果输入的时间是错误的时区,那么它会在显示回用户时关闭一个小时,因为它被存储了一个小时。
    猜你喜欢
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-20
    • 2015-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多