如何在使用strptime()解析日期/时间字符串时保留时区信息?

263

我有一个来自黑莓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()与时区一起使用?

1
你能不能...将所有日期转换为GMT? - Robus
2
@Robus:嗯,我本来希望能够这样做 - 但我假设strftime/datetime可以以某种方式实现?无论如何,我需要存储/解析日期时间在EST时区或任何它们所在的时区的事实。脚本需要能够解析带有时区信息的通用日期时间(例如ETC可能是任何其他时区)。 - victorhooi
4
EST也是美国时区的缩写。类似地,BST既是英国时区的缩写,也是巴西时区的缩写。这些缩写本质上是有歧义的。建议使用相对于协调世界时(UTC/GMT)的偏移量来表示时区。如果需要支持缩写,则需要使映射与语言环境相关,并且会变得非常复杂。 - Donal Fellows
5个回答

445

我建议使用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() 的格式烦恼...只需要把日期传给它,它就会做正确的事情。


1
鉴于很多人倾向于使用python-dateutil,我想指出该库的一个限制。>>> parser.parse("Thu, 25 Sep 2003 10:49:41,123 -0300") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/wanghq/awscli/lib/python2.7/site-packages/dateutil/parser.py", line 748, in parse return DEFAULTPARSER.parse(timestr, **kwargs) File "/Users/wanghq/awscli/lib/python2.7/site-packages/dateutil/parser.py", line 310, in parse res, skipped_tokens = self._parse(timestr, **kwargs) TypeError: 'NoneType' object is not iterable - wanghq
2
@wanghq,您需要将最后一个逗号替换为句号。然后parser.parse("Thu, 25 Sep 2003 10:49:41.123 -0300")返回: datetime.datetime(2003, 9, 25, 10, 49, 41, 123000, tzinfo=tzoffset(None, -10800)) - flyingfoxlee
11
@flyingfoxlee,是的,我明白。我只想告诉人们python-dateutil的限制。它可以做很多神奇的事情,但有时也会出现错误。因此,“只需将日期传递给它,它就会自动正确处理”并不完全正确。 - wanghq
6
dateutil.parser.parse("10-27-2016 09:06 AM PDT") 返回的是 datetime.datetime(2016, 10, 27, 9, 6),但它未能确定时区。 - HaPsantran
4
这取决于一个人的目标。 dateutil parser 可能很容易使用,但是 strptime() 更快。此外,它的格式非常容易学习。 - rapture
显示剩余5条评论

103

strptime返回一个具有tzinfo属性的日期时间对象,因此我们可以将其替换为所需的时区。

>>> 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'

4
并非所有的时间戳字符串都基于UTC(例如问题中的时间戳字符串)。 - Mew
2
这种方法对于许多时区都无法正确工作。例如:对于 Asia/Kolkata 进行此操作会得到一个偏移量为 tzinfo=<DstTzInfo 'Asia/Kolkata' LMT+5:53:00 STD - Irfanuddin
7
你描述的是一个本地化错误的 pytz 时区对象所导致的结果。使用 pytz 库时,必须进行本地化,不要使用 replace 方法!然而,在 Python 3.9 中,应该使用 zoneinfo 库来避免这个问题。在那里使用 replace 方法是安全的。 - FObersteiner

91

datetime模块文档中写道:

根据给定的格式解析date_string并返回相应的datetime对象。可以使用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开始,这一点已经更新了,相同的文档现在还声明如下:

当strptime()方法提供%z指示符时,将生成一个带时区信息的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

19
相关的 Python 错误:%Z 在 strptime 中不能匹配 EST 和其他时区 - jfs
文档实际上在那一部分之前说:“如果格式不包含微秒或时区信息,则等同于:”。 - undefined

12

您的时间字符串与RFC 2822中的时间格式(电子邮件和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 objects)的解决方案: 从电子邮件中解析带有时区的日期.

在这种格式中,EST-0500在语义上是等价的。但总的来说,仅有时区缩写并不能唯一确定一个时区。


0

遇到了这个确切的问题。

我最终做了什么:

# 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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接