使用Python的日期时间时间戳,精度为微秒级

Int*_*rer 4 python time datetime timestamp

我试图在 Windows 操作系统和 macOS 上使用 Python 3.10+ 获取精确到微秒的时间戳。

\n

在 Windows 操作系统上,我注意到 Python 的内置time.time()(与 配对datetime.fromtimestamp())并且datetime.datetime.now()时钟似乎较慢。它们没有足够的分辨率来区分微秒级事件。好消息是和time之类的函数似乎确实使用了足够快的时钟来测量微秒级事件。time.perf_counter()time.time_ns()

\n

可悲的是,我不知道如何将它们放入datetime对象中。如何将 PEP 564 的纳秒分辨率时间函数的输出time.perf_counter()转换对象datetime

\n

注意:我不需要纳秒级的东西,所以可以放弃低于 1-\xce\xbcs 的精度。

\n
\n

目前的解决方案

\n

这是我当前的(hacky)解决方案,实际上工作正常,但我想知道是否有更干净的方法:

\n
import time\nfrom datetime import datetime, timedelta\nfrom typing import Final\n\nIMPORT_TIMESTAMP: Final[datetime] = datetime.now()\nINITIAL_PERF_COUNTER: Final[float] = time.perf_counter()\n\n\ndef get_timestamp() -> datetime:\n    """Get a high resolution timestamp with \xce\xbcs-level precision."""\n    dt_sec = time.perf_counter() - INITIAL_PERF_COUNTER\n    return IMPORT_TIMESTAMP + timedelta(seconds=dt_sec)\n
Run Code Online (Sandbox Code Playgroud)\n

aar*_*ron 5

这几乎已经是最好的了,因为C 模块(如果可用)会使用datetime快速 C 实现覆盖该模块的纯 Python 实现中定义的所有类,并且没有钩子。
\n参考:python/cpython@cf86e36

\n

注意:

\n
    \n
  1. 准确度存在固有的亚微秒误差,该误差等于获取系统时间datetime.now()和获取性能计数器时间之间所需的时间。
  2. \n
  3. 添加 adatetime和 a会产生亚微秒的性能成本timedelta
  4. \n
\n

根据您的具体用例,如果多次调用,这可能会也可能无关紧要。

\n

一个小小的改进是:

\n
INITIAL_TIMESTAMP: Final[float] = time.time()\nINITIAL_TIMESTAMP_PERF_COUNTER: Final[float] = time.perf_counter()\n\ndef get_timestamp_float() -> float:\n    dt_sec = time.perf_counter() - INITIAL_TIMESTAMP_PERF_COUNTER\n    return INITIAL_TIMESTAMP + dt_sec\n\ndef get_timestamp_now() -> datetime:\n    dt_sec = time.perf_counter() - INITIAL_TIMESTAMP_PERF_COUNTER\n    return datetime.fromtimestamp(INITIAL_TIMESTAMP + dt_sec)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

轶事数字

\n

视窗:

\n
# Intrinsic error\ntimeit.timeit(\'datetime.now()\', setup=\'from datetime import datetime\')/1000000  # 0.31 \xce\xbcs\ntimeit.timeit(\'time.time()\', setup=\'import time\')/1000000                       # 0.07 \xce\xbcs\n\n# Performance cost\nsetup = \'from datetime import datetime, timedelta; import time\'\ntimeit.timeit(\'datetime.now() + timedelta(1.000001)\', setup=setup)/1000000            # 0.79 \xce\xbcs\ntimeit.timeit(\'datetime.fromtimestamp(time.time() + 1.000001)\', setup=setup)/1000000  # 0.44 \xce\xbcs\n
Run Code Online (Sandbox Code Playgroud)\n
# Resolution\nmin get_timestamp_float() delta: 239 ns\n
Run Code Online (Sandbox Code Playgroud)\n

Windows 和 macOS:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n
视窗苹果系统
# 固有错误
timeit.timeit(\'datetime.now()\', setup=\'from datetime import datetime\')/10000000.31 \xce\xbcs0.61 \xce\xbcs
timeit.timeit(\'time.time()\', setup=\'import time\')/10000000.07 \xce\xbcs0.08 \xce\xbcs
# 性能成本
setup = \'from datetime import datetime, timedelta; import time\'--
timeit.timeit(\'datetime.now() + timedelta(1.000001)\', setup=setup)/10000000.79 \xce\xbcs1.26 \xce\xbcs
timeit.timeit(\'datetime.fromtimestamp(time.time() + 1.000001)\', setup=setup)/10000000.44 \xce\xbcs0.69 \xce\xbcs
# 解决
min time() delta(基准)x毫秒716纳秒
min get_timestamp_float() delta239纳秒239纳秒
\n
\n

正如Kelly Bundy在评论中float指出的那样,239 ns 是 Unix 时间量级允许的最小差异。

\n
x = time.time()\nprint((math.nextafter(x, 2*x) - x) * 1e9)  # 238.4185791015625\n
Run Code Online (Sandbox Code Playgroud)\n

脚本

\n

解析脚本,基于https://www.python.org/dev/peps/pep-0564/#script

\n
import math\nimport time\nfrom typing import Final\n\nLOOPS = 10 ** 6\n\nINITIAL_TIMESTAMP: Final[float] = time.time()\nINITIAL_TIMESTAMP_PERF_COUNTER: Final[float] = time.perf_counter()\n\ndef get_timestamp_float() -> float:\n    dt_sec = time.perf_counter() - INITIAL_TIMESTAMP_PERF_COUNTER\n    return INITIAL_TIMESTAMP + dt_sec\n\nmin_dt = [abs(time.time() - time.time())\n          for _ in range(LOOPS)]\nmin_dt = min(filter(bool, min_dt))\nprint("min time() delta: %s ns" % math.ceil(min_dt * 1e9))\n\nmin_dt = [abs(get_timestamp_float() - get_timestamp_float())\n          for _ in range(LOOPS)]\nmin_dt = min(filter(bool, min_dt))\nprint("min get_timestamp_float() delta: %s ns" % math.ceil(min_dt * 1e9))\n
Run Code Online (Sandbox Code Playgroud)\n