Python时间库:如何使用strptime和strftime保留dst?

3

我需要以可读格式存储一个时间戳,然后稍后我需要将其转换为epoch以进行比较。

我尝试过以下方式:

import time
format = '%Y %m %d %H:%M:%S +0000'
timestamp1 = time.strftime(format,time.gmtime())  # '2016 03 25 04:06:22 +0000'
t1 = time.strptime(timestamp1, format) # time.struct_time(..., tm_isdst=-1)
time.sleep(1)
epoch_now = time.mktime(time.gmtime())
epoch_t1 = time.mktime(t1)
print "Delta: %s" % (epoch_now - epoch_t1)

运行这个程序,我得到的不是1秒的Delta值,而是3601(1小时1秒),这种情况一直存在。

进一步调查发现,当我只使用time.gmtime()时,结构体中tm_isdst=0,而从timestamp1字符串转换过来的结构体t1却有tm_isdst=-1。

如何确保isdst保持为0呢?我认为这可能是问题所在。

或者,有没有更好的方法记录人类可读的时间格式(UTC),并能够正确地将其转换回时代以进行时间差计算呢?

更新: 昨晚做了更多的研究后,我改用datetime,因为它在datetime对象中保留了更多的信息,这也得到了albertoql下面的答案的确认。

现在我有以下内容:

from datetime import datetime
format = '%Y-%m-%d %H:%M:%S.%f +0000' # +0000 is optional; only for user to see it's UTC
d1 = datetime.utcnow()
timestamp1 = d1.strftime(format)
d1a = datetime.strptime(timestamp1, format)
time.sleep(1)
d2 = datetime.utcnow()
print "Delta: %s" % (d2 - d1a).seconds

我选择不添加时区信息以使其更简单/更短; 我仍然可以使用strptime。

1个回答

3

以下是问题的解释,以及两种可能的解决方案,一种使用time,另一种使用datetime

问题解释

问题在于OP在问题中提到的tm_isdst=-1tm_isdst是一个标志,用于确定是否启用夏令时(有关更多详细信息,请参见https://docs.python.org/2/library/time.html#time.struct_time)。特别地,由于OP提供的时间字符串格式符合RFC 2822互联网电子邮件标准,因此[time.strptime]4不会存储有关时区的信息,即+0000。因此,当根据字符串中的信息再次创建struct_time时,tm_isdst=-1,即未知。在进行计算时如何填充该信息的猜测基于本地系统。例如,如果系统指的是夏令时生效的北美地区,则设置tm_isdst

time解决方案

如果您只想使用time包,则直接解析信息的最简单方法是指定时间为UTC,因此添加%Z到格式中。请注意,time不提供一种存储有关时区信息的方式在struct_time中。因此,它不会打印与保存在变量中的时间相关联的实际时区。时区从系统中检索。因此,不可能直接使用相同的格式进行time.strftime。编写和读取字符串的代码部分如下:

format = '%Y %m %d %H:%M:%S UTC'
format2 = '%Y %m %d %H:%M:%S %Z'
timestamp1 = time.strftime(format, time.gmtime())
t1 = time.strptime(timestamp1, format2)

使用 datetime 解决方案

另一个解决方案包括使用直接支持时区的 datetimedateutil 包。以下是代码示例(假设保留时区信息是一项要求):

from datetime import datetime
from dateutil import tz, parser
import time

time_format = '%Y %m %d %H:%M:%S %z'
utc_zone = tz.gettz('UTC')

utc_time1 = datetime.utcnow()
utc_time1 = utc_time1.replace(tzinfo=utc_zone)
utc_time1_string = utc_time1.strftime(time_format)
utc_time1 = parser.parse(utc_time1_string)
time.sleep(1)
utc_time2 = datetime.utcnow()
utc_time2 = utc_time2.replace(tzinfo=utc_zone)

print "Delta: %s" % (utc_time2 - utc_time1).total_seconds()

需要注意的几个方面:

  • After the call of utcnow, the timezone is not set, as it is a naive UTC datetime. If the information about UTC is not needed, it is possible to delete both lines where the timezone is set for the two times, and the result would be the same, as there is no guess about DST.

  • It is not possible to use datetime.strptime because of %z, which is not correctly parsed. If the string contains the information about the timezone, then parser should be used.

  • It is possible to directly perform the difference from two instances of datetime and transform the resulting delta into seconds.

  • If it is necessary to get the time in seconds since the epoch, an explicit computation should be made, as there is no direct function that does that automatically in datetime (at the time of the answer). Below the code, for example for utc_time2:

     epoch_time = datetime(1970,1,1)
     epoch2 = (utc_time2 - epoch_time).total_seconds()
    
  • datetime.resolution, namely the smallest possible difference between two non-equal datetime objects. This results in a difference that is up to the resolution.


感谢您的详细解释。现在我明白了,开始认为使用datetime比time更好/更统一。 - Maelstrom
请注意,time模块中的struct_time结构体并不提供存储时区信息的方法。但是根据文档,这是不正确的:https://docs.python.org/3/library/time.html#time.struct_time - Grey Christoforo

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