Python 如何解析ISO 8601格式的日期?

Python 如何解析ISO 8601格式的日期?,python,datetime,iso8601,datetime-parsing,rfc3339,Python,Datetime,Iso8601,Datetime Parsing,Rfc3339,我需要将像“2008-09-03T20:56:35.450686Z”这样的字符串解析为Python的datetime类型 我已经在Python标准库中找到了,但是它不是很方便 这样做的最佳方法是什么?您得到的确切错误是什么?是这样的吗 >>> datetime.datetime.strptime("2008-08-12T12:20:30.656234Z", "%Y-%m-%dT%H:%M:%S.Z") ValueError: time data did not match fo

我需要将像
“2008-09-03T20:56:35.450686Z”
这样的字符串解析为Python的
datetime
类型

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


这样做的最佳方法是什么?

您得到的确切错误是什么?是这样的吗

>>> datetime.datetime.strptime("2008-08-12T12:20:30.656234Z", "%Y-%m-%dT%H:%M:%S.Z")
ValueError: time data did not match format:  data=2008-08-12T12:20:30.656234Z  fmt=%Y-%m-%dT%H:%M:%S.Z
如果是,您可以在“.”上拆分输入字符串,然后将微秒添加到您得到的日期时间

试试这个:

>>> def gt(dt_str):
        dt, _, us= dt_str.partition(".")
        dt= datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
        us= int(us.rstrip("Z"), 10)
        return dt + datetime.timedelta(microseconds=us)

>>> gt("2008-08-12T12:20:30.656234Z")
datetime.datetime(2008, 8, 12, 12, 20, 30, 656234)
重新导入
导入日期时间
s=“2008-09-03T20:56:35.450686Z”
d=datetime.datetime(*映射(int,re.split(r'[^\d]',s)[:-1]))
尝试该模块;它就是这样做的


在python.org wiki的页面上还提到了其他几个选项。

注意,在python 2.6+和Py3K中,%f字符的捕捉时间为微秒

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

请参阅问题

,了解适用于2.X标准库的内容,请尝试:

calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))

calendar.timegm是time.mktime缺少的gm版本。

我已经为ISO 8601标准编写了一个解析器,并将其放在GitHub:。此实现支持规范中的所有内容,除了持续时间、间隔、周期间隔和Python datetime模块支持的日期范围之外的日期

包括测试!:P

包不仅要解析RFC 3339日期时间字符串(如问题中的字符串),还要解析其他不符合RFC 3339的日期和时间字符串(如不带UTC偏移量的字符串,或仅表示日期的字符串)


与:
isoparse
的比较可能比更粗糙的更为严格,但两者都相当宽容,并将尝试解释您传入的字符串。如果要消除任何误读的可能性,需要使用比这两个函数都更严格的函数


与Python 3.7+的内置解析器相比(请参阅):
isoparse
是一个完整的ISO-8601格式解析器,但是
fromsoformat
故意不这样做。请参阅函数的文档以了解此警告。

如果解析无效的日期字符串,python dateutil将引发异常,因此您可能希望捕获异常

from dateutil import parser
ds = '2012-60-31'
try:
  dt = parser.parse(ds)
except ValueError, e:
  print '"%s" is an invalid date' % ds

如果不想使用dateutil,可以尝试此函数:

def from_utc(utcTime,fmt="%Y-%m-%dT%H:%M:%S.%fZ"):
    """
    Convert UTC time string to time.struct_time
    """
    # change datetime.datetime to time, return time.struct_time type
    return datetime.datetime.strptime(utcTime, fmt)
测试:

结果:

datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)
在这些日子里,还可以用作第三方解决方案:

>>> import arrow
>>> date = arrow.get("2008-09-03T20:56:35.450686Z")
>>> date.datetime
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())

这适用于Python 3.2以后版本的stdlib(假设所有时间戳都是UTC):

比如说,

>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)
用于解析带时区的RFC 3339或ISO 8601日期时间,如问题中所示:

2008-09-03T20:56:35.450686Z
这是个坏主意

假设您希望支持完整的RFC 3339格式,包括对UTC偏移量(而非零)的支持,则这些答案所建议的代码不起作用。事实上,它无法工作,因为不可能使用
strtime
解析RFC3339语法。Python的datetime模块使用的格式字符串无法描述RFC3339语法

问题是UTC偏移量。要求每个日期时间包括UTC偏移量,并且这些偏移量可以是
Z
(Zulu time的缩写)或
+HH:MM
-HH:MM
格式,如
+05:00
-10:30

因此,这些都是有效的RFC 3339日期时间:

  • 2008-09-03T20:56:35.450686Z
  • 2008-09-03T20:56:35.450686+05:00
  • 2008-09-03T20:56:35.450686-10:30
唉,
strtime
strftime
使用的格式字符串没有与RFC 3339格式的UTC偏移量对应的指令。可以在中找到它们支持的指令的完整列表,列表中唯一包含的UTC偏移指令是
%z

%z UTC偏移量的形式为+HHMM或-HHMM(如果对象是原始的,则为空字符串)

示例:(空),+0000,-0400,+1030

这与RFC 3339偏移量的格式不匹配,实际上,如果我们尝试在格式字符串中使用
%z
,并解析RFC 3339日期,我们将失败:

>>从datetime导入datetime
>>>datetime.strtime(“2008-09-03T20:56:35.450686Z”,%Y-%m-%dT%H:%m:%S.%f%z”)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“/usr/lib/python3.4/_strtime.py”,第500行,在_strtime_datetime中
tt,分数=_strtime(数据字符串,格式)
文件“/usr/lib/python3.4/_strtime.py”,第337行,在_strtime中
(数据字符串,格式))
ValueError:时间数据“2008-09-03T20:56:35.450686Z”与格式“%Y-%m-%dT%H:%m:%S.%f%z”不匹配
>>>datetime.strtime(“2008-09-03T20:56:35.450686+05:00”,%Y-%m-%dT%H:%m:%S.%f%z”)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“/usr/lib/python3.4/_strtime.py”,第500行,在_strtime_datetime中
tt,分数=_strtime(数据字符串,格式)
文件“/usr/lib/python3.4/_strtime.py”,第337行,在_strtime中
(数据字符串,格式))
ValueError:时间数据“2008-09-03T20:56:35.450686+05:00”与格式“%Y-%m-%dT%H:%m:%S.%f%z”不匹配。
(实际上,上述内容正是您将在Python3中看到的内容。在Python2中,我们失败的原因更简单,就是这样。)

这里的多个答案建议使用
strtime
来解决这个问题,它们在格式字符串中包含一个文本
Z
,该文本与提问者示例日期时间字符串中的
Z
相匹配(并丢弃它,生成一个没有时区的
datetime
对象):

>>datetime.strtime(“2008-09-03T20:56:35.450686Z,%Y-%m-%dT%H:%m:%S.%fZ”)
datetime.datetime(2008,9,3,20,56,35450686)
由于这会丢弃原始datetime字符串中包含的时区信息,因此我们是否应该将此结果视为正确也是值得怀疑的。但更重要的是,由于这种方法涉及将特定UTC偏移量硬编码到格式字符串中,因此当它试图用不同的UTC偏移量解析任何RFC 3339 datetime时,它会阻塞:

from datetime import datetime, timezone, timedelta datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").replace( tzinfo=timezone(timedelta(0)))
>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)
2008-09-03T20:56:35.450686Z
class FixedOffset(tzinfo):
    """Fixed offset in minutes: `time = utc_time + utc_offset`."""
    def __init__(self, offset):
        self.__offset = timedelta(minutes=offset)
        hours, minutes = divmod(offset, 60)
        #NOTE: the last part is to remind about deprecated POSIX GMT+h timezones
        #  that have the opposite sign in the name;
        #  the corresponding numeric value is not used e.g., no minutes
        self.__name = '<%+03d%02d>%+d' % (hours, minutes, -hours)
    def utcoffset(self, dt=None):
        return self.__offset
    def tzname(self, dt=None):
        return self.__name
    def dst(self, dt=None):
        return timedelta(0)
    def __repr__(self):
        return 'FixedOffset(%d)' % (self.utcoffset().total_seconds() / 60)
    def __getinitargs__(self):
        return (self.__offset.total_seconds()/60,)

def parse_isoformat_datetime(isodatetime):
    try:
        return datetime.strptime(isodatetime, '%Y-%m-%dT%H:%M:%S.%f')
    except ValueError:
        pass
    try:
        return datetime.strptime(isodatetime, '%Y-%m-%dT%H:%M:%S')
    except ValueError:
        pass
    pat = r'(.*?[+-]\d{2}):(\d{2})'
    temp = re.sub(pat, r'\1\2', isodatetime)
    naive_date_str = temp[:-5]
    offset_str = temp[-5:]
    naive_dt = datetime.strptime(naive_date_str, '%Y-%m-%dT%H:%M:%S.%f')
    offset = int(offset_str[-4:-2])*60 + int(offset_str[-2:])
    if offset_str[0] == "-":
        offset = -offset
    return naive_dt.replace(tzinfo=FixedOffset(offset))
datetime.datetime.strptime(timestamp.translate(None, ':-'), "%Y%m%dT%H%M%S.%fZ")
import re
# this regex removes all colons and all 
# dashes EXCEPT for the dash indicating + or - utc offset for the timezone
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', timestamp)
datetime.datetime.strptime(conformed_timestamp, "%Y%m%dT%H%M%S.%f%z" )
import re
import datetime

# this regex removes all colons and all 
# dashes EXCEPT for the dash indicating + or - utc offset for the timezone
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', timestamp)

# split on the offset to remove it. use a capture group to keep the delimiter
split_timestamp = re.split(r"[+|-]",conformed_timestamp)
main_timestamp = split_timestamp[0]
if len(split_timestamp) == 3:
    sign = split_timestamp[1]
    offset = split_timestamp[2]
else:
    sign = None
    offset = None

# generate the datetime object without the offset at UTC time
output_datetime = datetime.datetime.strptime(main_timestamp +"Z", "%Y%m%dT%H%M%S.%fZ" )
if offset:
    # create timedelta based on offset
    offset_delta = datetime.timedelta(hours=int(sign+offset[:-2]), minutes=int(sign+offset[-2:]))
    # offset datetime with timedelta
    output_datetime = output_datetime + offset_delta
def parseISO8601DateTime(datetimeStr):
    import time
    from datetime import datetime, timedelta

    def log_date_string(when):
        gmt = time.gmtime(when)
        if time.daylight and gmt[8]:
            tz = time.altzone
        else:
            tz = time.timezone
        if tz > 0:
            neg = 1
        else:
            neg = 0
            tz = -tz
        h, rem = divmod(tz, 3600)
        m, rem = divmod(rem, 60)
        if neg:
            offset = '-%02d%02d' % (h, m)
        else:
            offset = '+%02d%02d' % (h, m)

        return time.strftime('%d/%b/%Y:%H:%M:%S ', gmt) + offset

    dt = datetime.strptime(datetimeStr, '%Y-%m-%dT%H:%M:%S.%fZ')
    timestamp = dt.timestamp()
    return dt + timedelta(hours=dt.hour-time.gmtime(timestamp).tm_hour)
#!/usr/bin/env python
from __future__ import with_statement, division, print_function
import sqlite3
import datetime

testtimes = [
    "2016-08-25T16:01:26.123456Z",
    "2016-08-25T16:01:29",
]
db = sqlite3.connect(":memory:")
c = db.cursor()
for timestring in testtimes:
    c.execute("SELECT strftime('%s', ?)", (timestring,))
    converted = c.fetchone()[0]
    print("%s is %s after epoch" % (timestring, converted))
    dt = datetime.datetime.fromtimestamp(int(converted))
    print("datetime is %s" % dt)
2016-08-25T16:01:26.123456Z is 1472140886 after epoch
datetime is 2016-08-25 12:01:26
2016-08-25T16:01:29 is 1472140889 after epoch
datetime is 2016-08-25 12:01:29
parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)
from django.utils import formats
from django.forms.fields import DateTimeField
from django.utils.dateparse import parse_datetime

class DateTimeFieldFixed(DateTimeField):
    def strptime(self, value, format):
        if format == 'iso-8601':
            return parse_datetime(value)
        return super().strptime(value, format)

DateTimeField.strptime = DateTimeFieldFixed.strptime
formats.ISO_INPUT_FORMATS['DATETIME_INPUT_FORMATS'].insert(0, 'iso-8601')
>>> from iso8601utils import parsers
>>> parsers.datetime('2008-09-03T20:56:35.450686Z')
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> import dateutil.parser as dp
>>> t = '1984-06-02T19:05:00.000Z'
>>> parsed_t = dp.parse(t)
>>> print(parsed_t)
datetime.datetime(1984, 6, 2, 19, 5, tzinfo=tzutc())
import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
import datetime
datetime.datetime.strptime('2018-01-31T09:24:31.488670+00:00', '%Y-%m-%dT%H:%M:%S.%f%z')
>>> from datetime import datetime
>>> datetime.fromisoformat('2011-11-04')
datetime.datetime(2011, 11, 4, 0, 0)
>>> import maya
>>> str = '2008-09-03T20:56:35.450686Z'
>>> maya.MayaDT.from_rfc3339(str).datetime()
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=<UTC>)
from dateutil import parser

date = parser.isoparse("2008-09-03T20:56:35.450686+01:00")
print(date)
2008-09-03 20:56:35.450686+01:00
from datetime import datetime

s = "2008-09-03T20:56:35.450686Z"

datetime.fromisoformat(s.replace('Z', '+00:00'))
# datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=datetime.timezone.utc)
%timeit datetime.fromisoformat(s.replace('Z', '+00:00'))
346 ns ± 22.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit datetime.strptime(s, '%Y-%m-%dT%H:%M:%S.%f%z')
14.2 µs ± 452 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit dateutil.parser.parse(s)
80.1 µs ± 3.32 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)