Several answers here suggest 使用 datetime.datetime.strptime 解析带有时区的 RFC 3339 或 ISO 8601 日期时间,就像问题中展示的那样:
2008-09-03T20:56:35.450686Z
这是个坏主意。
假设您想要支持完整的 RFC 3339 格式,包括对 UTC 偏移量(非零)的支持,那么这些答案建议的代码不起作用。事实上,它不能工作,因为使用 strptime 解析 RFC 3339 语法是不可能的。 Python 的 datetime 模块使用的格式字符串无法描述 RFC 3339 语法。
问题在于 UTC 偏移量。 RFC 3339 Internet Date/Time Format 要求每个日期时间都包含一个 UTC 偏移量,并且这些偏移量可以是 Z(“祖鲁时间”的缩写)或 +HH:MM 或 -HH:MM 格式,例如 +05:00 或 @ 987654336@.
因此,这些都是有效的 RFC 3339 日期时间:
2008-09-03T20:56:35.450686Z
2008-09-03T20:56:35.450686+05:00
2008-09-03T20:56:35.450686-10:30
唉,strptime 和 strftime 使用的格式字符串没有对应于 RFC 3339 格式的 UTC 偏移量的指令。他们支持的指令的完整列表可以在https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior找到,列表中唯一包含的UTC偏移指令是%z:
%z
+HHMM 或 -HHMM 形式的 UTC 偏移量(如果对象是幼稚的,则为空字符串)。
示例:(空)、+0000、-0400、+1030
这与 RFC 3339 偏移的格式不匹配,事实上,如果我们尝试在格式字符串中使用 %z 并解析 RFC 3339 日期,我们将会失败:
>>> from datetime import datetime
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686Z' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
(实际上,上面的内容正是您在 Python 3 中看到的。在 Python 2 中,我们会因为更简单的原因失败,那就是 strptime does not implement the %z directive at all in Python 2。)
此处推荐strptime 的多个答案都可以通过在其格式字符串中包含文字Z 来解决此问题,该字符串与提问者示例日期时间字符串中的Z 匹配(并丢弃它,生成@987654350 @没有时区的对象):
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
由于这会丢弃原始日期时间字符串中包含的时区信息,因此我们是否应该将这个结果视为正确是值得怀疑的。但更重要的是,因为这种方法涉及将特定的 UTC 偏移量硬编码到格式字符串中,它会在尝试解析任何具有不同 UTC 偏移量的 RFC 3339 日期时间时阻塞:
>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%fZ")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'
除非您确定只需要支持祖鲁时间的 RFC 3339 日期时间,而不需要支持其他时区偏移的日期时间,否则不要使用 strptime。请改用此处答案中描述的许多其他方法之一。