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

How do I parse an ISO 8601-formatted date?

提问人:Alexander Artemenko 提问时间:9/24/2008 最后编辑:CommunityAlexander Artemenko 更新时间:10/16/2023 访问量:718425

问:

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

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

最好的方法是什么?

python datetime iso8601 datetime 解析 rfc3339

评论

1赞 jfs 2/2/2016
相关新闻: 使用 strptime 将带有偏移量的时间戳转换为日期时间 obj
11赞 Basil Bourque 8/4/2018
需要明确的是:ISO 8601 是主要标准。RFC 3339 是 ISO 8601 的自称“配置文件”,它对 ISO 8601 规则进行了一些不明智的覆盖

答:

39赞 tzot 9/24/2008 #1

您得到的确切错误是什么?是不是像下面这样?

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

评论

12赞 Alexander Artemenko 9/24/2008
你不能只是剥离.Z,因为它表示时区,可以不同。我需要将日期转换为UTC时区。
0赞 tzot 9/25/2008
普通 datetime 对象没有时区概念。如果您的所有时间都以“Z”结尾,则您获得的所有日期时间都是UTC(祖鲁时间)。
9赞 quodlibetor 7/17/2012
此外,“%f”是微秒说明符,因此(时区幼稚)strptime 字符串如下所示: “%Y-%m-%dT%H:%M:%S.%f” 。
1赞 Mark Amery 6/8/2015
如果给定的日期时间字符串的 UTC 偏移量不是“Z”,这将引发异常。它不支持整个 RFC 3339 格式,并且与其他正确处理 UTC 偏移量的格式相比,它是一个较差的答案。
1赞 Nelson 8/9/2022
Python 3.11 有很大的改进,可以处理大多数 iso8601 和 rfc3339 格式。docs.python.org/3.11/library/......datetime.fromisoformat
27赞 Ted 9/24/2008 #2
import re
import datetime
s = "2008-09-03T20:56:35.450686Z"
d = datetime.datetime(*map(int, re.split(r'[^\d]', s)[:-1]))

评论

90赞 umbrae 12/21/2011
我不同意,这实际上是不可读的,据我所知,没有考虑到祖鲁语 (Z),即使提供了时区数据,它也会使这个日期时间幼稚。
15赞 Tobia 11/21/2012
我觉得它很有可读性。事实上,这可能是在不安装其他软件包的情况下进行转换的最简单、最有效的方法。
3赞 Xuan 5/21/2013
这相当于 d=datetime.datetime(*map(int, re.split('\D', s)[:-1])) 我想。
4赞 jfs 5/16/2014
一个变体:datetime.datetime(*map(int, re.findall('\d+', s))
6赞 w00t 6/13/2014
这会导致一个没有时区的朴素 datetime 对象,对吧?所以 UTC 位在翻译中丢失了?
85赞 Nicholas Riley 9/24/2008 #3

尝试 iso8601 模块;它正是这样做的。

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

评论

1赞 Pakman 4/26/2012
简单如iso8601.parse_date("2008-09-03T20:56:35.450686Z")
3赞 Nicholas Riley 9/20/2012
问题不在于“如何解析 ISO 8601 日期”,而在于“如何解析这种确切的日期格式”。
3赞 Tobia 1/28/2013
@tiktak OP 问“我需要解析像 X 这样的字符串”,在尝试了两个库之后,我的回答是使用另一个库,因为 iso8601 仍有重要问题悬而未决。我是否参与这样一个项目与答案完全无关。
6赞 Dave Hein 11/13/2014
iso8601,又名 pyiso8601,最近于 2014 年 2 月更新。最新版本支持更广泛的 ISO 8601 字符串集。我一直在我的一些项目中使用效果很好。
1赞 boxed 1/5/2016
可悲的是,pypi 上名为“iso8601”的库非常不完整。它明确指出,它不会仅仅为了举一个例子而处理基于周数的日期。
233赞 sethbc 9/24/2008 #4

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

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

在此处查看问题

评论

4赞 Danny Staple 2/3/2015
注意 - 如果使用 Naive 日期时间 - 我认为你根本没有得到 TZ - Z 可能与任何内容都不匹配。
2赞 ashim888 2/9/2016
在我的情况下,%f 捕获了微秒而不是 Z,所以这成功了datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
2赞 Robino 11/13/2017
Py3K 是否意味着 Python 3000?!?
1赞 Robino 11/14/2017
如果没有 ms 或 tz,则失败。
4赞 Throw Away Account 10/2/2018
@Robino IIRC 中,“Python 3000”是现在称为 Python 3 的旧名称。
1赞 Gordon Wrigley 7/21/2011 #5

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

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

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

评论

2赞 gatoatigrado 1/29/2013
这只是忽略了时区“2013-01-28T14:01:01.335612-08:00”-->解析为 UTC,而不是 PDT
0赞 Mark Amery 10/9/2022
除了@gatoatigrado指出忽略时区之外,如果您给它输入一个时区但不是小数秒数(因此没有字符),这也会引发异常,例如 .此类值是有效的 RFC 3339 和 ISO 8601 日期时间字符串,因此分析器不应阻塞它。.2022-10-09T15:49:22-07:00
9赞 boxed 3/2/2013 #6

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

包括测试!:P

评论

3赞 Samuel Liew 9/23/2018
通常,指向工具或库的链接应附有使用说明、链接资源如何适用于问题的具体说明、一些示例代码,或者如果可能的话,上述所有内容。
670赞 Flimm 3/5/2013 #7

isoparse来自 python-dateutil 的函数

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

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

python-dateutil 包还具有 dateutil.parser.parse与 相比,它可能不那么严格,但它们都非常宽容,并且会尝试解释您传入的字符串。如果要消除任何误读的可能性,则需要使用比这些函数中的任何一个更严格的功能。isoparse

与 Python 3.7+ 内置的 datetime.datetime.fromisoformat 比较

dateutil.parser.isoparse是一个完整的 ISO-8601 格式解析器,但在 Python ≤ 3.10 中故意不是。在 Python 3.11 中,支持有效 ISO 8601 中的几乎所有字符串。有关此警告,请参阅的文档。(请参阅此答案)。fromisoformatfromisoformatfromisoformat

评论

107赞 cod3monk3y 3/13/2014
对于懒惰的人来说,它是通过 not 安装的,所以: 。python-dateutildateutilpip install python-dateutil
32赞 ivan_pozdeev 4/24/2015
请注意,它是故意的:它试图猜测格式,并在模棱两可的情况下做出不可避免的假设(只能手动定制)。因此,仅当您需要解析未知格式的输入并且可以容忍偶尔的误读时才使用它。dateutil.parser
2赞 timbo 6/24/2016
同意。例如,传递 9999 的“日期”。这将返回与 datetime(9999, current month, current day) 相同的结果。在我看来,这不是一个有效的日期。
2赞 bgusach 1/10/2018
@ivan_pozdeev您推荐什么软件包进行非猜测解析?
2赞 theEpsilon 1/15/2020
@ivan_pozdeev模块有一个更新,上面写着 ISO8601 日期:dateutil.readthedocs.io/en/stable/......
2赞 user2646026 8/9/2013 #8

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

评论

0赞 Flimm 6/1/2021
我认为它有时会抛出异常,如果它能尽最大努力猜测日期时间是什么,则不能保证抛出异常。
1赞 Wolfgang Kuehn 9/16/2021
错误隐藏是反模式的前三名:不要。
12赞 enchanter 3/28/2014 #9

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

评论

5赞 Mark Amery 6/8/2015
此答案依赖于将特定的 UTC 偏移量(即“Z”,表示 +00:00)硬编码为传递给 的格式字符串。这是一个坏主意,因为它将无法解析具有不同 UTC 偏移量的任何日期时间并引发异常。请参阅我的答案,该答案描述了如何使用 strptime 解析 RFC 3339 实际上是不可能的。strptime
1赞 Sasha 7/27/2015
它是硬编码的,但当您只需要解析 zulu 时,它就足够了。
1赞 Mark Amery 8/20/2015
@alexander 是的 - 例如,如果您知道日期字符串是使用 JavaScript 的 toISOString 方法生成的,则可能是这种情况。但是这个答案中没有提到祖鲁时间日期的限制,问题也没有表明这就是所需要的,只是使用通常同样方便,而且可以解析的内容不那么狭窄。dateutil
22赞 Ilker Kesen 2/16/2015 #10

如今,Arrow 还可以用作第三方解决方案:

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

评论

3赞 danizen 2/5/2019
只需使用 python-dateutil - 箭头需要 python-dateutil。
8赞 Benjamin Riggs 3/11/2015 #11

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

评论

2赞 Mark Amery 6/8/2015
此答案依赖于将特定的 UTC 偏移量(即“Z”,表示 +00:00)硬编码为传递给 的格式字符串。这是一个坏主意,因为它将无法解析具有不同 UTC 偏移量的任何日期时间并引发异常。请参阅我的答案,该答案描述了如何使用 strptime 解析 RFC 3339 实际上是不可能的。strptime
1赞 Benjamin Riggs 12/30/2015
从理论上讲,是的,这失败了。在实践中,我从未遇到过不是祖鲁时间的 ISO 8601 格式的日期。对于我偶尔的需求,这效果很好,并且不依赖于某些外部库。
4赞 jfs 12/31/2015
您可以使用 代替 .此外,如果您提供 utc tzinfo 对象,则代码在 Python 2.6+ 中(至少)有效timezone.utctimezone(timedelta(0))
0赞 theannouncer 2/26/2019
不管你是否遇到过它,它都不符合规范。
0赞 sventechie 3/27/2019
您可以在最新版本的 Python 中使用 for 时区。%Z
194赞 Mark Amery 6/8/2015 #12

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

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

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

  • 从技术上讲,它并不完全支持 RFC 3339,因为 RFC 3339 奇怪地允许您使用空格而不是 a 来分隔日期和时间,即使 RFC 3339 声称是 ISO 8601 的配置文件,而 ISO 8601 不允许这样做。如果你想支持RFC 3339的这个愚蠢的怪癖,你可以添加到函数的开头。Tdatetime_str = datetime_str.replace(' ', 'T')
  • 我上面的实现比严格的 RFC 3339 解析器稍微宽松一些,因为它允许时区偏移量,例如没有冒号,而 RFC 3339 不支持。如果您不仅要解析已知的 RFC-3339 日期时间,还想严格验证您得到的日期时间是否为 RFC 3339,请使用其他方法或添加您自己的逻辑来验证时区偏移量格式。+0500
  • 此函数绝对不支持所有 ISO 8601,它包含的格式比 RFC 3339 广泛得多。(例如 是有效的 ISO 8601 日期。2009-W01-1
  • 它在 Python 3.6 或更早版本中不起作用,因为在那些旧版本中,说明符仅匹配时区偏移量,例如 or 或 ,而不是 RFC 3339 时区偏移量,例如 or 或 。%z+0500-0430+0000+05:00-04:30Z
13赞 Don Kirkby 10/1/2015 #13

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

如果你不使用 Django,并且你不想使用这里提到的其他库之一,你可能会将 Django 源代码用于 dateparse 调整到你的项目中。

评论

0赞 djvg 12/14/2018
Django 在你设置字符串值时会使用它。DateTimeField
1赞 omikron 3/14/2016 #14

感谢伟大的 Mark Amery 的回答,我设计了函数来解释所有可能的 ISO 日期时间格式:

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))
3赞 theannouncer 6/29/2016 #15

因为 ISO 8601 允许存在许多可选冒号和破折号的变化,基本上.如果要使用 strptime,则需要先去除这些变体。

目标是生成一个 utc datetime 对象。
CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]


如果您只想要一个适用于 UTC 的基本情况,带有 Z 后缀,例如:
如果您想处理时区偏移量,例如或使用以下内容。这些会将所有变体转换为没有变量分隔符的内容,例如使其更一致/更易于解析。
如果您的系统不支持 strptime 指令(您会看到类似 )的内容,那么您需要手动偏移时间(UTC)。注意:在 python 版本 3 中<您的系统可能无法运行,因为它取决于 c 库支持,该支持因系统/python 构建类型(即 Jython、Cython 等)而异。
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:0020080903T205635.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" )

%zValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'Z%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

评论

0赞 Mark Amery 10/10/2022
这被打破了;一些快速实验表明,如果 是 或 ,则会引发异常,这两者都是有效的 RFC 3339 和 ISO 8601 日期时间。timestamp'2016-06-29T19:36:29.123Z''2016-06-29T19:36:29+00:00'
-2赞 Denny Weinberg 8/9/2016 #16
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)

请注意,我们应该查看字符串是否不以 结尾,我们可以使用 进行解析。Z%z

评论

1赞 Wolfgang Kuehn 9/16/2021
90% 的死代码,10% 的错误:不要使用!
7赞 Damian Yerrick 8/26/2016 #17

在不安装第三方模块的情况下,在所有受支持的 Python 版本中将类似 ISO 8601 的日期字符串转换为 UNIX 时间戳或对象的一种直接方法是使用 SQLite 的日期解析器datetime.datetime

#!/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

评论

14赞 wchargin 2/1/2019
谢谢。这太恶心了。我喜欢。
1赞 Havok 2/8/2020
多么不可思议,令人敬畏,美丽的黑客!谢谢!
1赞 Wolfgang Kuehn 9/16/2021
欢迎来到“坏与丑”部分。
0赞 Mark Amery 10/10/2022
请注意,SQLite的日期和时间解析比RFC 3339更宽松,并且不足以处理所有ISO 8601,因此它不是解析任何一种格式的完美方法。此外,这是一个可怕的黑客。但我认为它避免了安装第三方库的需要这一事实是一种美德!
7赞 Artem Vasilev 9/8/2016 #18

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')
8赞 Marc Wilson 10/26/2016 #19

我是iso8601utils的作者。它可以在 GitHubPyPI 上找到。以下是分析示例的方法:

>>> from iso8601utils import parsers
>>> parsers.datetime('2008-09-03T20:56:35.450686Z')
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
21赞 Blairg23 3/1/2017 #20

只需使用以下模块:python-dateutil

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

文档

评论

0赞 Mark Amery 10/10/2022
dateutil.parser.parse将接受绝对不是 ISO 8601 的格式,例如 .如果你特别想要ISO 8601解析,你可能更愿意使用,正如Flimms的回答所建议的那样。"Sat Oct 11 17:13:46 UTC 2003"dateutil.parse.isoparse
16赞 movermeyer 3/28/2017 #21

我发现 ciso8601 是解析 ISO 8601 时间戳的最快方法。

它还完全支持 RFC 3339,以及用于严格解析 RFC 3339 时间戳的专用函数。

用法示例:

>>> import ciso8601
>>> ciso8601.parse_datetime('2014-01-09T21')
datetime.datetime(2014, 1, 9, 21, 0)
>>> ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
datetime.datetime(2014, 1, 9, 21, 48, 0, 921000, tzinfo=datetime.timezone(datetime.timedelta(seconds=19800)))
>>> ciso8601.parse_rfc3339('2014-01-09T21:48:00.921000+05:30')
datetime.datetime(2014, 1, 9, 21, 48, 0, 921000, tzinfo=datetime.timezone(datetime.timedelta(seconds=19800)))

GitHub Repo README 显示了它们与其他答案中列出的所有其他库相比的加速速度。

我个人的项目涉及大量的ISO 8601解析。能够切换呼叫并走得更快真是太好了。:)

编辑:从那以后,我成为了 ciso8601 的维护者。现在比以往任何时候都快!

评论

0赞 hamx0r 7/4/2018
这看起来像一个很棒的图书馆!对于那些想要在 Google App Engine 上优化ISO8601解析的人来说,遗憾的是,我们不能使用它,因为它是一个 C 库,但您的基准测试很有见地,表明原生是下一个最快的解决方案。感谢您将所有这些信息放在一起!datetime.strptime()
3赞 movermeyer 7/4/2018
@hamx0r,请注意,这不是一个完整的 ISO 8601 解析库。如果你使用的是 Python 3.7,你可以使用这种方法,它更灵活一些。您可能对这个更完整的解析器列表感兴趣,它应该很快合并到 ciso8601 README 中。datetime.strptime()datetime.fromisoformat()
0赞 d_- 7/28/2018
CISO8601 工作得很好,但必须首先执行“pip install pytz”,因为如果没有 pytz 依赖项,就无法解析带有时区信息的时间戳。示例如下所示:dob = ciso8601.parse_datetime(result['dob']['date'])
2赞 movermeyer 7/30/2018
@Dirk,仅在 Python 2 中。但即便如此,也应该在下一个版本中删除。
49赞 Andreas Profous 1/31/2018 #22

从 Python 3.7 开始,strptime 支持 UTC 偏移量中的冒号分隔符(来源)。因此,您可以使用:

import datetime

def parse_date_string(date_string: str) -> datetime.datetime
    try:
       return datetime.datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%S.%f%z')
    except ValueError:
       return datetime.datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%S%z')

编辑:

正如 Martijn 所指出的,如果您使用 isoformat() 创建了 datetime 对象,则只需使用 .datetime.fromisoformat()

编辑2:

正如马克·埃默里(Mark Amery)所指出的,我添加了一个尝试。除了块,以考虑丢失的小数秒。

评论

8赞 Martijn Pieters 1/30/2019
但在 3.7 中,您还可以自动处理像输入一样的字符串:.datetime.fromisoformat()datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00')
2赞 Andreas Profous 6/20/2019
好点子。我同意,我建议使用datetime.fromisoformat()datetime.isoformat()
0赞 Danielo515 2/22/2021
这是唯一真正符合问题标准的答案。如果您必须使用 strptime,这是正确答案
0赞 Eric 3/30/2021
您的示例在 Python 3.6 上失败:这是由于不匹配。但是,匹配项请参阅 python doc docs.python.org/3.6/library/...ValueError: time data '2018-01-31T09:24:31.488670+00:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'%z+00:00+0000%z
3赞 Martijn Pieters 11/26/2022
@MarkAmery:Python 3.11 得到了进一步的改进,它现在可以处理时区:produce .fromisoformat()Zdatetime.fromisoformat('2018-01-31T09:24:31Z')datetime.datetime(2018, 1, 31, 9, 24, 31, tzinfo=datetime.timezone.utc)
472赞 Taku 4/12/2018 #23

从 Python 3.11 开始,标准库支持任何有效的 ISO 8601 输入。在早期版本中,它仅解析特定的子集,请参阅文档中的警告说明。如果您使用的是 Python 3.10 或更早版本,请参阅标准库外部函数的其他答案。文档:datetime.fromisoformat

类方法 DateTime.FromIsoFormat(date_string)

以任何有效的 ISO 8601 格式返回与date_string相对应的 a,但以下情况除外:datetime

  1. 时区偏移量可能具有小数秒。
  2. 分隔符可以替换为任何单个 unicode 字符。T
  3. 目前不支持序号日期。
  4. 不支持小数小时和分钟。

例子:

>>> from datetime import datetime
>>> datetime.fromisoformat('2011-11-04')
datetime.datetime(2011, 11, 4, 0, 0)
>>> datetime.fromisoformat('20111104')
datetime.datetime(2011, 11, 4, 0, 0)
>>> datetime.fromisoformat('2011-11-04T00:05:23')
datetime.datetime(2011, 11, 4, 0, 5, 23)
>>> datetime.fromisoformat('2011-11-04T00:05:23Z')
datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone.utc)
>>> datetime.fromisoformat('20111104T000523')
datetime.datetime(2011, 11, 4, 0, 5, 23)
>>> datetime.fromisoformat('2011-W01-2T00:05:23.283')
datetime.datetime(2011, 1, 4, 0, 5, 23, 283000)
>>> datetime.fromisoformat('2011-11-04 00:05:23.283')
datetime.datetime(2011, 11, 4, 0, 5, 23, 283000)
>>> datetime.fromisoformat('2011-11-04 00:05:23.283+00:00')
datetime.datetime(2011, 11, 4, 0, 5, 23, 283000, tzinfo=datetime.timezone.utc)
>>> datetime.fromisoformat('2011-11-04T00:05:23+04:00')   
datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone(datetime.timedelta(seconds=14400)))

3.7 新版功能.

在 3.11 版更改:以前,此方法仅支持可由 date.isoformat() 或 datetime.isoformat() 发出的格式。

评论

12赞 Hendy Irawan 7/17/2018
这很奇怪。因为 a 可能包含 ,因此输出一个时区,但不解析 tzinfo ?似乎是一个错误..datetimetzinfodatetime.fromisoformat()
68赞 Flimm 8/24/2018
不要错过文档中的注释,它不接受所有有效的 ISO 8601 字符串,只接受由 .由于尾随,它不接受问题中的示例,但它确实接受 。isoformat"2008-09-03T20:56:35.450686Z"Z"2008-09-03T20:56:35.450686"
81赞 jox 12/2/2018
为了正确支持输入脚本,可以使用 修改。Zdate_string.replace("Z", "+00:00")
17赞 Felk 5/29/2019
请注意,在几秒钟内,它只能处理精确的 0、3 或 6 位小数。如果输入数据有 1、2、4、5、7 位或更多小数位,解析将失败!
4赞 jox 12/29/2021
@kevinarpe不,似乎期待另一种格式。我刚刚测试了这两个版本,虽然它运行良好,但我得到“ValueError:无效的isoformat字符串”。datetime.fromisoformat+00:00+0000
3赞 jrc 9/25/2018 #24

现在有Maya: Datetimes for Humans,来自流行的Requests: HTTP for Humans™™包的作者:

>>> 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>)
-1赞 A T 5/15/2019 #25

最初我尝试了:

from operator import neg, pos
from time import strptime, mktime
from datetime import datetime, tzinfo, timedelta

class MyUTCOffsetTimezone(tzinfo):
    @staticmethod
    def with_offset(offset_no_signal, signal):  # type: (str, str) -> MyUTCOffsetTimezone
        return MyUTCOffsetTimezone((pos if signal == '+' else neg)(
            (datetime.strptime(offset_no_signal, '%H:%M') - datetime(1900, 1, 1))
          .total_seconds()))

    def __init__(self, offset, name=None):
        self.offset = timedelta(seconds=offset)
        self.name = name or self.__class__.__name__

    def utcoffset(self, dt):
        return self.offset

    def tzname(self, dt):
        return self.name

    def dst(self, dt):
        return timedelta(0)


def to_datetime_tz(dt):  # type: (str) -> datetime
    fmt = '%Y-%m-%dT%H:%M:%S.%f'
    if dt[-6] in frozenset(('+', '-')):
        dt, sign, offset = strptime(dt[:-6], fmt), dt[-6], dt[-5:]
        return datetime.fromtimestamp(mktime(dt),
                                      tz=MyUTCOffsetTimezone.with_offset(offset, sign))
    elif dt[-1] == 'Z':
        return datetime.strptime(dt, fmt + 'Z')
    return datetime.strptime(dt, fmt)

但这在负时区不起作用。但是我在 Python 3.7.3 中工作正常:

from datetime import datetime


def to_datetime_tz(dt):  # type: (str) -> datetime
    fmt = '%Y-%m-%dT%H:%M:%S.%f'
    if dt[-6] in frozenset(('+', '-')):
        return datetime.strptime(dt, fmt + '%z')
    elif dt[-1] == 'Z':
        return datetime.strptime(dt, fmt + 'Z')
    return datetime.strptime(dt, fmt)

一些测试指出,输出仅因微秒精度而异。在我的机器上达到了 6 位数的精度,但 YMMV:

for dt_in, dt_out in (
        ('2019-03-11T08:00:00.000Z', '2019-03-11T08:00:00'),
        ('2019-03-11T08:00:00.000+11:00', '2019-03-11T08:00:00+11:00'),
        ('2019-03-11T08:00:00.000-11:00', '2019-03-11T08:00:00-11:00')
    ):
    isoformat = to_datetime_tz(dt_in).isoformat()
    assert isoformat == dt_out, '{} != {}'.format(isoformat, dt_out)

评论

1赞 Prahlad Yeri 6/8/2019
请问你为什么这样做?像这样的普通元组不应该能够完成同样的事情吗?frozenset(('+', '-'))('+', '-')
0赞 A T 6/8/2019
当然,但这难道不是线性扫描而不是完美的哈希查找吗?
0赞 Mark Amery 10/10/2022
您的函数中至少存在几个错误:1. 尽管 ISO 8601 和 RFC 3339 日期时间有效,但秒中没有小数点的日期时间字符串(如)会触发异常,以及 2.时区偏移的处理方式与时区偏移量不同,即使它们应该意味着相同的事情。to_datetime_tz2019-03-11T08:00:00+11:00Z+00:00
0赞 Mark Amery 10/10/2022
至于@PrahladYeri关于冰冻集的观点,普拉拉德说得很对。只有两个项目,查找不可能更快,尤其是当您实际上必须构造和迭代一个等效的 2 项元组作为 .即使它更快,在 2 项集合中进行查找的成本也无关紧要。frozensetfrozenset
0赞 A T 10/16/2022
我想您可以对输入字符串进行长度检查以确定其中的内容。欢迎您编辑 3 年前>此答案。
7赞 zawuza 9/24/2019 #26

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

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.isoparse 是 在第三方软件包 dateutil 中可用。

71赞 FObersteiner 7/7/2020 #27

Python >= 3.11

fromisoformat 现在直接解析:Z

from datetime import datetime

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

datetime.fromisoformat(s)
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=datetime.timezone.utc)

Python 3.7 到 3.10

来自其中一个注释的简单选项:替换为 - 并使用:'Z''+00:00'fromisoformat

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)

为什么更喜欢?fromisoformat

虽然 可以将字符解析为 UTC,但 fromisoformat 的速度要快 ~ x40(对于 Python 3.11 甚至更快 ~x60):strptime%z'Z'

from datetime import datetime
from dateutil import parser

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

# Python 3.11+
%timeit datetime.fromisoformat(s)
85.1 ns ± 0.473 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

# Python 3.7 to 3.10
%timeit datetime.fromisoformat(s.replace('Z', '+00:00'))
134 ns ± 0.522 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

%timeit parser.isoparse(s)
4.09 µs ± 5.2 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

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

%timeit parser.parse(s)
28.5 µs ± 99.2 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

(GNU/Linux 上的 Python 3.11.3 x64)

Смотритетакже: 更快的 strptime

评论

2赞 FObersteiner 3/28/2021
@mikerodent:关键是解析但不知道 tzinfo 为 UTC 的日期时间。如果你的输入以 结尾,例如,你可以在将其输入之前删除 。其他 UTC 偏移量,例如 然后将被解析为静态 UTC 偏移量(而不是实际时区)。fromisoformat+00:00ZZ+00:00Zfromisoformat+05:30
0赞 mike rodent 6/16/2023
现在完全不同了(我对“意识”有了更多的了解)。但我在文档中指出“在 3.11 版中更改:以前,此方法仅支持可由 date.isoformat() 或 datetime.isoformat() 发出的格式”和“对应于任何有效 ISO 8601 格式的date_string”。这实际上可能不是人们想要的。of 更明确:“返回与以任何有效的 ISO 8601 格式给出的date_string相对应的日期......" ...它给出了一些令人惊讶的字符串示例,这些字符串的工作方式并不简单 YYYY-MM-DD。fromisoformatdatetime.date
5赞 Michael Dorner 7/28/2021 #28

如果无论如何都使用 pandas,我可以推荐 Timestamp。在那里你可以pandas

ts_1 = pd.Timestamp('2020-02-18T04:27:58.000Z')    
ts_2 = pd.Timestamp('2020-02-18T04:27:58.000')

咆哮:令人难以置信的是,在2021年,我们仍然需要担心诸如日期字符串解析之类的事情。

评论

1赞 Wolfgang Kuehn 9/16/2021
对于这个简单的情况,强烈建议不要使用 pandas:它依赖于违反 python 标准的 pytz 和 pd。Timestamp 巧妙地不是一个兼容的 datetime 对象。
0赞 Michael Dorner 9/17/2021
感谢您的评论。你有什么建议给我吗?我找不到pytz:github.com/pandas-dev/pandas/blob/...我不确定您指的是什么Python标准及其违规行为。
2赞 Wolfgang Kuehn 9/17/2021
请看保罗·甘斯勒(Paul Ganssle)的咆哮。至于不兼容,请同时执行 和 : 完全不一样。datetime.fromisoformat('2021-01-01T00:00:00+01:00').tzinfo.utcpandas.Timestamp('2021-01-01T00:00:00+01:00').tzinfo.utc
0赞 Michael Dorner 9/18/2021
感谢您对这项正在进行的工作的指导。我不知道这个问题,但我真的希望他们尽快解决它!但再说一遍:我不敢相信时间解析仍然是一个问题。:-)
2赞 Ash Nazg 11/9/2022 #29

datetime.fromisoformat()在 Python 3.11 中进行了改进,可以解析大多数 ISO 8601 格式

datetime.fromisoformat() 现在可用于解析大多数 ISO 8601 格式,但仅支持小数小时和分钟格式除外。以前,此方法仅支持可由 datetime.isoformat() 发出的格式。

>>> from datetime import datetime
>>> datetime.fromisoformat('2011-11-04T00:05:23Z')
datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone.utc)
>>> datetime.fromisoformat('20111104T000523')
datetime.datetime(2011, 11, 4, 0, 5, 23)
>>> datetime.fromisoformat('2011-W01-2T00:05:23.283')
datetime.datetime(2011, 1, 4, 0, 5, 23, 283000)

评论

1赞 John Leuenhagen 4/9/2023
对以前的情况有很好的改进。这应该是大多数人的首选。