关于解析:如何将RFC 2822日期/时间解析为Python日期时间?

How to parse a RFC 2822 date/time into a Python datetime?

我有一个RFC 2822指定格式的日期 - 比如Fri, 15 May 2009 17:58:28 +0000,作为字符串。 是否有一种快速和/或标准的方法将其作为Python 2.5中的datetime对象? 我试图生成一个strptime格式字符串,但+0000时区说明符会混淆解析器。


问题是parsedate会忽略偏移量。

改为:

1
2
from email.utils import parsedate_tz
print parsedate_tz('Fri, 15 May 2009 17:58:28 +0700')

我想详细说明以前的答案。 email.utils.parsedateemail.utils.parsedate_tz都返回元组,因为OP需要一个datetime.datetime对象,我为了完整性添加了这些示例:

1
2
3
4
5
6
from email.utils import parsedate
from datetime import datetime
import time

t = parsedate('Sun, 14 Jul 2013 20:14:30 -0000')
d1 = datetime.fromtimestamp(time.mktime(t))

要么:

1
d2 = datetime.datetime(*t[:6])

请注意,d1d2都是天真的日期时间对象,没有存储时区信息。如果需要了解日期时间对象,请检查tzinfo datetime() arg。

或者,您可以使用dateutil模块


1
2
from email.utils import parsedate
print parsedate('Fri, 15 May 2009 17:58:28 +0000')

文档。


email.util中有一个parsedate函数。
它解析所有有效的RFC 2822日期和一些特殊情况。


看起来Python 3.3继续在email.utils中有一个新方法parsedate_to_datetime来处理中间步骤:

email.utils.parsedate_to_datetime(date)

The inverse of format_datetime(). Performs the same function as parsedate(), but on
success returns a datetime. If the input date has a timezone of -0000,
the datetime will be a naive datetime, and if the date is conforming
to the RFCs it will represent a time in UTC but with no indication of
the actual source timezone of the message the date comes from. If the
input date has any other valid timezone offset, the datetime will be
an aware datetime with the corresponding a timezone tzinfo.

New in version 3.3.

http://python.readthedocs.org/en/latest/library/email.util.html#email.utils.parsedate_to_datetime


email.utils.parsedate_tz(date)是要使用的功能。以下是一些变化。

将日期/时间字符串(RFC 5322,RFC 2822,RFC 1123)发送到以浮点秒为单位的unix时间戳:

1
2
3
4
5
6
7
8
9
10
import email.utils
import calendar
def email_time_to_timestamp(s):
    tt = email.utils.parsedate_tz(s)
    if tt is None: return None
    return calendar.timegm(tt) - tt[9]

import time
print(time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(email_time_to_timestamp("Wed, 04 Jan 2017 09:55:45 -0800"))))
# 2017-01-04T17:55:45Z

确保不使用mktime(它解释计算机本地时间的time_struct,而不是UTC);改为使用timegmmktime_tz(但请注意下一段中mktime_tz的警告)。

如果你确定你有python版本2.7.4,3.2.4,3.3或更新版本,那么你可以使用email.utils.mktime_tz(tt)而不是calendar.timegm(tt) - tt[9]。在此之前,mktime_tz在本地时区的秋季夏令时转换期间调用了错误的时间(错误14653)。

感谢@ j-f-sebastian关于mktime和mktime_tz的警告。

将日期/时间字符串(RFC 5322,RFC 2822,RFC 1123)发送到python 3.3上的"aware"datetime

在python 3.3及更高版本上,使用email.utils.parsedate_to_datetime,它返回一个带有原始区域偏移的知晓datetime

1
2
3
4
5
import email.utils
email.utils.parsedate_to_datetime(s)

print(email.utils.parsedate_to_datetime("Wed, 04 Jan 2017 09:55:45 -0800").isoformat())
# 2017-01-04T09:55:45-08:00

警告:如果时间落在闰秒上,这将抛出ValueError,例如email.utils.parsedate_to_datetime("Sat, 31 Dec 2016 15:59:60 -0800")

将日期/时间字符串(RFC 5322,RFC 2822,RFC 1123)发送到UTC区域中的"aware"datetime

这只是转换为时间戳然后转换为UTC datetime

1
2
3
4
5
6
7
8
9
10
11
import email.utils
import calendar
import datetime
def email_time_to_utc_datetime(s):
    tt = email.utils.parsedate_tz(s)
    if tt is None: return None
    timestamp = calendar.timegm(tt) - tt[9]
    return datetime.datetime.utcfromtimestamp(timestamp)

print(email_time_to_utc_datetime("Wed, 04 Jan 2017 09:55:45 -0800").isoformat())
# 2017-01-04T17:55:45

将日期/时间字符串(RFC 5322,RFC 2822,RFC 1123)发送到带有原始偏移量的python"aware"datetime

在python 3.2之前,python没有附带tzinfo实现,所以这里有一个使用dateutil.tz.tzoffset(pip install dateutil)的例子:

1
2
3
4
5
6
7
8
9
10
11
import email.utils
import datetime
import dateutil.tz
def email_time_to_datetime(s):
    tt = email.utils.parsedate_tz(s)
    if tt is None: return None
    tz = dateutil.tz.tzoffset("UTC%+02d%02d"%(tt[9]//60//60, tt[9]//60%60), tt[9])
    return datetime.datetime(*tt[:5]+(min(tt[5], 59),), tzinfo=tz)

print(email_time_to_datetime("Wed, 04 Jan 2017 09:55:45 -0800").isoformat())
# 2017-01-04T09:55:45-08:00

如果您使用的是python 3.2,则可以使用内置的tzinfo实现datetime.timezonetz = datetime.timezone(datetime.timedelta(seconds=tt[9]))而不是第三方dateutil.tz.tzoffset

再次感谢@ j-f-sebastian关于钳制闰秒的说明。