为什么Python的datetime ISO函数在逻辑上不正确和有bug?

3 投票
1 回答
3826 浏览
提问于 2025-04-16 16:29

我有点震惊,Python的 datetime.isoformat() 函数居然没有返回正确的信息。这个函数在你给 fromtimestamp() 方法提供时区时,能够正确返回一个符合ISO 8601格式的字符串。但是,在计算结果时,它却忽略了这个时区。你可以看看下面的例子:

13:29 msimsonnet:~$ python
Python 2.7.1 (r271:86832, Jan 26 2011, 13:56:46) 
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
Running with pythonstartup.py
>>> import pytz,datetime
>>> datetime.datetime.fromtimestamp(1303876413).isoformat()
'2011-04-26T23:53:33'
>>> ny = pytz.timezone('America/New_York')
>>> sf = pytz.timezone('America/Los_Angeles')
>>> datetime.datetime.fromtimestamp(1303876413,ny).isoformat()
'2011-04-26T23:53:33-04:00'
>>> datetime.datetime.fromtimestamp(1303876413,sf).isoformat()
'2011-04-26T20:53:33-07:00'
>>> 

我在一个位于东部夏令时(EDT,距离格林威治标准时间(GMT)-400)的电脑上运行这个代码。时间1303876413实际上是2011年4月26日晚上11:53:33,也就是我第一次写这个问题的时候。注意,第一个例子中,简单调用 .isoformat() 返回了 '2011-04-26T23:53:33',这是错误的——它应该返回 '2011-04-26T23:53:33-04:00',因为它返回的是本地时间,而Python知道时区。第二个例子是正确的,但我强行传入了纽约的时区对象。第三个例子则完全错误——Python虽然保留了时区,但没有相应地调整时间。

补充说明:

如果你读了所有的评论,你会发现我想要的行为可以通过使用 utcfromtimestamp 而不是 fromtimestamp 来实现。

1 个回答

16

确保你使用的是“了解时区”的 datetime 对象,而不是“天真的”对象。

要创建一个“了解时区”的 datetime 对象,你需要在创建时提供时区信息。

更多详细信息可以查看这里: http://docs.python.org/library/datetime.html

另外,ISO 8601 并不要求必须有时区,isoformat 是符合这个标准的。实际上,它根本不支持时区,只支持与 UTC 的时间偏移。

ISO 8601 允许多种不同的格式,例如,以下都是有效的:

2011-04-27
2011-04-27 02:48Z
2011-04-27T02:48Z
2011-W17-3
2011-117

想了解更多,可以查看 http://en.wikipedia.org/wiki/ISO_8601

编辑,回应你的更新:

这并不是有bug,它正常工作,符合文档说明。天真的 datetime 对象根本没有任何时区信息。所以当你调用 isoformat() 时,不应该期待它能给你时区信息。

当你使用时间戳创建对象时,它会根据 Python 认为你的系统时区是什么来生成一个本地时间的 datetime 对象。这就是为什么当你给它一个 posix 时间戳时,它会为你转换成本地时间。需要注意的是,虽然 datetime 模块知道时间戳是 UTC,并且知道你的本地时区,而 fromtimestamp 使用这些信息来创建 datetime 对象,但生成的对象仍然是天真的,并不知道任何关于时区的事情。如果你想使用时间戳,就不应该使用天真的 datetime 对象。

根据文档:

一个天真的 datetime 对象 代表协调世界时(UTC)、本地时间 或其他时区的时间完全取决于 程序,就像一个特定的数字 代表米、英里或质量也取决于 程序一样。天真的 datetime 对象 容易理解和使用,但代价是忽略 现实中的某些方面。

以下是 fromtimestamp 方法的文档(加粗部分为强调):

返回与 POSIX 时间戳对应的本地日期和时间, 例如由 time.time() 返回的时间戳。如果 可选参数 tz 为 None 或未指定, 时间戳将转换为平台的本地日期和时间, 返回的 datetime 对象是天真的。

如果你想让它考虑时区,你需要传入一个时区,它不会自动推断。 仅仅因为它没有按照你认为的方式工作,并不意味着它不符合标准或有bug。它按预期工作,并且文档中也有说明。这几乎是与有bug相反的情况。

撰写回答