我需要将RFC 3339字符串(如“2008-09-03T20:56:55.450686Z”)解析为Python的datetime类型。

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

最好的方法是什么?


当前回答

如果解析无效的日期字符串,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

其他回答

你得到的确切错误是什么?它像下面这样吗?

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

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

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)

另一种方法是为ISO-8601使用专用解析器,即使用dateutil解析器的等参函数:

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

标准Python函数datetime.fromisoformat的文档中也提到了该函数:

一个功能更全面的ISO 8601解析器dateutil.parser.isose是在第三方包dateutil中提供。

如果不想使用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)

测试:

from_utc("2007-03-04T21:08:12.123Z")

结果:

datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)

从Python 3.7开始,您基本上可以使用datetime.datetime.strptime解析RFC 3339日期时间,如下所示:

from datetime import datetime

def parse_rfc3339(datetime_str: str) -> datetime:
    try:
        return datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%S.%f%z")
    except ValueError:
        # Perhaps the datetime has a whole number of seconds with no decimal
        # point. In that case, this will work:
        return datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%S%z")

这有点尴尬,因为我们需要尝试两种不同的格式字符串,以便同时支持小数秒的日期时间(如2022-01-01T12:12:12.123Z)和没有小数秒的(如2021-01-01T12:12Z),这两种格式在RFC 3339下都是有效的。但只要我们做一点逻辑,这就行得通。

此方法需要注意的一些注意事项:

它在技术上并不完全支持RFC 3339,因为RFC 3339允许您使用空格而不是t来分隔日期和时间,尽管RFC 3339声称是ISO 8601的概要文件,但ISO 8601不允许这样做。如果您想支持RFC 3339的这种愚蠢的怪癖,可以在函数的开头添加datetime_str=datetime_str.replace(“”,“T”)。我上面的实现比严格的RFC 3339解析器应该更宽松,因为它将允许时区偏移,如+0500而不带冒号,而RFC 3339不支持。如果您不仅想解析known-to-be-RFC-339日期时间,而且还想严格验证您获得的日期时间是否为RFC 3339,请使用另一种方法或添加您自己的逻辑来验证时区偏移格式。这个函数肯定不支持所有的ISO 8601,它包括比RFC 3339更广泛的格式。(例如,2009-W01-1是有效的ISO 8601日期。)它在Python 3.6或更早版本中不起作用,因为在那些旧版本中,%z说明符只匹配+0500或-0430或+0000等时区偏移,而不是+05:00或-04:30或z等RFC 3339时区偏移。