datetime.replace 从根本上被破坏了吗? [复制]

Posted

技术标签:

【中文标题】datetime.replace 从根本上被破坏了吗? [复制]【英文标题】:Is datetime.replace fundamentally broken? [duplicate] 【发布时间】:2017-12-27 18:33:17 【问题描述】:

将时区原始日期时间转换为特定时区会产生完全错误的结果。

import dateutil as du
import pytz    
du.parser.parse('2017-05-31T15:00:00').replace(tzinfo=pytz.timezone('Europe/London')).isoformat()

返回一分钟而不是一小时与 UTC 的偏移量

'2017-05-31T15:00:00-00:01'

我以前见过一些日期时间的特殊性,但这个令人叹为观止。

【问题讨论】:

不太清楚为什么这需要立即投反对票而不发表评论。 什么是 dateutil(第三方库,不在标准库中,如 datetime)? parse之后的结果是什么? 这是一个相当知名的package。 Parse 生成一个标准的日期时间对象。 您可以将parse 的输出添加到问题中吗?看看问题出在哪里会很有帮助。 要么使用符合 tzinfo 的时区接口,例如 dateutil.tz,要么使用 pytz.timezone("Europe/London").localize(my_datetime) 【参考方案1】:

我经常在使用replace()tzinfo 对象时运气不好。然而,我发现这个结构是可靠的:

代码:

def naive_to_aware(ts, tz):
    return tz.localize(ts)

评论更新:

来自 (pytz DOCS)

不幸的是,在许多时区使用标准日期时间构造函数的 tzinfo 参数“不起作用”。

但对于没有夏令时转换的时区是安全的,例如 UTC

所以这不仅仅是运气不好,对于时区具有 DST 的 pytz 对象来说也是有问题的。

测试代码:

import dateutil as du
import pytz

print(naive_to_aware(du.parser.parse('2017-05-31T15:00:00'),
                     pytz.timezone('Europe/London')).isoformat())

结果:

2017-05-31T15:00:00+01:00

【讨论】:

这不是“倒霉”。 pytz 文档指出,接近开头:此库不同于用于 tzinfo 实现的文档化 Python API。它继续说明使用 pytz 时区的一种方法是使用 astimezone 方法,如果它具有夏令时转换,则应特别避免将其用作 tzinfo 参数。它明确表示 UTC 对于tzinfo 是安全的。因此,您似乎基本上已经自己发现了 pytz 文档的建议。 ;) @JohnY,非常感谢您提供的信息。一个不兼容的 API 肯定会解释为什么我只是认为这是运气...更新以反映 为什么 @StephenRauch 您的第一段代码所做的事情与.replace(tzinfo=tz) 完全不同。 replace 只是附加tzinfo 而不修改时间。您的代码假定代码已经采用 UTC。如果你有一个幼稚的 datetime 代表,这将给出错误的答案,例如东部时间,您只想为其附加一个区域。 注意,我投了反对票,因为这是错误的和误导性的。如果/当它被编辑,我很高兴撤回。【参考方案2】:

这里的主要问题是您使用的是pytz 时区。 pytz 区域不遵循tzinfo 接口并且不能简单地附加到datetime 对象(通过构造函数或通过replace)。如果你想使用pytz 时区,你应该使用pytz.timezone.localize 和一个幼稚的datetime。如果datetime 已经可以识别时区,您可以使用datetime.astimezone 在时区之间进行转换。

from dateutil import parser
import pytz

LON = pytz.timezone('Europe/London')
dt = parser.parse('2017-05-31T15:00:00')
dt = LON.localize(dt) 

print(dt)   # 2017-05-31 15:00:00+01:00

这是因为pytz 的接口使用localize 将静态时区附加到datetime。出于同样的原因,如果您对现在本地化的 datetime 对象进行算术运算,它可能会给出类似的不正确结果,您将不得不使用 pytz.timezone.normalize 来修复它。这样做的原因是,从历史上看,不可能使用 Pythonic 的tzinfo 接口来处理模棱两可的日期时间,该接口在 Python 3.6 中随着PEP 495 的变化而改变,使得pytz 的变通方法不太必要。

如果您想使用replace 或构造函数将tzinfo 传递给datetime,或者您更喜欢使用pythonic 接口,dateutil 的时区套件实现了符合 PEP 495 的tzinfo 接口。使用dateutil 区域的等价物是:

from dateutil import parser
from dateutil import tz

LON = tz.gettz('Europe/London')
dt = parser.parse('2017-05-31T15:00:00').replace(tzinfo=LON)

print(dt)   # 2017-05-31 15:00:00+01:00

【讨论】:

以上是关于datetime.replace 从根本上被破坏了吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

Android CardView 圆角在 27 之前在 Android API 上被破坏

通知 DeleteIntent 在更高版本的 Android 上被破坏

核心图像人脸检测在 64 位 iOS 上被破坏?

Interface Builder 在使用 Cocoapods 1.0 的框架项目上被破坏

Delphi Seattle 中运行时 DPI 更改后如何处理菜单缩放

框阴影在 safari 上被父 div 截断