Python 3 datetime.fromtimestamp失败1微秒

时间:2014-07-02 14:56:52

标签: python datetime python-3.x unix-timestamp python-3.4

我想以微秒分辨率保存日期时间作为时间戳。但似乎Python 3 datetime模块在加载它们时会丢失一微秒。为了测试这个,我们创建一个脚本:

test_datetime.py

from random import randint
from datetime import datetime

now = datetime.now()

for n in range(1000):
    d = datetime(year=now.year, month=now.month, day=now.day,
            hour=now.hour, minute=now.minute, second=now.second,
            microsecond=randint(0,999999))

    ts = d.timestamp()
    d2 = datetime.fromtimestamp(ts)

    assert d == d2, 'failed in pass {}: {} != {}'.format(n, d, d2)

python3 test_datetime.py 总是失败1微秒:

Traceback (most recent call last):
  File "test_datetime.py", line 14, in <module>
    assert d == d2, 'failed in pass {}: {} != {}'.format(n, d, d2)
AssertionError: failed in pass 4: 2014-07-02 11:51:46.984716 != 2014-07-02 11:51:46.984715

这种行为是否被接受?如果我们想要微秒分辨率,我们不应该依赖datetime.fromtimestamp吗?

2 个答案:

答案 0 :(得分:4)

时间戳值是浮点值。浮点值是近似值,因此,应用舍入误差。

例如,1404313854.442585的浮点值不准确。它确实是:

>>> dt = datetime(2014, 7, 2, 16, 10, 54, 442585)
>>> dt.timestamp()
1404313854.442585
>>> format(dt.timestamp(), '.20f')
'1404313854.44258499145507812500'

这非常接近442585,但不完全。它正好低于 442585,所以当你小数部分时,乘以1百万,然后只取整数部分0.991455078125余数被忽略,你最终与442584。

因此,当您将浮点值转换回datetime对象时,1微秒的舍入错误是正常的

如果您需要精确度,请不要依赖float;或许将微秒值存储为单独的整数,然后使用dt.fromtimestamp(seconds).replace(microsecond=microseconds)

在此上下文中,您可能会发现rejection noticePEP-410 (Use decimal.Decimal type for timestamps)具有启发性。 PEP触及精度问题,时间戳表示为浮点数。

答案 1 :(得分:0)

时间戳是POSIX时间,基本上概念化为自任意&#34; epoch&#34;以来的整数秒数。 datetime.fromtimestamp()返回&#34;与POSIX时间戳对应的本地日期和时间,例如time.time()&#34;其documentation告诉我们&#34;将自纪元以来的秒数作为浮点数返回[s]。请注意,即使时间总是作为浮点数返回,但并非所有系统都提供的精度高于1秒。&#34;

当中间数据类型实际上不保证亚秒级精度时,期望通过转换到时间戳和从时间戳返回来保留六位小数的精度似乎有点不合理。浮点数无法准确表示所有十进制值。

编辑:以下代码测试程序运行时哪个微秒值对于任意日期时间无效。

from datetime import datetime
baset = datetime.now()

dodgy = []
for i in range(1000000):
    d = baset.replace(microsecond=i)
    ts = d.timestamp()
    if d != datetime.fromtimestamp(ts):
        dodgy.append(i)
print(len(dodgy))

我得到了499,968&#34;狡猾&#34;时间,但我还没有检查过它们。