考虑以下简单的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=}"

当我像python test_utc.py一样从shell运行它时,它完成了没有错误的操作,正如预期的那样.但是,当我在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

怎么可能呢?

P、 一位美国同事报告称,将range参数增加到100_000_000也会使其在mac上的shell中失败(但对我来说不是这样).

推荐答案

utcnownowtodayfromtimestamptime,表示:

虽然此函数通常返回非递减值,但如果在两次调用之间设置了系统时钟,则它可以返回比前一次调用更低的值.

utcnow code还显示了time的用法:

def utcnow(cls):
    "Construct a UTC datetime from time.time()."
    t = _time.time()
    return cls.utcfromtimestamp(t)

此类系统时钟更新也是monotonic存在的原因,即:

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

utcnow没有这样的保证.

你的电脑没有一个完美的时钟,它时不时地通过互联网与更精确的时钟同步,可能会向后调整.参见示例answers here.

看起来Docker让情况变得更糟,参见Docker博客中的示例Addressing Time Drift in Docker Desktop for Mac.摘录:

macOS没有本机容器支持.helper VM有自己的内部时钟,独立于主机的时钟.当两个时钟漂移时,依赖于时间或文件时间戳的命令可能会突然开始表现出不同的行为

最后,当发生向后更新时,您可以增加捕获向后更新的机会.如果一个不是发生在firstsecond之间,而是发生在second到下一个first之间,你会错过它的!下面的代码修复了该问题,并进行了微优化(包括删除utcnow中间人),因此它可以更快/更频繁地进行判断:

import time
from itertools import repeat

def function():
    n = 10_000_000
    reps = repeat(1, n)
    now = time.time
    first = now()
    for _ in reps:
        second = now()
        assert first <= second, f"{first=} {second=} i={n - sum(reps)}"
        first = second
function()

Python相关问答推荐

PyQt5,如何使每个对象的 colored颜色 不同?'

Pandas计数符合某些条件的特定列的数量

如何根据一列的值有条件地 Select 前N个组,然后按两列分组?

如何根据一列的值有条件地 Select 前N组?

什么是最好的方法来切割一个相框到一个面具的第一个实例?

与命令行相比,相同的Python代码在Companyter Notebook中运行速度慢20倍

可以bcrypts AES—256 GCM加密损坏ZIP文件吗?

网格基于1.Y轴与2.x轴显示在matplotlib中

数据框,如果值在范围内,则获取范围和

如何在Airflow执行日期中保留日期并将时间转换为00:00

在我融化极点数据帧之后,我如何在不添加索引的情况下将其旋转回其原始形式?

Polars表达式无法访问中间列创建表达式

将数字数组添加到Pandas DataFrame的单元格依赖于初始化

启动线程时,Python键盘模块冻结/不工作

一维不匹配两个数组上的广义ufunc

如何在基于时间的数据帧中添加计算值

根据边界点的属性将图划分为子图

如何计算Pandas 中具有特定条件的行之间的天差

使用Django标签显示信息

Python键盘模块不会立即检测到按键