评估时间戳和total_seconds之间的区别

时间:2017-08-10 00:29:06

标签: python python-3.x datetime

当我在python的datetime中使用两种不同的方法(使用timestamp()或total_seconds())来评估两个日期之间的秒数时,我会得到不同的结果。为什么是这样?或者我做错了什么?这是我的意思的一个例子。

t1=dt.datetime(1970,6,12,0,0,0)
t2=dt.datetime(1970,1,1,0,0,0)

print(t1.timestamp()-t2.timestamp())
print((t1-t2).total_seconds())

我得到的答案是: 13993200.0 13996800.0

3 个答案:

答案 0 :(得分:4)

差异是由夏令时引起的。如果您的某个日期属于您的时区的DST范围,而另一个日期不属于您的日期,那么您的计算结果会出现一小时误差。

从1966年到1973年,DST in the United States ran from the last Sunday in April to the last Sunday in October,解释了@JoshuaRLi's findings

看起来,当减去两个日期时,它没有注意DST差异; t1 - t2生成datetime.timedelta(162),差异为162天,即使从技术上讲,小时数差异为162 * 24 - 1小时(DST跳过的-1个占比)。 timestamp正在处理此问题(两个时间戳都与UTC相关,因此DST时间戳正确显示为提前一小时,因为跳过了一个小时来生成它)。

答案 1 :(得分:2)

夏令时

两个dt.datetime对象的减法魔术方法会创建一个与夏令时无关的dt.timedelta

纪元时间戳转换功能考虑了夏令时,这解释了3600秒(1小时)的差异。

请参阅下面的我的侦探帖子。这很有趣!

掀起一个快速的剧本,因为这对我来说似乎很有趣。

这是在3.5.4和3.6.2上以相同的输出运行。

import datetime as dt

t1 = dt.datetime(1970,1,1,0,0,0)
t2 = dt.datetime(1970,1,1,0,0,0)

for _ in range(365):
    try:
        d1 = t1.timestamp() - t2.timestamp()
        d2 = (t1-t2).total_seconds()
        assert d1 == d2
    except AssertionError as e:
        print(t1, d2-d1)
    t1 += dt.timedelta(days=1)

我得到了这个输出。看起来它从4/27开始,差异始终是一小时,这意味着跳转只发生一次(实际上没关系,继续阅读)

1970-04-27 00:00:00 3600.0
1970-04-28 00:00:00 3600.0
1970-04-29 00:00:00 3600.0
...

我写了第二个脚本:

import datetime as dt

t = dt.datetime(1970,1,1,0,0,0)
sid = 60*60*24

while 1:
    prev = t
    t += dt.timedelta(days=1)
    diff1 = (t-prev).total_seconds()
    diff2 = t.timestamp() - prev.timestamp()
    try:
        assert diff1 == diff2 == sid
    except AssertionError:
        print(diff1, diff2, t, prev)
        exit(1)

输出:

86400.0 82800.0 1970-04-27 00:00:00 1970-04-26 00:00:00

删除exit(1)后,输出会变得很有趣:

86400.0 82800.0 1970-04-27 00:00:00 1970-04-26 00:00:00
86400.0 90000.0 1970-10-26 00:00:00 1970-10-25 00:00:00
86400.0 82800.0 1971-04-26 00:00:00 1971-04-25 00:00:00
86400.0 90000.0 1971-11-01 00:00:00 1971-10-31 00:00:00
86400.0 82800.0 1972-05-01 00:00:00 1972-04-30 00:00:00
86400.0 90000.0 1972-10-30 00:00:00 1972-10-29 00:00:00
86400.0 82800.0 1973-04-30 00:00:00 1973-04-29 00:00:00
86400.0 90000.0 1973-10-29 00:00:00 1973-10-28 00:00:00
86400.0 82800.0 1974-01-07 00:00:00 1974-01-06 00:00:00
86400.0 90000.0 1974-10-28 00:00:00 1974-10-27 00:00:00
86400.0 82800.0 1975-02-24 00:00:00 1975-02-23 00:00:00
86400.0 90000.0 1975-10-27 00:00:00 1975-10-26 00:00:00
86400.0 82800.0 1976-04-26 00:00:00 1976-04-25 00:00:00
86400.0 90000.0 1976-11-01 00:00:00 1976-10-31 00:00:00
...

看起来epoch时间戳转换t.timestamp() - prev.timestamp()不可靠。更重要的是,它看起来像是一个有点不规则但间隔开的日期间隔从零到一个小时的振荡(编辑:意识到这些是历史夏令时)。如果你保持脚本运行,振荡将一直持续到我们到达结束时间为止:

86400.0 82800.0 9997-03-10 00:00:00 9997-03-09 00:00:00
86400.0 90000.0 9997-11-03 00:00:00 9997-11-02 00:00:00
86400.0 82800.0 9998-03-09 00:00:00 9998-03-08 00:00:00
86400.0 90000.0 9998-11-02 00:00:00 9998-11-01 00:00:00
86400.0 82800.0 9999-03-15 00:00:00 9999-03-14 00:00:00
86400.0 90000.0 9999-11-08 00:00:00 9999-11-07 00:00:00
Traceback (most recent call last):
  File "check.py", line 8, in <module>
    t += dt.timedelta(days=1)
OverflowError: date value out of range

这种行为促使我仔细研究了我的第一个脚本的输出:

...
1970-10-24 00:00:00 3600.0
1970-10-25 00:00:00 3600.0
1971-04-26 00:00:00 3600.0
1971-04-27 00:00:00 3600.0
...

哇,所以AssertionErrors1970-10-25之间没有1971-04-26非包容性。这与第二个脚本中发现的振荡相匹配。

这真的很奇怪......

等一下...... 白天节省时间

答案 2 :(得分:0)

.timestamp仅适用于Python 3(3.3版中的新功能)。 Python 2中没有这样的方法。

  

版本3.6中更改:timestamp()方法使用fold属性消除重复间隔期间的时间歧义。

     

注意:没有方法直接从表示UTC时间的天真日期时间实例获取POSIX时间戳。如果您的应用程序使用此约定并且您的系统时区未设置为UTC,则可以通过提供tzinfo = timezone.utc来获取POSIX时间戳:   timestamp = dt.replace(tzinfo=timezone.utc).timestamp()   或直接计算时间戳:   timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)

相关问题