我需要将RFC 3339字符串(如“2008-09-03T20:56:55.450686Z”)解析为Python的datetime类型。
我在Python标准库中找到了strptime,但它不是很方便。
最好的方法是什么?
我需要将RFC 3339字符串(如“2008-09-03T20:56:55.450686Z”)解析为Python的datetime类型。
我在Python标准库中找到了strptime,但它不是很方便。
最好的方法是什么?
当前回答
Python>=3.11
fromsoformat现在直接解析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”-并使用fromsoformat:
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可以将“z”字符解析为UTC,但fromsoformat的速度要快~x40(另请参阅:更快的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)
其他回答
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进行解析。
最初我尝试了:
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)
python dateutil中的等参函数
python dateutil包具有dateutil.parser.isose,不仅可以解析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。与isose相比,它可能不那么严格,但这两个函数都非常宽容,都会尝试解释传入的字符串。如果要消除任何误读的可能性,需要使用比这两个函式更严格的函数。
与Python 3.7+内置datetime.datetime.fromisoformat的比较
dateutil.parser.isorse是一个完整的ISO-8601格式解析器,但在Python≤3.10中,fromsoformat故意不是。在Python 3.11中,fromsoformat支持有效ISO 8601中的几乎所有字符串。请参阅isoformat的文档以了解此警告。(参见此答案)。
自Python 3.7以来,datetime标准库有一个用于反转datetime.isoformat()的函数。
classmethod datetime.fromisoformat(date_string):以任何有效的ISO 8601格式返回与date_string对应的日期时间,但以下情况除外:时区偏移可能有小数秒。T分隔符可以由任何单个unicode字符替换。当前不支持顺序日期。不支持分数小时和分钟。示例:>>>从datetime导入datetime>>>日期时间。来自同一格式(“2011-11-04”)datetime.datetime(2011,11,4,0,0)>>>datetime.fromisoformat('20111104')datetime.datetime(2011,11,4,0,0)>>>日期时间。来自同一格式('2011-11-04T00:05:23')datetime.datetime(2011,11,4,0,5,23)>>>日期时间。来自同一格式('2011-11-04T00:05:23Z')datetime.datetime(2011,11,4,0,5,23,tzinfo=datetime.timezone.utc)>>>日期时间。来自同一格式('20111104T000523')datetime.datetime(2011,11,4,0,5,23)>>>datetime.fromisoformat('2011-W01-2T0:05:32.283')datetime.datetime(2011,1,4,0,5,23,283000)>>>日期时间。来自同一格式('2011-11-04 00:05:23.283')datetime.datetime(2011,11,4,0,5,23,283000)>>>日期时间。来自同一格式('2011-11-04 00:05:23.283+00:00')datetime.datetime(2011,11,4,0,5,23,283000,tzinfo=datetime.timezone.utc)>>>日期时间。来自同一格式('2011-11-04T00:05:23+04:00')datetime.datetime(2011,11,4,0,5,23,tzinfo=datetime.timezone(datetime.time增量(秒=1440)))3.7版新增。3.11版本中更改:以前,此方法只支持date.isoformat()或datetime.isoformat()发出的格式。
如果您还没有升级到Python 3.11,请务必阅读文档中的警告!
要获得与2.X标准库兼容的功能,请尝试:
calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))
calendar.timegm是time.mktime缺少的gm版本。