[python] 如何解析ISO 8601格式的日期?


Answers

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

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

here看到问题

Question

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

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

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




这适用于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)



你得到了什么确切的错误? 是否如下所示:

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



Django的parse_datetime ()函数支持UTC偏移量的日期:

parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)

因此它可用于在整个项目中的字段中解析iso-8601日期:

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



我发现ciso8601是解析ISO 8601时间戳的最快方法。 顾名思义,它是在C中实现的。

import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')

GitHub Repo自述文件显示了其相对于其他答案中列出的所有其他库的10倍以上的加速比。

我的个人项目涉及很多ISO 8601解析。 很高兴能够切换通话并提高10倍。 :)




对于与2.X标准库一起工作的东西,请尝试:

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

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




由于ISO 8601允许存在许多可选冒号和破折号的变体,基本上CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm] 。 如果你想使用strptime,你需要首先去掉这些变体。

目标是生成一个utc日期时间对象。

如果您只想要一个适用于UTC的基本案例,并使用Z后缀,如2016-06-29T19:36:29.3453Z

datetime.datetime.strptime(timestamp.translate(None, ':-'), "%Y%m%dT%H%M%S.%fZ")

如果您想要处理时区偏移,例如2016-06-29T19:36:29.3453-04002008-09-03T20:56:35.450686+05:00使用以下内容。 这些将把所有的变体转换成没有可变分隔符的东西,比如20080903T205635.450686+0500 ,使它更加一致/更容易解析。

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

如果你的系统不支持%z strptime指令(你会发现类似于ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z' ),那么你需要手动偏移Z (UTC)的时间。 注意%z可能无法在python版本<3的系统上工作,因为它依赖于系统/ python构建类型(即Jython,Cython等)不同的c库支持。

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



我是iso8601utils的作者。 它可以在githubPyPI上找到。 以下是您可以如何解析您的示例:

>>> from iso8601utils import parsers
>>> parsers.datetime('2008-09-03T20:56:35.450686Z')
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)

希望这可以帮助!




如果你正在使用Django,它提供了dateparse模块 ,它接受一系列类似于ISO格式的格式,包括时区。

如果你没有使用Django,并且你不想使用这里提到的其他库中的一个,那么你可以将Django的源代码dateparse改为你的项目。




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






Related