How do I parse an ISO 8601-formatted date?

后端 未结 27 2310
小鲜肉
小鲜肉 2020-11-21 06:08

I need to parse RFC 3339 strings like \"2008-09-03T20:56:35.450686Z\" into Python\'s datetime type.

I have found strptime in the Python sta

27条回答
  •  我在风中等你
    2020-11-21 06:17

    Several answers here suggest using datetime.datetime.strptime to parse RFC 3339 or ISO 8601 datetimes with timezones, like the one exhibited in the question:

    2008-09-03T20:56:35.450686Z
    

    This is a bad idea.

    Assuming that you want to support the full RFC 3339 format, including support for UTC offsets other than zero, then the code these answers suggest does not work. Indeed, it cannot work, because parsing RFC 3339 syntax using strptime is impossible. The format strings used by Python's datetime module are incapable of describing RFC 3339 syntax.

    The problem is UTC offsets. The RFC 3339 Internet Date/Time Format requires that every date-time includes a UTC offset, and that those offsets can either be Z (short for "Zulu time") or in +HH:MM or -HH:MM format, like +05:00 or -10:30.

    Consequently, these are all valid 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

    Alas, the format strings used by strptime and strftime have no directive that corresponds to UTC offsets in RFC 3339 format. A complete list of the directives they support can be found at https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior, and the only UTC offset directive included in the list is %z:

    %z

    UTC offset in the form +HHMM or -HHMM (empty string if the the object is naive).

    Example: (empty), +0000, -0400, +1030

    This doesn't match the format of an RFC 3339 offset, and indeed if we try to use %z in the format string and parse an RFC 3339 date, we'll fail:

    >>> 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'

    (Actually, the above is just what you'll see in Python 3. In Python 2 we'll fail for an even simpler reason, which is that strptime does not implement the %z directive at all in Python 2.)

    The multiple answers here that recommend strptime all work around this by including a literal Z in their format string, which matches the Z from the question asker's example datetime string (and discards it, producing a datetime object without a timezone):

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

    Since this discards timezone information that was included in the original datetime string, it's questionable whether we should regard even this result as correct. But more importantly, because this approach involves hard-coding a particular UTC offset into the format string, it will choke the moment it tries to parse any RFC 3339 datetime with a different UTC offset:

    >>> 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'

    Unless you're certain that you only need to support RFC 3339 datetimes in Zulu time, and not ones with other timezone offsets, don't use strptime. Use one of the many other approaches described in answers here instead.

提交回复
热议问题