通过电子邮件解析带有时区的日期?

gru*_*czy 31 python format timezone datetime rfc5322

我正在尝试从电子邮件中检索日期.起初它很容易:

message = email.parser.Parser().parse(file)
date = message['Date']
print date
Run Code Online (Sandbox Code Playgroud)

我收到:

'Mon, 16 Nov 2009 13:32:02 +0100'
Run Code Online (Sandbox Code Playgroud)

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

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

提升了ValueError, since %Z isn't format for +0100.但我在文档中找不到适当的时区格式,只有这个%Z区域.有人可以帮助我吗?

gue*_*tli 33

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


Ben*_*mes 30

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

但是请注意,该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
Run Code Online (Sandbox Code Playgroud)

因此,您仍然需要解析时区并考虑当地时差:

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

  • 在较新版本的 python 中,您还可以使用“email.utils.parsedate_to_datetime” (4认同)
  • `mktime + timezone`可能会为过去的日期生成错误的值,或者如果时区有DST转换:`time.timezone!= time.altzone`.使用`tt = parsedate_tz(date_str); timestamp = calendar.timegm(tt) - tt [9]`而不是. (3认同)

jfs*_*jfs 7

在Python 3.3+中,email消息可以为您解析标题:

import email
import email.policy

headers = email.message_from_file(file, policy=email.policy.default)
print(headers.get('date').datetime)
# -> 2009-11-16 13:32:02+01:00
Run Code Online (Sandbox Code Playgroud)

从Python 3.2+开始,如果你替换%Z%z:

>>> from datetime import datetime
>>> datetime.strptime("Mon, 16 Nov 2009 13:32:02 +0100", 
...                   "%a, %d %b %Y %H:%M:%S %z")
datetime.datetime(2009, 11, 16, 13, 32, 2,
                  tzinfo=datetime.timezone(datetime.timedelta(0, 3600)))
Run Code Online (Sandbox Code Playgroud)

或者使用email包(Python 3.3+):

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

如果指定了UTC偏移量,-0000那么它将返回一个天真的日期时间对象,该对象表示UTC时间,否则它将返回一个具有相应tzinfo集合的知晓日期时间对象.

要解析早期Python版本(2.6+)上的rfc 5322日期时间字符串:

from calendar import timegm
from datetime import datetime, timedelta, tzinfo
from email.utils import parsedate_tz

ZERO = timedelta(0)
time_string = 'Mon, 16 Nov 2009 13:32:02 +0100'
tt = parsedate_tz(time_string)
#NOTE: mktime_tz is broken on Python < 2.7.4,
#  see https://bugs.python.org/issue21267
timestamp = timegm(tt) - tt[9] # local time - utc offset == utc time
naive_utc_dt = datetime(1970, 1, 1) + timedelta(seconds=timestamp)
aware_utc_dt = naive_utc_dt.replace(tzinfo=FixedOffset(ZERO, 'UTC'))
aware_dt = aware_utc_dt.astimezone(FixedOffset(timedelta(seconds=tt[9])))
print(aware_utc_dt)
print(aware_dt)
# -> 2009-11-16 12:32:02+00:00
# -> 2009-11-16 13:32:02+01:00
Run Code Online (Sandbox Code Playgroud)

where FixedOffset基于文档中的tzinfo子类datetime:

class FixedOffset(tzinfo):
    """Fixed UTC offset: `time = utc_time + utc_offset`."""
    def __init__(self, offset, name=None):
        self.__offset = offset
        if name is None:
            seconds = abs(offset).seconds
            assert abs(offset).days == 0
            hours, seconds = divmod(seconds, 3600)
            if offset < ZERO:
                hours = -hours
            minutes, seconds = divmod(seconds, 60)
            assert seconds == 0
            #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>GMT%+d' % (hours, minutes, -hours)
        else:
            self.__name = name
    def utcoffset(self, dt=None):
        return self.__offset
    def tzname(self, dt=None):
        return self.__name
    def dst(self, dt=None):
        return ZERO
    def __repr__(self):
        return 'FixedOffset(%r, %r)' % (self.utcoffset(), self.tzname())
Run Code Online (Sandbox Code Playgroud)


rfe*_*rov 5

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

官方文件:

format_datetime()的倒数。执行与parsedate()相同的功能,但成功则返回日期时间。如果输入日期的时区为-0000,则datetime将是一个简单的日期时间;如果日期符合RFC,则它将以UTC表示一个时间,但没有指示消息的实际来源时区。从。如果输入日期具有任何其他有效时区偏移量,则datetime将是具有相应时区tzinfo的已知日期时间。版本3.3中的新功能。