奇怪的 datetime.utcnow() 错误

Eug*_*ash 7 python macos python-datetime docker macos-monterey

考虑这个简单的 Python 脚本:

$ cat test_utc.py
from datetime import datetime

for i in range(10_000_000):
    first = datetime.utcnow()
    second = datetime.utcnow()

    assert first <= second, f"{first=} {second=} {i=}"
Run Code Online (Sandbox Code Playgroud)

当我从 shell 运行它时,就像python test_utc.py它完成时没有错误一样,正如预期的那样。但是,当我在 Docker 容器中运行它时,断言失败:

$ docker run -it --rm -v "$PWD":/code -w /code python:3.10.4 python test_utc.py
Traceback (most recent call last):
  File "/code/test_utc.py", line 7, in <module>
    assert first <= second, f"{first=} {second=} {i=}"
AssertionError: first=datetime.datetime(2022, 5, 24, 19, 5, 1, 861308) second=datetime.datetime(2022, 5, 24, 19, 5, 1, 818270) i=1818860
Run Code Online (Sandbox Code Playgroud)

这怎么可能?

PS 一位同事报告说,增加范围参数也会100_000_000使其在 Mac 上的 shell 中失败(但对我来说不是)。

Kel*_*ndy 9

utcnow指的是now指指的todayfromtimestamp指指的time是指:

\n
\n

虽然此函数通常返回不递减的值,但如果系统时钟在两次调用之间被调回,则它可能返回比前一次调用更低的值。

\n
\n

utcnow代码还显示了它的用法time

\n
def utcnow(cls):\n    "Construct a UTC datetime from time.time()."\n    t = _time.time()\n    return cls.utcfromtimestamp(t)\n
Run Code Online (Sandbox Code Playgroud)\n

这种系统时钟更新也是monotonic存在的原因,它说:

\n
\n

返回单调时钟的值(以小数秒为单位),即不能倒退的时钟。时钟不受系统时钟更新的影响。

\n
\n

并且utcnow没有这样的保证。

\n

您的计算机没有完美的时钟,它时不时地通过互联网与更准确的时钟同步,可能会向后调整它。请参阅此处的示例答案

\n

看起来 Docker 使情况变得更糟,请参阅Docker 博客中的解决 Docker Desktop for Mac 中的时间漂移​​的示例。摘抄:

\n
\n

macOS 没有\xe2\x80\x99t 本机容器支持。辅助虚拟机有自己的内部时钟,与主机\xe2\x80\x99s 时钟分开。当两个时钟偏离时,依赖时间或文件时间戳的命令可能会突然开始表现不同

\n
\n

最后,您可以增加在发生向后更新时捕获向后更新的机会。如果一个不是发生在 gettingfirst和之间,second而是发生在second和 next之间first,你就会错过它!下面的代码修复了这个问题,并且还进行了微优化(包括删除中间人utcnow),因此它检查得更快/更频繁:

\n
import time\nfrom itertools import repeat\n\ndef function():\n    n = 10_000_000\n    reps = repeat(1, n)\n    now = time.time\n    first = now()\n    for _ in reps:\n        second = now()\n        assert first <= second, f"{first=} {second=} i={n - sum(reps)}"\n        first = second\nfunction()\n
Run Code Online (Sandbox Code Playgroud)\n