如何在使用strptime()解析日期/时间字符串时保留时区信息?
如何在使用strptime()解析日期/时间字符串时保留时区信息?
我有一个来自Blackberry 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()
与时区友好地配合使用?
问题的原因是datetime模块的strptime()函数在解析日期时间字符串时,返回的datetime对象中没有包含时区信息。根据官方文档,strptime()返回的是一个包含年、月、日、时、分、秒的元组,但没有提到关于时区的信息。
有趣的是,在Windows XP SP2上使用Python 2.6和2.7时,将示例中的字符串传递给time.strptime()函数将无法正常工作,但是如果去掉字符串中的" %Z"和" EST"部分,则可以正常工作。另外,使用"UTC"或"GMT"替换"EST"也可以正常工作,但使用"PST"和"MEZ"则不行,这令人困惑。
值得注意的是,自Python版本3.2开始,官方文档进行了更新,并新增了以下说明:当strptime()方法提供了%z指令时,将生成一个带时区信息的datetime对象,结果中的tzinfo将设置为一个时区实例。
需要注意的是,这个更新不适用于%Z指令,所以大小写是有区别的。下面是一个示例:
from datetime import datetime start_time = datetime.strptime('2018-04-18-17-04-30-AEST','%Y-%m-%d-%H-%M-%S-%Z') print("TZ NAME: {tz}".format(tz=start_time.tzname())) # 输出: TZ NAME: None start_time = datetime.strptime('2018-04-18-17-04-30-+1000','%Y-%m-%d-%H-%M-%S-%z') print("TZ NAME: {tz}".format(tz=start_time.tzname())) # 输出: TZ NAME: UTC+10:00
相关的Python bug:[strptime中的%Z无法匹配EST等](http://bugs.python.org/issue22377)
问题的原因是使用strptime()解析日期/时间字符串时,无法保留时区信息。解决方法是将返回的datetime对象的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'
这种方法对于所有以UTC为基准的时间戳字符串有效。然而,并非所有的时间戳字符串都是以UTC为基准的,例如问题中的时间戳字符串。
对于许多时区,这种方法无法正确工作。例如,对于'Asia/Kolkata'时区,这样做将给出一个偏移量为'tzinfo= 你描述的情况是使用了一个错误的本地化pytz时区对象。在使用pytz时,必须进行本地化,而不是使用replace()方法。然而,在Python 3.9中,应该使用zoneinfo模块,它完全避免了这个问题。在那里,可以安全地使用replace()方法。 总结起来,要在解析日期/时间字符串时保留时区信息,可以使用strptime()方法解析字符串,然后使用replace()方法将时区信息替换为所需的时区。如果使用的是Python 3.9及以上版本,建议使用zoneinfo模块来避免出现时区问题。
如何在使用strptime()解析日期/时间字符串时保留时区?
在解析日期/时间字符串时,建议使用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()的格式问题,只需要将日期传递给库,它就能正确解析。
然而,需要注意的是,尽管很多人倾向于使用python-dateutil库,但该库也存在一些限制。例如,当解析带有逗号的日期字符串时,会出现错误。需要将最后一个逗号替换为句号,然后再进行解析。
例如,`parser.parse("Thu, 25 Sep 2003 10:49:41.123 -0300")` 返回的结果是 `datetime.datetime(2003, 9, 25, 10, 49, 41, 123000, tzinfo=tzoffset(None, -10800))`。
此外,由于python-dateutil库可能无法表示模糊的本地时间,如果您的应用程序无法容忍大约1小时的错误,建议在处理Python中的时区时使用基于pytz的解决方案。
例如,`dateutil.parser.parse("10-27-2016 09:06 AM PDT")` 的返回结果是 `datetime.datetime(2016, 10, 27, 9, 6)`,无法确定时区。
总之,python-dateutil库虽然功能强大,但并非百分之百准确。因此,不能完全依赖它的自动解析能力。对于一些特殊的日期格式,还是需要使用strptime()方法。
此外,需要注意的是,使用python-dateutil库的解析器比strptime()方法的解析速度要慢得多。
最后,需要注意的是,在使用上述代码时,可能会遇到 `NameError: name 'tzlocal' is not defined` 的错误。