Python 3 datetime.fromtimestamp误差一微秒

5 投票
2 回答
2259 浏览
提问于 2025-04-18 11:53

我想把带有微秒精度的日期时间保存为时间戳。但是似乎在加载这些时间时,Python 3 的日期时间模块会丢失一个微秒。为了测试这个问题,我们来写一个脚本:

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 总是会少一个微秒:

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

时间戳是一个POSIX时间,简单来说,就是从一个任意的“起始时间”开始,经过的秒数,用一个整数来表示。datetime.fromtimestamp()这个函数可以把这个时间戳转换成对应的本地日期和时间,就像time.time()返回的那样。根据它的文档,这个函数“返回自起始时间以来的秒数,结果是一个浮点数。需要注意的是,虽然时间总是以浮点数的形式返回,但并不是所有系统都能提供比1秒更精确的时间。”

如果你期待在转换成时间戳后再转换回来时,能保留六位小数的精度,这就有点不现实了,因为中间的数据类型并不能保证小于一秒的精确度。而且,浮点数并不能准确表示所有的小数值。

编辑:下面的代码用来测试在程序运行时,哪些微秒值对于一个任意的日期时间来说是无效的。

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个“有问题”的时间,但我还没有仔细检查它们。

4

时间戳的值其实是浮点数。浮点数是近似值,所以会有四舍五入的误差。

比如说,一个浮点数值1404313854.442585并不是特别精确。实际上,它是:

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

这个值非常接近442585,但并不完全相等。它稍微低于442585,所以当你只取小数部分,乘以100万,然后再取整数部分时,剩下的0.991455078125就被忽略了,最后得到的结果是442584。

因此,当你把浮点数值转换回datetime对象时,1微秒的四舍五入误差是很正常的

如果你需要更精确的值,最好不要依赖float;可以考虑把微秒的值单独存成一个整数,然后使用dt.fromtimestamp(seconds).replace(microsecond=microseconds)来处理。

在这个上下文中,你可能会觉得拒绝通知PEP-410(使用decimal.Decimal类型表示时间戳很有启发性。这个PEP提到了用浮点数表示时间戳时的精度问题。

撰写回答