Python 时间库:如何使用 strptime 和 strftime 保存 dst
Posted
技术标签:
【中文标题】Python 时间库:如何使用 strptime 和 strftime 保存 dst【英文标题】:Python time library: how do I preserve dst with strptime and strftime 【发布时间】:2016-07-12 20:28:13 【问题描述】:我需要以可读格式存储时间戳,然后我需要将其转换为纪元以进行比较。
我试过这样做:
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
我选择不添加 tz 以使其简单/更短;我仍然可以那样进行strptime。
【问题讨论】:
【参考方案1】:下面,首先是对问题的解释,然后是两种可能的解决方案,一种使用time
,另一种使用datetime
。
问题说明
问题在于 OP 在问题中所做的观察:tm_isdst=-1
。 tm_isdst
是一个标志,用于确定夏令时是否有效(有关更多详细信息,请参阅 https://docs.python.org/2/library/time.html#time.struct_time)。
具体来说,给定来自 OP 的时间字符串格式(符合RFC 2822 Internet 电子邮件标准),[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
的解决方案
另一种解决方案涉及使用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。
无法使用datetime.strptime
,因为%z
解析不正确。如果字符串包含有关时区的信息,则应使用parser
。
可以直接对datetime
的两个实例进行求差,并将得到的delta转换为秒。
如果需要从纪元开始以秒为单位获取时间,则应进行显式计算,因为在datetime
(在回答时)中没有自动执行此操作的直接函数。下面的代码,例如utc_time2
:
epoch_time = datetime(1970,1,1)
epoch2 = (utc_time2 - epoch_time).total_seconds()
datetime.resolution
,即两个不相等的datetime
对象之间的最小可能差异。这会产生取决于分辨率的差异。
【讨论】:
感谢您的详细解释。现在这一切都说得通了,我开始认为使用日期时间比时间更好/更统一。以上是关于Python 时间库:如何使用 strptime 和 strftime 保存 dst的主要内容,如果未能解决你的问题,请参考以下文章
python 怎么把atetime.datetime.strptime变成time.strftime
使用strptime进行Python解析 - 月日时间:分钟:秒年时区 - 与格式不匹配