如何解析ISO 8601

我需要将RFC 3339字符串解析为Python的datetime类型,如"2008-09-03T20:56:35.450686Z"

我已经在Python标准库中找到了strptime ,但它不是很方便。

做这个的最好方式是什么?


python-dateutil包不仅可以解析RFC 3339日期时间字符串(如问题中的日期时间字符串),还可以解析不符合RFC 3339的其他ISO 8601日期和时间字符串(例如没有UTC偏移量的字符串,或者代表只有一个日期)。

>>> import dateutil.parser
>>> dateutil.parser.parse('2008-09-03T20:56:35.450686Z') # RFC 3339 format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
>>> dateutil.parser.parse('2008-09-03T20:56:35.450686') # ISO 8601 extended format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.parse('20080903T205635.450686') # ISO 8601 basic format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.parse('20080903') # ISO 8601 basic format, date only
datetime.datetime(2008, 9, 3, 0, 0)

需要警告的是, dateutil.parser是故意dateutil.parser :它试图猜测格式,并在不明确的情况下做出不可避免的假设(只能手工定制)。 所以只有在需要解析未知格式的输入时才使用它,并且可以容忍偶然的误读。 (感谢ivan_pozdeev)

Pypi的名字是python-dateutil ,而不是dateutil (感谢code3monk3y):

pip install python-dateutil

请注意,在Python 2.6+和Py3K中,%f字符捕获微秒。

>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")

在这里看到问题


这里有几个答案建议使用datetime.datetime.strptime来解析具有时区的RFC 3339或ISO 8601日期时间,就像问题中展示的日期时间一样:

2008-09-03T20:56:35.450686Z

这是一个坏主意。

假设您要支持完整的RFC 3339格式,包括对UTC以外的零偏移的支持,那么这些答案建议的代码将不起作用。 事实上,它不能工作,因为使用strptime解析RFC 3339语法是不可能的。 Python日期时间模块使用的格式字符串不能描述RFC 3339语法。

问题是UTC偏移。 RFC 3339互联网日期/时间格式要求每个日期时间包括UTC偏移量,并且这些偏移量可以是Z (“祖鲁时间”的缩写)或+HH:MM-HH:MM格式,例如+05:00-10:30

因此,这些都是有效的RFC 3339 datetimes:

  • 2008-09-03T20:56:35.450686Z
  • 2008-09-03T20:56:35.450686+05:00
  • 2008-09-03T20:56:35.450686-10:30
  • 唉, strptimestrftime使用的格式字符串没有与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在Python 2中根本没有实现%z指令。)

    在这里推荐strptime所有解决strptime都是通过在其格式字符串中包含一个字面值Z ,该字符串与问题提交者的示例datetime字符串中的Z相匹配(并放弃它,生成不带时区的datetime对象):

    >>> 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 。 相反,使用其中一种解答中描述的其他方法。

    链接地址: http://www.djcxy.com/p/5985.html

    上一篇: How to parse an ISO 8601

    下一篇: Given a DateTime object, how do I get an ISO 8601 date in string format?