【问题标题】:pytz localize vs datetime replacepytz 本地化与日期时间替换
【发布时间】:2010-11-25 16:04:42
【问题描述】:

我在使用 pytz 的 .localize() 函数时遇到了一些奇怪的问题。有时它不会对本地化的日期时间进行调整:

.localize 行为:

>>> tz
<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD> 
>>> d
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421)

>>> tz.localize(d)
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421, 
                  tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>)
>>> tz.normalize(tz.localize(d))
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421,
                  tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>)

如您所见,本地化/规范化操作并未改变时间。 但是,如果使用 .replace:

>>> d.replace(tzinfo=tz)
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421, 
                  tzinfo=<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD>)
>>> tz.normalize(d.replace(tzinfo=tz))
datetime.datetime(2009, 9, 2, 15, 1, 42, 91421,
                  tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>)

这似乎对日期时间进行了调整。

问题是 - 哪个是正确的,为什么其他人是错的?

【问题讨论】:

标签: python datetime timezone utc pytz


【解决方案1】:

localize 只是假设您传递给它的天真日期时间是“正确的”(除了不知道时区!),所以只是设置时区,没有其他调整。

您可以(并且建议...)在 UTC 内部工作(而不是使用简单的日期时间)并在需要以本地化方式执行日期时间的 I/O 时使用 replacenormalize 将处理 DST等等)。

【讨论】:

  • 引用 Alex 关于在 I/O 操作期间使用 UTC 和 localize/delocalize 的建议。我可以建议这是不可取的,但强烈推荐(阅读义务)!
  • OP 询问了localizereplace 之间的区别。 replace 不是也只是设置了时区,不做其他调整吗?如果是这样,为什么两个结果之间存在差异?
  • @MichaelWaterfall: pytz.timezone() 可能对应多个 tzinfo 对象(同一个地方,不同的 UTC 偏移量,时区缩写)。 tz.localize(d) 尝试为给定的 d 本地时间找到正确的 tzinfo(某些本地时间不明确或不存在)。 replace() 只是设置默认情况下 pytz 时区提供的任何(随机)信息,而不考虑给定日期(最近版本中的 LMT)。如果d 是不存在的本地时间,tz.normalize() 可能会调整时间,例如春季(北半球)夏令时转换期间的时间,否则在这种情况下它什么也不做。
  • replace(tzinfo = ...) 因此似乎没用。最好避免它。
  • @paolov replace 并非完全没用,它只是对tzinfo 对象的工作方式做出了某些假设,而pytz 并未遵循这些假设。我认为它适用于例如 Python 3.9 中引入的新 ZoneInfo objects
【解决方案2】:

localize 是用于创建具有初始固定日期时间值的日期时间感知对象的正确函数。生成的日期时间感知对象将具有原始日期时间值。在我看来,这是一种非常常见的使用模式,也许 pytz 可以更好地记录。

replace(tzinfo = ...) 不幸被命名。它是一个行为随机的函数。我建议避免使用此功能来设置时区,除非您喜欢自己造成的痛苦。使用这个功能我已经受够了。

【讨论】:

  • 对此无可争辩。
  • 完全协议。现在处理 replace() 根本不起作用但不会引发错误的问题。就是什么都不做。需要一种更好的方法来将一个天真的日期时间对象强制为 UTC。
  • 我已经通过replace(tzinfo=...)。我感觉到你的痛苦。
【解决方案3】:

此 DstTzInfo 类用于与 UTC 的偏移量在特定时间点发生变化的时区。例如(您可能知道),许多地点在夏季开始时转换为“夏令时”,然后在夏季结束时恢复为“标准时间”。每个 DstTzInfo 实例仅代表其中一个时区,但“localize”和“normalize”方法可帮助您获得正确的实例。

对于阿比让,只有一次过渡(根据 pytz 的说法),那是在 1912 年:

>>> tz = pytz.timezone('Africa/Abidjan')
>>> tz._utc_transition_times
[datetime.datetime(1, 1, 1, 0, 0), datetime.datetime(1912, 1, 1, 0, 16, 8)]

我们从 pytz 得到的 tz 对象代表 1912 年以前的时区:

>>> tz
<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD>

现在查看您的两个示例,看到当您调用 tz.localize(d) 时,您确实将这个 1912 年之前的时区添加到您的天真日期时间对象中。它假定您提供给它的 datetime 对象代表当地时间在该当地时间的正确时区,即 1912 年后的时区。

但是,在您使用 d.replace(tzinfo=tz) 的第二个示例中,它需要您的 datetime 对象来表示 1912 年之前的时区中的时间。这可能不是你的意思。然后,当您调用 dt.normalize 时,它​​会将其转换为适合该日期时间值的时区,即 1912 年后的时区。

【讨论】:

    【解决方案4】:

    我意识到我在这方面有点晚了...... 但这是我发现效果很好的方法。 正如 Alex 所说,在 UTC 工作:

    tz = pytz.timezone('Africa/Abidjan')
    now = datetime.datetime.utcnow()
    

    然后进行本地化:

    tzoffset = tz.utcoffset(now)
    mynow = now+tzoffset
    

    而且这种方法确实可以完美处理夏令时

    【讨论】:

    • 如果.utcoffset() 方法期望本地时区的日期时间与UTC 不同,则不正确。要获取本地时区的当前时间:now = datetime.now(tz)。要将 UTC 时间转换为本地时区:dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(tz)(此处不需要.localize().normalize())。
    • 如果偏移量越过 DST 边界也可能不正确
    • 这是误导。使用aware datetime 表示特定时区的日期时间。要进入某个时区,请向 datetime.now 提供一个 tz。要立即获取 UTC,请提供 tz=UTC。另一方面,天真的日期时间将始终被解释为本地时间(操作系统的 tz 设置)。使用 Python 3.9,您可以使用 zoneinfo 处理时区 - 无需担心本地化与替换。
    猜你喜欢
    • 2011-08-13
    • 2012-11-01
    • 2016-12-18
    • 2011-11-17
    • 2018-10-28
    • 2015-02-16
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多