如何将ISO 8601日期时间字符串转换为Python日期时间对象?

And*_*rov 397 python datetime iso8601 datetime-parsing

我正在以"2009-05-28T16:15:00"的格式获取日期时间字符串(我相信这是ISO 8601).一个hackish选项似乎是使用time.strptime并将元组的前六个元素传递给datetime构造函数来解析字符串,如:

datetime.datetime(*time.strptime("2007-03-04T21:08:12", "%Y-%m-%dT%H:%M:%S")[:6])
Run Code Online (Sandbox Code Playgroud)

我无法找到一种"更清洁"的方式来做到这一点.有吗?

Wes*_*ham 647

我更喜欢使用 dateutil库进行时区处理和一般的固态日期解析.如果您要获得ISO 8601字符串,例如:2010-05-08T23:41:54.000Z,您可以通过strptime解析它,特别是如果您事先不知道时区是否包含在内.pyiso8601有一些问题(检查他们的跟踪器),我在使用过程中遇到过,并且在几年内没有更新.相比之下,dateutil一直活跃并为我工作:

import dateutil.parser
yourdate = dateutil.parser.parse(datestring)
Run Code Online (Sandbox Code Playgroud)

  • @YuriRitvin:您提供的[链接](https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat)中的官方文档读取了以下**警告**:_这不支持解析任意 ISO 8601 字符串 - 它仅用作 datetime.isoformat() 的逆操作。第三方包 dateutil_ 中提供了功能更全的 ISO 8601 解析器 dateutil.parser.isoparse。所以,是的,即使对于 Python 3.7,我们又回到了“dateutil”包。 (31认同)
  • 从python 3.7开始,你可以使用`datetime.datetime.fromisoformat` https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat (26认同)
  • 是的,pyiso8601有一些非常微妙的问题,当它已经遍布整个代码时你可能会注意到这些问题.dateutil.parser非常好,但是如果有必要,应该密切注意强制执行tz-awareness. (5认同)
  • 2014年2月初对pyiso8601的更新解决了许多问题.它处理更广泛的有效ISO8601字符串集.值得再看看. (5认同)
  • 如果我错了,请纠正我,但是你所包含的时间示例中的Z不是特意指示UTC时间吗? (4认同)
  • 我一直在用大量的`elif试图使用`datetime.datetime.strptime`来处理我所有的各种日期时间格式.谢谢你向我展示了光明. (3认同)
  • _在版本 3.11 中更改:以前,此方法仅支持 date.isoformat() 或 datetime.isoformat()._ 可以发出的格式。_ - 听起来如果你有 >3.11(在我写这篇文章时非常新),你'很好,可以走了。 (2认同)

Phi*_*e F 78

使用Python 3并且没有外部库:

datetime.datetime.strptime('2019-01-04T16:41:24+0200', "%Y-%m-%dT%H:%M:%S%z")
Run Code Online (Sandbox Code Playgroud)

Python 2不支持%z格式说明符,因此最好在可能的地方明确使用Zulu时间:

datetime.datetime.strptime("2007-03-04T21:08:12Z", "%Y-%m-%dT%H:%M:%SZ")
Run Code Online (Sandbox Code Playgroud)

  • 你必须同意这一点,这与蟒蛇的意识形态相矛盾,相当不明显......"strptime"?难道他们不能使用一个有意义的名字,而不是传播一个旧的蹩脚的C名称?... (12认同)
  • 文档指出:“警告这不支持解析任意 ISO 8601 字符串 - 它仅用作 datetime.isoformat() 的逆操作。功能更全面的 ISO 8601 解析器 dateutil.parser.isoparse 在第三个中可用-派对包 dateutil。” 特别是,示例中给出的示例不起作用。它抛出“ValueError:无效的 isoformat 字符串:” (11认同)
  • 请注意,这会分析ISO 8601的一个子集.如果您告诉您的客户端您可以解析所有8601个日期时间,他们可能会发送一个没有短划线,没有冒号,一个周而不是一个月等的一个. (5认同)
  • 从Python 3.7开始,标准库中有这个类方法:“datetime.datetime.fromisoformat(date_string)”。请参阅https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat /sf/answers/3484882691/ (4认同)
  • 也许您正在查看datetime模块级别函数,而不是datetime.datetime类方法. (3认同)
  • 被否决是因为问题具体地说了 ISO-8601,同时避免了显式的字符串格式。我特别想找到一个答案,而不必知道如何明确的字符串格式。Python 的 str(datetime) 和 JavaScript 的 Date.toISOString() 也有一点不同。 (3认同)
  • @MarkLakata 从 Python 3.11 开始,它支持解析任意字符串:*版本 3.11 中的更改:以前,此方法仅支持 date.isoformat() 或 datetime.isoformat() 可以发出的格式。* [link]( https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat) (2认同)

the*_*cer 49

因为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")
Run Code Online (Sandbox Code Playgroud)

如果您想处理时区偏移2016-06-29T19:36:29.3453-0400,请2008-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" )
Run Code Online (Sandbox Code Playgroud)

如果你的系统不支持%zstrptime指令(你看到类似的东西ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z')那么你需要手动偏移Z(UTC)的时间.注意%z可能不适用于Python版本<3的系统,因为它依赖于C库支持,这种支持因系统/ Python构建类型(即Jython,Cython等)而异.

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
Run Code Online (Sandbox Code Playgroud)

  • 哦,亲爱的,Python.你到底在做什么?!? (3认同)

Avi*_*lax 38

Arrow看起来很有希望:

>>> import arrow
>>> arrow.get('2014-11-13T14:53:18.694072+00:00').datetime
datetime.datetime(2014, 11, 13, 14, 53, 18, 694072, tzinfo=tzoffset(None, 0))
Run Code Online (Sandbox Code Playgroud)

Arrow是一个Python库,它提供了一种合理,智能的创建,操作,格式化和转换日期和时间的方法.Arrow简单,轻巧,受到moment.js请求的启发.


Dan*_*l F 16

您应该密切关注时区信息,因为在将非tz感知日期时间与tz感知日期时间进行比较时可能会遇到麻烦.

最好总是使它们具有tz感知能力(即使只是作为UTC),除非你真的知道它为什么没有任何用处.

#-----------------------------------------------
import datetime
import pytz
import dateutil.parser
#-----------------------------------------------

utc = pytz.utc
BERLIN = pytz.timezone('Europe/Berlin')
#-----------------------------------------------

def to_iso8601(when=None, tz=BERLIN):
  if not when:
    when = datetime.datetime.now(tz)
  if not when.tzinfo:
    when = tz.localize(when)
  _when = when.strftime("%Y-%m-%dT%H:%M:%S.%f%z")
  return _when[:-8] + _when[-5:] # Remove microseconds
#-----------------------------------------------

def from_iso8601(when=None, tz=BERLIN):
  _when = dateutil.parser.parse(when)
  if not _when.tzinfo:
    _when = tz.localize(_when)
  return _when
#-----------------------------------------------
Run Code Online (Sandbox Code Playgroud)


Avi*_*lax 9

我还没有尝试过,但pyiso8601承诺支持这一点.

  • pyiso8601具有_very_有限范围的格式,它接受.更好地使用dateutil.parser - >"目前处理以下格式:1)2006-01-01T00:00:00Z 2)2006-01-01T00:00:00 [+ - ] 00:00"有[+ - ] 0000因为tz信息在iso标准下同样有效.IIRC在[+ - ] 0000它会丢弃tz信息...... (4认同)

Tob*_*obu 6

Isodate似乎拥有最完整的支持.

  • 语法为:`dt = isodate.parse_datetime(ts)` (2认同)

ron*_*nak 6

import datetime, time
def convert_enddate_to_seconds(self, ts):
    """Takes ISO 8601 format(string) and converts into epoch time."""
    dt = datetime.datetime.strptime(ts[:-7],'%Y-%m-%dT%H:%M:%S.%f')+\
                datetime.timedelta(hours=int(ts[-5:-3]),
                minutes=int(ts[-2:]))*int(ts[-6:-5]+'1')
    seconds = time.mktime(dt.timetuple()) + dt.microsecond/1000000.0
    return seconds
Run Code Online (Sandbox Code Playgroud)

这还包括毫秒和时区.

如果时间是'2012-09-30T15:31:50.262-08:00',这将转换为纪元时间.

>>> import datetime, time
>>> ts = '2012-09-30T15:31:50.262-08:00'
>>> dt = datetime.datetime.strptime(ts[:-7],'%Y-%m-%dT%H:%M:%S.%f')+ datetime.timedelta(hours=int(ts[-5:-3]), minutes=int(ts[-2:]))*int(ts[-6:-5]+'1')
>>> seconds = time.mktime(dt.timetuple()) + dt.microsecond/1000000.0
>>> seconds
1348990310.26
Run Code Online (Sandbox Code Playgroud)


bil*_*anH 6

双向:

ISO时代的时代:

isoTime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(epochTime))
Run Code Online (Sandbox Code Playgroud)

ISO时间到Epoch:

epochTime = time.mktime(time.strptime(isoTime, '%Y-%m-%dT%H:%M:%SZ'))
Run Code Online (Sandbox Code Playgroud)

  • 但您仅限于UTC(z) (2认同)
  • 既不是十进制秒也不是时区("Z"除外) (2认同)