使用 Python 的 datetime 时间戳,精度为微秒级
datetime timestamp using Python with microsecond level accuracy
我正在尝试在 Windows OS 和 Python 3.10+ 中的 macOS 上获得精确到微秒的时间戳。
在 Windows OS 上,我注意到 Python 的内置 time.time()
(与 datetime.fromtimestamp()
配对)和 datetime.datetime.now()
似乎有一个较慢的时钟。它们没有足够的分辨率来区分微秒级事件。好消息是 time
函数,如 time.perf_counter()
和 time.time_ns()
do 似乎使用了足够快的时钟来测量微秒级事件。
遗憾的是,我不知道如何将它们放入 datetime
对象中。如何将 time.perf_counter()
或 PEP 564 的纳秒分辨率时间函数的输出转换为 datetime
对象?
注意:我不需要纳秒级别的东西,所以丢掉低于 1-μs 的精度也没关系。
当前解
这是我目前的(hacky)解决方案,实际上工作正常,但我想知道是否有更简洁的方法:
import time
from datetime import datetime, timedelta
from typing import Final
IMPORT_TIMESTAMP: Final[datetime] = datetime.now()
INITIAL_PERF_COUNTER: Final[float] = time.perf_counter()
def get_timestamp() -> datetime:
"""Get a high resolution timestamp with μs-level precision."""
dt_sec = time.perf_counter() - INITIAL_PERF_COUNTER
return IMPORT_TIMESTAMP + timedelta(seconds=dt_sec)
这几乎和它得到的一样好,因为 C 模块(如果可用)会覆盖 datetime
的纯 Python 实现中定义的所有 类具有快速 C 实现的模块,并且没有挂钩。
参考:python/cpython@cf86e36
注意:
- 精度存在固有 sub-microsecond 误差,等于在
datetime.now()
中获取系统时间与获取性能计数器时间之间花费的时间。
- 添加
datetime
和 timedelta
需要 sub-microsecond 性能成本。
根据您的具体用例,如果多次调用,这可能重要也可能不重要。
稍微改进一下:
INITIAL_TIMESTAMP: Final[float] = time.time()
INITIAL_TIMESTAMP_PERF_COUNTER: Final[float] = time.perf_counter()
def get_timestamp_float() -> float:
dt_sec = time.perf_counter() - INITIAL_TIMESTAMP_PERF_COUNTER
return INITIAL_TIMESTAMP + dt_sec
def get_timestamp_now() -> datetime:
dt_sec = time.perf_counter() - INITIAL_TIMESTAMP_PERF_COUNTER
return datetime.fromtimestamp(INITIAL_TIMESTAMP + dt_sec)
轶事数字
Windows:
# Intrinsic error
timeit.timeit('datetime.now()', setup='from datetime import datetime')/1000000 # 0.31 μs
timeit.timeit('time.time()', setup='import time')/1000000 # 0.07 μs
# Performance cost
setup = 'from datetime import datetime, timedelta; import time'
timeit.timeit('datetime.now() + timedelta(1.000001)', setup=setup)/1000000 # 0.79 μs
timeit.timeit('datetime.fromtimestamp(time.time() + 1.000001)', setup=setup)/1000000 # 0.44 μs
# Resolution
min get_timestamp_float() delta: 239 ns
Windows 和 macOS:
Windows
macOS
# Intrinsic error
timeit.timeit('datetime.now()', setup='from datetime import datetime')/1000000
0.31 μs
0.61 μs
timeit.timeit('time.time()', setup='import time')/1000000
0.07 μs
0.08 μs
# Performance cost
setup = 'from datetime import datetime, timedelta; import time'
-
-
timeit.timeit('datetime.now() + timedelta(1.000001)', setup=setup)/1000000
0.79 μs
1.26 μs
timeit.timeit('datetime.fromtimestamp(time.time() + 1.000001)', setup=setup)/1000000
0.44 μs
0.69 μs
# Resolution
min time() delta
(benchmark)
x ms
716 ns
min get_timestamp_float() delta
239 ns
239 ns
239 ns 是 float
在 Unix 时间量级允许的最小差异,如评论中 Kelly Bundy 所述。
x = time.time()
print((math.nextafter(x, 2*x) - x) * 1e9) # 238.4185791015625
脚本
解析脚本,基于https://www.python.org/dev/peps/pep-0564/#script:
import math
import time
from typing import Final
LOOPS = 10 ** 6
INITIAL_TIMESTAMP: Final[float] = time.time()
INITIAL_TIMESTAMP_PERF_COUNTER: Final[float] = time.perf_counter()
def get_timestamp_float() -> float:
dt_sec = time.perf_counter() - INITIAL_TIMESTAMP_PERF_COUNTER
return INITIAL_TIMESTAMP + dt_sec
min_dt = [abs(time.time() - time.time())
for _ in range(LOOPS)]
min_dt = min(filter(bool, min_dt))
print("min time() delta: %s ns" % math.ceil(min_dt * 1e9))
min_dt = [abs(get_timestamp_float() - get_timestamp_float())
for _ in range(LOOPS)]
min_dt = min(filter(bool, min_dt))
print("min get_timestamp_float() delta: %s ns" % math.ceil(min_dt * 1e9))
我正在尝试在 Windows OS 和 Python 3.10+ 中的 macOS 上获得精确到微秒的时间戳。
在 Windows OS 上,我注意到 Python 的内置 time.time()
(与 datetime.fromtimestamp()
配对)和 datetime.datetime.now()
似乎有一个较慢的时钟。它们没有足够的分辨率来区分微秒级事件。好消息是 time
函数,如 time.perf_counter()
和 time.time_ns()
do 似乎使用了足够快的时钟来测量微秒级事件。
遗憾的是,我不知道如何将它们放入 datetime
对象中。如何将 time.perf_counter()
或 PEP 564 的纳秒分辨率时间函数的输出转换为 datetime
对象?
注意:我不需要纳秒级别的东西,所以丢掉低于 1-μs 的精度也没关系。
当前解
这是我目前的(hacky)解决方案,实际上工作正常,但我想知道是否有更简洁的方法:
import time
from datetime import datetime, timedelta
from typing import Final
IMPORT_TIMESTAMP: Final[datetime] = datetime.now()
INITIAL_PERF_COUNTER: Final[float] = time.perf_counter()
def get_timestamp() -> datetime:
"""Get a high resolution timestamp with μs-level precision."""
dt_sec = time.perf_counter() - INITIAL_PERF_COUNTER
return IMPORT_TIMESTAMP + timedelta(seconds=dt_sec)
这几乎和它得到的一样好,因为 C 模块(如果可用)会覆盖 datetime
的纯 Python 实现中定义的所有 类具有快速 C 实现的模块,并且没有挂钩。
参考:python/cpython@cf86e36
注意:
- 精度存在固有 sub-microsecond 误差,等于在
datetime.now()
中获取系统时间与获取性能计数器时间之间花费的时间。 - 添加
datetime
和timedelta
需要 sub-microsecond 性能成本。
根据您的具体用例,如果多次调用,这可能重要也可能不重要。
稍微改进一下:
INITIAL_TIMESTAMP: Final[float] = time.time()
INITIAL_TIMESTAMP_PERF_COUNTER: Final[float] = time.perf_counter()
def get_timestamp_float() -> float:
dt_sec = time.perf_counter() - INITIAL_TIMESTAMP_PERF_COUNTER
return INITIAL_TIMESTAMP + dt_sec
def get_timestamp_now() -> datetime:
dt_sec = time.perf_counter() - INITIAL_TIMESTAMP_PERF_COUNTER
return datetime.fromtimestamp(INITIAL_TIMESTAMP + dt_sec)
轶事数字
Windows:
# Intrinsic error
timeit.timeit('datetime.now()', setup='from datetime import datetime')/1000000 # 0.31 μs
timeit.timeit('time.time()', setup='import time')/1000000 # 0.07 μs
# Performance cost
setup = 'from datetime import datetime, timedelta; import time'
timeit.timeit('datetime.now() + timedelta(1.000001)', setup=setup)/1000000 # 0.79 μs
timeit.timeit('datetime.fromtimestamp(time.time() + 1.000001)', setup=setup)/1000000 # 0.44 μs
# Resolution
min get_timestamp_float() delta: 239 ns
Windows 和 macOS:
Windows | macOS | |
---|---|---|
# Intrinsic error | ||
timeit.timeit('datetime.now()', setup='from datetime import datetime')/1000000 |
0.31 μs | 0.61 μs |
timeit.timeit('time.time()', setup='import time')/1000000 |
0.07 μs | 0.08 μs |
# Performance cost | ||
setup = 'from datetime import datetime, timedelta; import time' |
- | - |
timeit.timeit('datetime.now() + timedelta(1.000001)', setup=setup)/1000000 |
0.79 μs | 1.26 μs |
timeit.timeit('datetime.fromtimestamp(time.time() + 1.000001)', setup=setup)/1000000 |
0.44 μs | 0.69 μs |
# Resolution | ||
min time() delta (benchmark) |
x ms | 716 ns |
min get_timestamp_float() delta |
239 ns | 239 ns |
239 ns 是 float
在 Unix 时间量级允许的最小差异,如评论中 Kelly Bundy 所述。
x = time.time()
print((math.nextafter(x, 2*x) - x) * 1e9) # 238.4185791015625
脚本
解析脚本,基于https://www.python.org/dev/peps/pep-0564/#script:
import math
import time
from typing import Final
LOOPS = 10 ** 6
INITIAL_TIMESTAMP: Final[float] = time.time()
INITIAL_TIMESTAMP_PERF_COUNTER: Final[float] = time.perf_counter()
def get_timestamp_float() -> float:
dt_sec = time.perf_counter() - INITIAL_TIMESTAMP_PERF_COUNTER
return INITIAL_TIMESTAMP + dt_sec
min_dt = [abs(time.time() - time.time())
for _ in range(LOOPS)]
min_dt = min(filter(bool, min_dt))
print("min time() delta: %s ns" % math.ceil(min_dt * 1e9))
min_dt = [abs(get_timestamp_float() - get_timestamp_float())
for _ in range(LOOPS)]
min_dt = min(filter(bool, min_dt))
print("min get_timestamp_float() delta: %s ns" % math.ceil(min_dt * 1e9))