pytz localize vs datetime replace

Art*_*Art 47 python timezone datetime utc pytz

我在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>)
Run Code Online (Sandbox Code Playgroud)

如您所见,由于本地化/规范化操作,时间没有改变.但是,如果使用.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>)
Run Code Online (Sandbox Code Playgroud)

这似乎调整了日期时间.

问题是 - 这是正确的,为什么其他的错?

Ale*_*lli 41

localize 假设您传递的天真日期时间是"正确"(除了不知道时区!)因此只设置时区,没有其他调整.

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

  • @MichaelWaterfall:`pytz.timezone()`可能对应于几个tzinfo对象(相同的地方,不同的UTC偏移,时区缩写).`tz.localize(d)`试图找到给定`d`本地时间的正确tzinfo(某些本地时间不明确或不存在).`replace()`只是设置默认提供的任意(随机)信息pytz时区而不考虑给定日期(最近版本中的LMT).`tz.normalize()`可以调整时间,如果`d`是不存在的本地时间,例如,春天(北半球)DST过渡期间的时间,否则它在这种情况下什么都不做. (8认同)
  • OP询问了`localize`和`replace`之间的区别.不"替换"也只是设置时区,没有其他调整?如果是这样,为什么两个结果之间存在差异? (4认同)
  • 引用 Alex 关于在 I/O 操作期间使用 UTC 和本地化/非本地化的建议。我可以建议不建议但强烈推荐(请阅读义务)! (2认同)
  • 因此 `replace(tzinfo = ...)` 似乎毫无用处。最好避免它。 (2认同)

pao*_*lov 11

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

replace(tzinfo = ...)很遗憾地命名.它是一个随机行为的函数.我会建议避免使用此功能来设置时区,除非你喜欢自己造成的痛苦.我已经厌倦了使用这个功能.

  • 真的不能反驳。 (3认同)
  • 对于那些在“后“pytz”世界(其中“localize()”不再存在)中阅读的人:我过去在“datetime.replace(tz=...)”方面也有过类似的糟糕经历。然而,在使用 PEP 495 兼容时区(例如“ZoneInfo”)的情况下,“datetime.replace(tz=...)”的表现足以让社区接受再次使用它。例如,Django 的 `make_aware()` 只是包装了 `datetime.replace()` (但如果它发现您仍在使用 `pytz`,则仍然使用 `localize()`)。这里也推荐:https://pytz-deprecation-shim.readthedocs.io/en/latest/migration.html (2认同)
  • pytz.localize 实际上是 datetime.replace() 的薄包装 https://github.com/stub42/pytz/blob/master/src/pytz/__init__.py#L423 (2认同)

小智 10

此 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)]
Run Code Online (Sandbox Code Playgroud)

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

>>> tz
<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD>
Run Code Online (Sandbox Code Playgroud)

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

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


der*_*rks 8

我意识到我在这方面有点迟了......但这是我发现工作得很好的东西.亚历克斯说:

tz = pytz.timezone('Africa/Abidjan')
now = datetime.datetime.utcnow()
Run Code Online (Sandbox Code Playgroud)

然后本地化:

tzoffset = tz.utcoffset(now)
mynow = now+tzoffset
Run Code Online (Sandbox Code Playgroud)

而且这种方法可以完美地处理DST

  • 如果 `.utcoffset()` 方法期望本地时区中的日期时间与 utc 不同,则这是不正确的。获取本地时区的当前时间:`now = datetime.now(tz)`。要将 UTC 时间转换为本地时区:`dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(tz)`(这里不需要 `.localize()`、`.normalize()`)。 (2认同)