我需要以可读的格式存储时间戳,然后为了进行比较,我需要将其转换为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秒的增量,而是3601(1小时1秒),始终如一。
进一步研究,当我只执行time.gmtime()时,结构的tm_isdst=0,而从时间戳p1字符串转换的结构t1的tm_isdst=-1。
如何确保isdst保留为0。我想这可能就是问题所在。
或者有没有更好的方法以人类可读的格式(UTC)记录时间,并且能够正确地转换回epoch进行时间差异计算?
更新:昨晚做了更多的研究后,我改用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
我选择不添加tz以保持它的简单/简短;我仍然可以那样做。
下面,首先是对问题的解释,然后是两种可能的解决方案,一种使用time
,另一种使用datetime
。
问题解释
问题在于OP在问题中所做的观察:tm_isdst=-1
。tm_isdst
是一个标志,用于确定夏令时是否有效(有关更多详细信息,请参阅https://docs.python.org/2/library/time.html#time.struct_time)。具体地,给定来自OP的时间的字符串的格式(符合RFC 2822互联网电子邮件标准),[time.strptime
]4不存储关于时区的信息,即+0000
。因此,当根据字符串中的信息tm_isdst=-1
再次创建struct_time
时,即未知。在进行计算时,如何填写这些信息的猜测是基于本地系统的。例如,就好像该系统指的是夏令时生效的北美一样,设置了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
解决方案
另一个解决方案涉及使用直接支持时区的datetime
和dateutil
包,代码可以是(假设保留时区信息是一项要求):
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()
有一些方面需要注意:
在调用
utcnow
之后,不设置时区,因为它是一个原始的UTC日期时间。如果不需要有关UTC的信息,可以删除为两次设置时区的两行,结果将是相同的,因为没有关于DST的猜测。由于
%z
未正确解析,因此无法使用datetime.strptime
。如果字符串包含有关时区的信息,则应使用parser
。可以直接执行
datetime
的两个实例的差异,并将得到的增量转换为秒。如果有必要获得自epoch以来的时间(以秒为单位),则应进行显式计算,因为在
datetime
中(在回答时)没有直接函数自动进行计算。代码下方,例如utc_time2
:epoch_time = datetime(1970,1,1) epoch2 = (utc_time2 - epoch_time).total_seconds()
datetime.resolution
,即两个不相等的datetime
对象之间的最小可能差。这会导致一个达到分辨率的差异。