Python 3 datetime.fromtimestamp失败1微秒

Python 3 datetime.fromtimestamp失败1微秒,python,datetime,python-3.x,unix-timestamp,python-3.4,Python,Datetime,Python 3.x,Unix Timestamp,Python 3.4,我想以微秒分辨率将日期时间保存为时间戳。但Python3DateTime模块在加载时似乎损失了一微秒。为了测试这一点,让我们创建一个脚本: 测试\u 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

我想以微秒分辨率将日期时间保存为时间戳。但Python3DateTime模块在加载时似乎损失了一微秒。为了测试这一点,让我们创建一个脚本:

测试\u 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测试\u 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
回溯(最近一次呼叫最后一次):
文件“test_datetime.py”,第14行,在
断言d==d2,'在过程{}:{}中失败!={}格式(n,d,d2)
AssertionError:在通过过程中失败4:2014-07-02 11:51:46.984716!=2014-07-02 11:51:46.984715

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

时间戳是POSIX时间,它本质上被概念化为自任意“历元”以来的整数秒数
datetime.fromtimestamp()
返回“与POSIX时间戳对应的本地日期和时间,例如由
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))
我得到了499968个“不可靠”的时间,但我还没有检查它们。

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

例如,浮点值
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(微秒=微秒)


在这种情况下,你可能会发现这一点很有启发性。政治公众人物谈到了以浮点数表示的时间戳的精度问题。

用新微秒值生成新值的简单方法:
d=now。替换(微秒=randint(0999999))
@MartijnPieters:是的,谢谢。我只是想在这个问题上非常明确。我会发现
现在.replace(microsend=randint(099999))
更清晰,因为这样你就不必解析其他6个关键字参数来查看你在这行中做了什么。
datetime
实际上没有使用
time.time()
,无论平台如何,都支持微秒。然而,这是浮点数的问题。我同意这是一个浮点数问题,并且没有意识到现在所有的平台都提供微秒精度——只是他们将计时器报告为最佳分辨率的浮点数。499968“不可靠”次数:几乎一半。这听起来是正确的,因为在转换时(注意这里的
\u PyTime\u ROUND\u DOWN
常量)。给定100万个浮点值,有可能会有不到一半的浮点值略低于原始微秒值,另一半的浮点值略高于原始微秒值。余数是可以精确表示为二进制分数的值。这难道不表明
fromtimestamp
中存在截断而不是舍入的缺陷吗?考虑到时间戳预计会稍微偏离。这建议通过使用
fromtimstamp(ts+0.0000005)
@MarkRansom:引入参数;在此之前,舍入在其他操作中是隐含的。根据评论和建议,这里保留了旧的舍入行为,并为其他地方的问题引入了显式舍入。@MarkRansom:啊,找到了。看见舍入被更改,因为这是其他地方更常见的行为,并避免将时间戳舍入到未来。@Markransem:例如,与其让另一半时间戳延迟一微秒,不如让另一半时间戳延迟一微秒。我不确定我是否同意该问题中的推理。有一个重要的原则我喜欢坚持:当有互补功能时,如果可能的话,往返应该总是产生相同的结果。在这种情况下,这是可能的。即使只是在下一个之后添加一个
,而不是四舍五入,也能解决问题。