Is datetime.replace fundamentally broken?
将时区天真日期时间转换为特定时区会产生完全错误的结果。
1 2 3
| import dateutil as du
import pytz
du.parser.parse('2017-05-31T15:00:00').replace(tzinfo=pytz.timezone('Europe/London')).isoformat() |
返回一分钟而不是一小时的偏移量与UTC相比
1
| '2017-05-31T15:00:00-00:01' |
我之前看到过一些日期时间的特点,但这一点令人叹为观止。
-
不太确定为什么要立即投票而不发表评论。
-
什么是dateutil(第三方库,而不是像datetime这样的标准库)? parse之后的结果是什么?
-
它是一个相当知名的包。 Parse生成标准的日期时间对象。
-
你可以在问题中添加parse的输出吗? 查看问题所在将会很有帮助。
-
我不会说datetime.replace从根本上被打破了。 有充分证据表明,pytz并不完全符合Python文档中指定的tzinfo API,并且使用pytz时区对象作为datetime.tzinfo参数是不可靠的。
-
使用符合tzinfo的时区接口(如dateutil.tz)或使用pytz.timezone("Europe/London").localize(my_datetime)。
-
这是一个非常非常普遍的误解。 (有些人认为这些都是重复的问题,而且可能还有更多。)
这里的主要问题是您使用pytz时区。 pytz区域不遵循tzinfo接口,不能简单地附加到datetime对象(通过构造函数或通过replace)。如果您想使用pytz时区,则应使用带有天真datetime的pytz.timezone.localize。如果datetime已经知道时区,则可以使用datetime.astimezone在区域之间进行转换。
1 2 3 4 5 6 7 8
| 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区域的等价物是:
1 2 3 4 5 6 7
| 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 |
我经常使用带有tzinfo对象的replace()运气不好。然而,我发现这个结构是可靠的:
码:
1 2
| def naive_to_aware(ts, tz):
return tz.localize(ts) |
来自评论的更新:
来自(pytz DOCS)
Unfortunately using the tzinfo argument of the standard datetime constructors ‘’does not work’’ with pytz for many timezones.
It is safe for timezones without daylight saving transitions though, such as UTC
所以这不仅仅是运气不好,对于带有DST的时区的pytz对象来说也是有问题的。
测试代码:
1 2 3 4 5
| import dateutil as du
import pytz
print(naive_to_aware(du.parser.parse('2017-05-31T15:00:00'),
pytz.timezone('Europe/London')).isoformat()) |
结果:
1
| 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代表,例如,这将给出错误的答案。东部时间你只想附加一个区域。
-
请注意,我贬低了,因为这是错误的和误导性的。如果/何时编辑,我很乐意收回。