从邮件中解析带时区的日期?

42 投票
8 回答
37784 浏览
提问于 2025-04-15 16:20

我正在尝试从一封邮件中提取日期。一开始这很简单:

message = email.parser.Parser().parse(file)
date = message['Date']
print date

然后我得到了:

'Mon, 16 Nov 2009 13:32:02 +0100'

但是我需要一个好用的日期时间对象,所以我使用:

datetime.strptime('Mon, 16 Nov 2009 13:32:02 +0100', '%a, %d %b %Y %H:%M:%S %Z')

这时出现了一个错误,提示 ValueError,因为 %Z 不是 +0100 的格式。但我在文档中找不到适合时区的正确格式,里面只有这个 %Z 用于表示时区。有人能帮我解决这个问题吗?

8 个回答

17

对于 Python 3.3 及以上版本,你可以使用 parsedate_to_datetime 这个函数:

>>> from email.utils import parsedate_to_datetime
>>> parsedate_to_datetime('Mon, 16 Nov 2009 13:32:02 +0100')
...
datetime.datetime(2009, 11, 16, 13, 32, 2, tzinfo=datetime.timezone(datetime.timedelta(0, 3600)))

官方文档说明:

这个函数是 format_datetime() 的反向操作。它的功能和 parsedate() 一样,但如果成功的话,会返回一个日期时间对象。如果输入的日期有时区 -0000,那么返回的日期时间对象就是一个“无时区”的日期时间;如果日期符合相关标准,它会表示一个 UTC 时间,但不会显示消息来源的实际时区。如果输入的日期有其他有效的时区偏移量,那么返回的日期时间对象就是一个“有时区”的日期时间,并且会包含相应的时区信息。这个功能在 3.3 版本中新增。

38

使用 email.utils.parsedate_tz(date) 这个函数:

msg=email.message_from_file(open(file_name))
date=None
date_str=msg.get('date')
if date_str:
    date_tuple=email.utils.parsedate_tz(date_str)
    if date_tuple:
        date=datetime.datetime.fromtimestamp(email.utils.mktime_tz(date_tuple))
if date:
    ... # valid date found
43

email.utils 里有一个叫 parsedate() 的函数,它是用来处理 RFC 2822 格式的,听说这个函数现在还在用,没有被淘汰。

>>> import email.utils
>>> import time
>>> import datetime
>>> email.utils.parsedate('Mon, 16 Nov 2009 13:32:02 +0100')
(2009, 11, 16, 13, 32, 2, 0, 1, -1)
>>> time.mktime((2009, 11, 16, 13, 32, 2, 0, 1, -1))
1258378322.0
>>> datetime.datetime.fromtimestamp(1258378322.0)
datetime.datetime(2009, 11, 16, 13, 32, 2)

不过要注意的是,parsedate 这个方法不考虑时区的问题,而 time.mktime 总是需要一个本地时间的元组。

>>> (time.mktime(email.utils.parsedate('Mon, 16 Nov 2009 13:32:02 +0900')) ==
... time.mktime(email.utils.parsedate('Mon, 16 Nov 2009 13:32:02 +0100'))
True

所以你还需要自己处理时区的问题,并考虑本地时间的差异:

>>> REMOTE_TIME_ZONE_OFFSET = +9 * 60 * 60
>>> (time.mktime(email.utils.parsedate('Mon, 16 Nov 2009 13:32:02 +0900')) +
... time.timezone - REMOTE_TIME_ZONE_OFFSET)
1258410122.0

撰写回答