来自 python-dateutil 的 isoparse 函数
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 格式解析器,但 fromisoformat
故意不是。请参阅后一个函数的文档以了解此警告性警告。 (见this answer)。
自 Python 3.7 起,datetime
标准库具有用于反转 datetime.isoformat()
的函数。
classmethod datetime.fromisoformat(date_string):返回与 date_string 对应的 datetime,该 date_string 采用 date.isoformat() 和 datetime.isoformat() 发出的格式之一。具体来说,此函数支持以下格式的字符串:YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]] ] 其中 * 可以匹配任何单个字符。注意:这不支持解析任意 ISO 8601 字符串 - 它仅用作 datetime.isoformat() 的逆操作。示例: >>> from datetime import datetime >>> datetime.fromisoformat('2011-11-04') datetime.datetime(2011, 11, 4, 0, 0) ...
请务必阅读文档中的警告!
datetime
可能包含 tzinfo
,因此输出时区,但 datetime.fromisoformat()
不解析 tzinfo ?似乎是一个错误..
isoformat
生成的字符串。由于尾随 Z
,它不接受问题 "2008-09-03T20:56:35.450686Z"
中的示例,但它接受 "2008-09-03T20:56:35.450686"
。
Z
,可以使用 date_string.replace("Z", "+00:00")
修改输入脚本。
请注意,在 Python 2.6+ 和 Py3K 中,%f 字符会捕获微秒。
>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
见问题 here
strptime
解析 RFC 3339 实际上是不可能的。
datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
所以这成功了
Several answers here suggest 使用 datetime.datetime.strptime
解析带有时区的 RFC 3339 或 ISO 8601 日期时间,就像问题中展示的那样:
2008-09-03T20:56:35.450686Z
这是一个坏主意。
假设您想要支持完整的 RFC 3339 格式,包括支持除零以外的 UTC 偏移量,那么这些答案建议的代码不起作用。事实上,它不能工作,因为使用 strptime
解析 RFC 3339 语法是不可能的。 Python 的 datetime 模块使用的格式字符串无法描述 RFC 3339 语法。
问题是UTC偏移量。 RFC 3339 Internet Date/Time Format 要求每个日期时间都包含一个 UTC 偏移量,并且这些偏移量可以是 Z
(“祖鲁时间”的缩写)或 +HH:MM
或 -HH:MM
格式,如 +05:00
或 { 6}。
因此,这些都是有效的 RFC 3339 日期时间:
2008-09-03T20:56:35.450686Z
2008-09-03T20:56:35.450686+05:00
2008-09-03T20:56:35.450686-10:30
唉,strptime
和 strftime
使用的格式字符串没有对应于 RFC 3339 格式的 UTC 偏移量的指令。他们支持的指令的完整列表可以在 https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior 中找到,列表中包含的唯一 UTC 偏移指令是 %z
:
%z UTC 偏移量,格式为 +HHMM 或 -HHMM(如果对象是幼稚的,则为空字符串)。示例:(空)、+0000、-0400、+1030
这与 RFC 3339 偏移量的格式不匹配,实际上,如果我们尝试在格式字符串中使用 %z
并解析 RFC 3339 日期,我们将失败:
>>> from datetime import datetime
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686Z' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
(实际上,上面的内容正是您在 Python 3 中看到的。在 Python 2 中,我们会因为更简单的原因失败,那就是 strptime
does not implement the %z
directive at all in Python 2。)
这里推荐 strptime
的多个答案都通过在其格式字符串中包含一个文字 Z
来解决这个问题,它与提问者的示例日期时间字符串中的 Z
匹配(并丢弃它,产生一个没有时区):
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
由于这会丢弃原始日期时间字符串中包含的时区信息,因此我们是否应该将这个结果视为正确是值得怀疑的。但更重要的是,因为这种方法涉及将特定的 UTC 偏移量硬编码到格式字符串中,所以当它尝试使用不同的 UTC 偏移量解析任何 RFC 3339 日期时间时,它会阻塞:
>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%fZ")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'
除非您确定只需要支持 Zulu 时间的 RFC 3339 日期时间,而不需要支持其他时区偏移的日期时间,否则不要使用 strptime
。请改用此处答案中描述的许多其他方法之一。
strptime()
现在支持此答案中描述为不可能的所有内容(时区偏移中的“Z”字面量和“:”)。不幸的是,还有另一个极端情况使 RFC 3339 与 ISO 8601 根本不兼容,即前者允许负空时区偏移 -00:00,而后者则不允许。
试试 iso8601 模块;它正是这样做的。
python.org wiki 的 WorkingWithTime 页面上还提到了其他几个选项。
iso8601.parse_date("2008-09-03T20:56:35.450686Z")
一样简单
从 Python 3.7 开始,strptime 支持 UTC 偏移 (source) 中的冒号分隔符。所以你可以使用:
import datetime
datetime.datetime.strptime('2018-01-31T09:24:31.488670+00:00', '%Y-%m-%dT%H:%M:%S.%f%z')
编辑:
正如 Martijn 所指出的,如果您使用 isoformat() 创建了 datetime 对象,您可以简单地使用 datetime.fromisoformat()
。
datetime.fromisoformat()
,它会自动处理与您的输入类似的字符串:datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00')
。
datetime.fromisoformat()
和 datetime.isoformat()
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
请参阅 python 文档 docs.python.org/3.6/library/…
你得到的确切错误是什么?是不是像下面这样?
>>> 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)
""
或 "Z"
,则它必须是以小时/分钟为单位的偏移量,可以直接添加到日期时间对象中/从日期时间对象中减去。您可以创建一个 tzinfo 子类来处理它,但这可能不被推荐。
来自评论之一的简单选项:将 'Z'
替换为 '+00:00'
- 并使用 Python 3.7+ 的 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)
尽管 strptime
可以将 'Z'
字符解析为 UTC,但 fromisoformat
的速度要快 ~ x40(另请参见:A faster strptime):
%timeit datetime.fromisoformat(s.replace('Z', '+00:00'))
388 ns ± 48.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit dateutil.parser.isoparse(s)
11 µs ± 1.05 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit datetime.strptime(s, '%Y-%m-%dT%H:%M:%S.%f%z')
15.8 µs ± 1.32 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit dateutil.parser.parse(s)
87.8 µs ± 8.54 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
(Windows 10 上的 Python 3.9.12 x64)
fromisoformat
将 +00:00
而不是 Z
解析为 aware datetime,其中 tzinfo 为 UTC。如果您的输入例如以 Z+00:00
结尾,您可以在将其输入 fromisoformat
之前删除 Z
。其他 UTC 偏移量(例如 +05:30
)将被解析为静态 UTC 偏移量(不是实际时区)。
import re
import datetime
s = "2008-09-03T20:56:35.450686Z"
d = datetime.datetime(*map(int, re.split(r'[^\d]', s)[:-1]))
datetime.datetime(*map(int, re.findall('\d+', s))
目前,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())
只需使用 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())
455051100
(在 epochconverter.com 处检查),,除非我遗漏了什么?
我发现 ciso8601 是解析 ISO 8601 时间戳的最快方法。顾名思义,它是用 C 实现的。
import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
GitHub Repo README 显示了与其他答案中列出的所有其他库相比,它们的速度提高了 10 倍以上。
我的个人项目涉及大量的 ISO 8601 解析。能够切换通话并以 10 倍的速度运行真是太好了。 :)
编辑:我已经成为 ciso8601 的维护者。现在比以往任何时候都快!
datetime.strptime()
是下一个最快的解决方案。感谢您将所有这些信息放在一起!
datetime.strptime()
不是完整的 ISO 8601 解析库。如果您使用的是 Python 3.7,则可以使用更灵活的 datetime.fromisoformat()
方法。您可能是 interested in this more complete list of parsers,应该很快将其合并到 ciso8601 README 中。
如果不想使用 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)
strptime
的格式字符串中。这是一个坏主意,因为它将无法解析具有不同 UTC 偏移量的任何日期时间并引发异常。请参阅 my answer,其中描述了使用 strptime 解析 RFC 3339 实际上是不可能的。
toISOString
方法生成的,则可能是这种情况。但是在这个答案中没有提到对祖鲁时间日期的限制,问题也没有表明这就是所需要的,并且仅使用 dateutil
通常同样方便,并且可以解析的范围更小。
如果您使用 Django,它会提供 dateparse module,它接受一堆类似于 ISO 格式的格式,包括时区。
如果您不使用 Django 并且不想使用此处提到的其他库之一,您可以将 the Django source code for dateparse 调整为您的项目。
DateTimeField
会使用它。
我为 ISO 8601 标准编写了一个解析器并将其放在 GitHub 上:https://github.com/boxed/iso8601。此实现支持规范中的所有内容,但持续时间、间隔、周期性间隔和 Python 的 datetime 模块支持的日期范围之外的日期除外。
包括测试! :P
这适用于 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)
strptime
的格式字符串中。这是一个坏主意,因为它将无法解析具有不同 UTC 偏移量的任何日期时间并引发异常。请参阅 my answer,其中描述了使用 strptime 解析 RFC 3339 实际上是不可能的。
%Z
作为时区。
在所有受支持的 Python 版本中将类似 ISO 8601 的日期字符串转换为 UNIX 时间戳或 datetime.datetime
对象的一种直接方法是使用 date parser of SQLite。
#!/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
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')
另一种方法是使用 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
documentation for the standard Python function datetime.fromisoformat 中也提到了这个函数:
第三方包 dateutil 中提供了功能更全的 ISO 8601 解析器 dateutil.parser.isoparse。
如果仍然使用 pandas
,我可以推荐 pandas
中的 Timestamp
。在那里你可以
ts_1 = pd.Timestamp('2020-02-18T04:27:58.000Z')
ts_2 = pd.Timestamp('2020-02-18T04:27:58.000')
Rant:令人难以置信的是,我们仍然需要担心 2021 年的日期字符串解析之类的事情。
datetime.fromisoformat('2021-01-01T00:00:00+01:00').tzinfo.utc
和 pandas.Timestamp('2021-01-01T00:00:00+01:00').tzinfo.utc
:根本不一样。
因为 ISO 8601 允许存在许多可选的冒号和破折号,基本上是 CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
。如果要使用 strptime,则需要先去除这些变化。
目标是生成一个 utc 日期时间对象。
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-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" )
%z
ValueError:“z”是格式“%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
对于适用于 2.X 标准库的内容,请尝试:
calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))
calendar.timegm 是 time.mktime 的缺失 gm 版本。
如果解析无效的日期字符串,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
现在有 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>)
感谢伟大的Mark Amery's answer,我设计了一个函数来解释所有可能的日期时间 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))
最初我尝试过:
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)
frozenset(('+', '-'))
?像 ('+', '-')
这样的普通元组不应该能够完成同样的事情吗?
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
进行解析。
python-dateutil
而不是dateutil
安装的,所以:pip install python-dateutil
。dateutil.parser
是故意的:它试图猜测格式并在模棱两可的情况下做出不可避免的假设(只能手动定制)。因此,仅当您需要解析未知格式的输入并且可以容忍偶尔的误读时才使用它。