如何保存时区解析日期/时间字符串与strptime()?

我有一个黑莓IPD备份的CSV转储文件,使用IPDDump创建。 这里的日期/时间字符串看起来像这样 (其中EST是澳大利亚时区):

Tue Jun 22 07:46:22 EST 2010

我需要能够在Python中解析这个日期。首先,我尝试使用datettime中的strptime()函数。

>>> datetime.datetime.strptime('Tue Jun 22 12:10:20 2010 EST', '%a %b %d %H:%M:%S %Y %Z')

然而,由于某种原因,返回的datetime对象似乎没有任何与它相关的tzinfo

我确实在这个页面上读到,显然datetime.strptime无声地丢弃了tzinfo,但是,我检查了文档,我找不到任何关于在这里的文档。

有没有办法让strptime()很好地使用时区?

288264 次浏览

datetime模块文档说:

返回一个datetime,对应于date_string,根据format解析。这相当于datetime(*(time.strptime(date_string, format)[0:6]))

看到[0:6]了吗?这就得到(year, month, day, hour, minute, second)。什么都没有。没有提到时区。

有趣的是,[Win XP SP2, Python 2.6, 2.7]将你的例子传递给time.strptime不工作,但如果你去掉“%Z”和“EST”,它就可以工作。同样,使用“UTC”或“GMT”来代替“EST”也可以。“PST”和“MEZ”都不行。令人费解。

值得注意的是,这已经更新到3.2版,相同的文档现在还声明如下:

当%z指令提供给strptime()方法时,将生成一个感知datetime对象。结果的tzinfo将被设置为一个时区实例。

注意,这对%Z不起作用,所以大小写很重要。示例如下:

In [1]: from datetime import datetime


In [2]: start_time = datetime.strptime('2018-04-18-17-04-30-AEST','%Y-%m-%d-%H-%M-%S-%Z')


In [3]: print("TZ NAME: {tz}".format(tz=start_time.tzname()))
TZ NAME: None


In [4]: start_time = datetime.strptime('2018-04-18-17-04-30-+1000','%Y-%m-%d-%H-%M-%S-%z')


In [5]: print("TZ NAME: {tz}".format(tz=start_time.tzname()))
TZ NAME: UTC+10:00

我建议使用python-dateutil。它的解析器能够解析到目前为止我抛出的所有日期格式。

>>> from dateutil import parser
>>> parser.parse("Tue Jun 22 07:46:22 EST 2010")
datetime.datetime(2010, 6, 22, 7, 46, 22, tzinfo=tzlocal())
>>> parser.parse("Fri, 11 Nov 2011 03:18:09 -0400")
datetime.datetime(2011, 11, 11, 3, 18, 9, tzinfo=tzoffset(None, -14400))
>>> parser.parse("Sun")
datetime.datetime(2011, 12, 18, 0, 0)
>>> parser.parse("10-11-08")
datetime.datetime(2008, 10, 11, 0, 0)

等等。没有处理strptime()格式的废话…只要给它一个日期,它就会做正确的事情。

你的时间字符串类似于rfc2822(电子邮件的日期格式,HTTP报头)中的时间格式。你可以只用stdlib来解析它:

>>> from email.utils import parsedate_tz
>>> parsedate_tz('Tue Jun 22 07:46:22 EST 2010')
(2010, 6, 22, 7, 46, 22, 0, 1, -1, -18000)

请参阅为各种Python版本生成时区感知datetime对象的解决方案:从电子邮件中解析带有时区的日期

在此格式中,__ABC0在语义上等价于-0500。尽管,一般来说,时区缩写不足以唯一地标识一个时区

就遇到了这个问题。

我最后做了什么:

# starting with date string
sdt = "20190901"
std_format = '%Y%m%d'


# create naive datetime object
from datetime import datetime
dt = datetime.strptime(sdt, sdt_format)


# extract the relevant date time items
dt_formatters = ['%Y','%m','%d']
dt_vals = tuple(map(lambda formatter: int(datetime.strftime(dt,formatter)), dt_formatters))


# set timezone
import pendulum
tz = pendulum.timezone('utc')


dt_tz = datetime(*dt_vals,tzinfo=tz)

由于strptime返回一个具有tzinfo属性的datetime对象,我们可以简单地将其替换为所需的时区。

>>> import datetime


>>> date_time_str = '2018-06-29 08:15:27.243860'
>>> date_time_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S.%f').replace(tzinfo=datetime.timezone.utc)
>>> date_time_obj.tzname()
'UTC'

作为乔·肖的回答的扩展,dateutil的解析器提供了从IANA时区名派生的时区名称缩写到时区对象的映射的可能性。

import dateutil


tzdict = {'EST': dateutil.tz.gettz('America/New_York'),
'EDT': dateutil.tz.gettz('America/New_York')}


dt = dateutil.parser.parse("Tue Jun 22 07:46:22 EST 2010", tzinfos=tzdict)


print(dt)
# 2010-06-22 07:46:22-04:00
      

print(repr(dt))
# datetime.datetime(2010, 6, 22, 7, 46, 22, tzinfo=tzfile('US/Eastern'))

相对于固定UTC偏移量的优点是,如果您对获得的datetime对象执行任何timedelta算术,则会考虑时区规则(例如DST转换)。