【问题标题】:Python time library: how do I preserve dst with strptime and strftimePython 时间库:如何使用 strptime 和 strftime 保存 dst
【发布时间】: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。

【问题讨论】:

    标签: python time timezone utc dst


    【解决方案1】:

    下面,首先是对问题的解释,然后是两种可能的解决方案,一种使用time,另一种使用datetime

    问题说明

    问题在于 OP 在问题中所做的观察:tm_isdst=-1tm_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 的解决方案

    另一种解决方案涉及使用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()
    

    有几个方面需要注意:

    • 调用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 对象之间的最小可能差异。这会产生取决于分辨率的差异。

    【讨论】:

    • 感谢您的详细解释。现在这一切都说得通了,我开始认为使用日期时间比时间更好/更统一。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-17
    • 2013-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多