time --- 时间相关函数¶
time 模块提供了用于获取当前时间和日期、测量时间间隔以及延时的函数。
时间纪元:基于 Alif 和 i.MX RT 的 OpenMV Cam 使用 POSIX 纪元 1970-01-01 00:00:00 UTC。基于 STM32 的 OpenMV Cam 使用纪元 2000-01-01 00:00:00 UTC。可在运行时通过 gmtime(0)[0] 确定纪元年份。
维护实际日历日期/时间:这需要一个实时时钟(RTC)。在 OpenMV Cam 上,系统时间由 machine.RTC 对象提供。当前日历时间可通过 machine.RTC().datetime(tuple) 设置,并由以下方式之一维护:
后备电池(在某些 OpenMV Cam 上是可选组件)。
诸如
ntptime这样的网络时间协议(需要网络连接)。在每次上电时手动设置。RTC 通常在软复位后仍能保持,但在掉电时会丢失,除非安装了后备电池。
如果未维护日历时间,下面那些引用当前绝对时间的函数将不会按预期工作。
函数¶
- time.gmtime(secs: int | None = None) Tuple[int, int, int, int, int, int, int, int]¶
- time.localtime(secs: int | None = None) Tuple[int, int, int, int, int, int, int, int]¶
将以自纪元(见上文)以来的秒数表示的时间 secs 转换为一个 8 元组,其中包含:
(year, month, mday, hour, minute, second, weekday, yearday)。如果未提供 secs 或其为 None,则使用 RTC 中的当前时间。gmtime()函数返回 UTC 的日期时间元组,localtime()返回本地时间的日期时间元组。8 元组中各条目的格式为:
year 包含世纪(例如 2014)。
month 为 1-12
mday 为 1-31
hour 为 0-23
minute 为 0-59
second 为 0-59
weekday 为 0-6,对应周一至周日
yearday 为 1-366
- time.mktime(date_time_tuple: Tuple[int, int, int, int, int, int, int, int]) int¶
这是 localtime 的反函数。它的参数是一个完整的 8 元组,按 localtime 的方式表示时间。它返回一个整数,即自时间纪元以来的秒数。
- time.sleep(seconds: float) None¶
休眠指定的秒数。seconds 可以是浮点数,以便休眠不足一秒的时间。若需更细粒度或仅限整数的延时,请使用
sleep_ms()和sleep_us()函数。调用
sleep()(包括sleep(0))保证会调用待处理的回调函数。
- time.sleep_ms(ms: int) None¶
延时指定的毫秒数,应为正数或 0。
本函数会至少延时给定的毫秒数,但如果必须进行其他处理(例如中断处理程序或其他线程),所花时间可能更长。为 ms 传入 0 仍会允许这些其他处理发生。若需更精确的延时,请使用
sleep_us()。调用
sleep_ms()(包括sleep_ms(0))保证会调用待处理的回调函数。
- time.ticks_ms() int¶
返回一个递增的毫秒计数器,其参考点是任意的,并会在某个值之后回绕。
回绕值不会被显式公开,但为了便于讨论,我们将其称为 TICKS_MAX。这些值的周期为 TICKS_PERIOD = TICKS_MAX + 1。TICKS_PERIOD 保证是 2 的幂,但除此之外可能因移植而异。为简单起见,
ticks_ms()、ticks_us()、ticks_cpu()这几个函数都使用相同的周期值。因此,这些函数将返回 [0 .. TICKS_MAX] 范围内(含端点)的值,共 TICKS_PERIOD 个值。请注意,只使用非负值。在大多数情况下,你应将这些函数返回的值视为不透明的。对它们唯一可用的操作是下文所述的ticks_diff()和ticks_add()函数。注意:直接对这些值执行标准数学运算(+、-)或关系运算符(<、<=、>、>=)将导致无效结果。先执行数学运算再将其结果作为参数传给
ticks_diff()或ticks_add()同样会导致这些函数返回无效结果。
- time.ticks_us() int¶
与上文的
ticks_ms()类似,但以微秒为单位。
- time.ticks_cpu() int¶
与
ticks_ms()和ticks_us()类似,但具有系统中可能的最高分辨率。这通常是 CPU 时钟,函数也因此而得名。但它不一定是 CPU 时钟,系统中可用的其他某种计时源(例如高分辨率定时器)也可以代替使用。本函数确切的计时单位(分辨率)在time模块层面未作规定,但特定移植的文档可能会提供更具体的信息。本函数适用于非常精细的基准测试或非常紧凑的实时循环。请避免在可移植代码中使用它。它在所有 OpenMV Cam 上都可用。
- time.ticks_add(ticks: int, delta: int) int¶
将 ticks 值偏移给定的数值,该数值可正可负。给定一个 ticks 值,本函数可按 tick 值的模运算定义(见上文的
ticks_ms())计算其之前或之后 delta 个 tick 的 ticks 值。ticks 参数必须是调用ticks_ms()、ticks_us()或ticks_cpu()函数(或上一次调用ticks_add())的直接结果。然而,delta 可以是任意整数或数值表达式。ticks_add()适用于计算事件/任务的截止期限。(注意:你必须使用ticks_diff()函数来处理截止期限。)示例:
# Find out what ticks value there was 100ms ago print(ticks_add(time.ticks_ms(), -100)) # Calculate deadline for operation and test for it deadline = ticks_add(time.ticks_ms(), 200) while ticks_diff(deadline, time.ticks_ms()) > 0: do_a_little_of_something() # Find out TICKS_MAX used by this port print(ticks_add(0, -1))
- time.ticks_diff(ticks1: int, ticks2: int) int¶
测量
ticks_ms()、ticks_us()或ticks_cpu()函数返回值之间的 ticks 差值,结果为可能回绕的有符号值。参数顺序与减法运算符相同,
ticks_diff(ticks1, ticks2)与ticks1 - ticks2含义相同。然而,ticks_ms()等函数返回的值可能回绕,因此直接对它们使用减法会产生不正确的结果。这正是需要ticks_diff()的原因,它实现了模运算(更确切地说是环运算),即使对回绕值也能产生正确结果(只要它们彼此相距不太远,见下文)。本函数返回 [-TICKS_PERIOD/2 .. TICKS_PERIOD/2-1] 范围内的 有符号 值(这是二进制补码有符号整数的典型范围定义)。如果结果为负,表示 ticks1 在时间上早于 ticks2。否则表示 ticks1 发生在 ticks2 之后。仅当 ticks1 和 ticks2 相距不超过 TICKS_PERIOD/2-1 个 tick 时,这一结论才 成立。如果不成立,将返回不正确的结果。具体来说,如果两个 tick 值相距 TICKS_PERIOD/2-1 个 tick,函数将返回该值。但如果两者之间已经过去了 TICKS_PERIOD/2 个实时 tick,函数将转而返回 -TICKS_PERIOD/2,即结果值将回绕到可能取值的负值范围。对上述约束的非正式说明:假设你被锁在一个房间里,除了一只标准的 12 刻度时钟外没有任何监测时间流逝的手段。那么如果你现在看一眼表盘,之后 13 个小时都不再看(例如你睡了很长一觉),那么当你最终再看时,可能会觉得只过了 1 个小时。要避免这种错误,只需定期看时钟即可。你的应用程序也应如此。“睡得太久”这个比喻同样直接映射到应用程序的行为:不要让你的应用程序在任何单个任务上运行太久。分步运行任务,并在各步之间进行时间记录。
ticks_diff()旨在适应各种使用模式,其中包括:带超时的轮询。在这种情况下,事件的顺序是已知的,你只需处理
ticks_diff()的正值结果:# Wait for GPIO pin to be asserted, but at most 500us start = time.ticks_us() while pin.value() == 0: if time.ticks_diff(time.ticks_us(), start) > 500: raise TimeoutError
事件调度。在这种情况下,如果某个事件已逾期,
ticks_diff()的结果可能为负:# This code snippet is not optimized now = time.ticks_ms() scheduled_time = task.scheduled_time() if ticks_diff(scheduled_time, now) > 0: print("Too early, let's nap") sleep_ms(ticks_diff(scheduled_time, now)) task.run() elif ticks_diff(scheduled_time, now) == 0: print("Right at time!") task.run() elif ticks_diff(scheduled_time, now) < 0: print("Oops, running late, tell task to run faster!") task.run(run_faster=true)
注意:不要将
time()值传给ticks_diff(),你应对它们使用普通的数学运算。但请注意,time()也可能(而且终将)溢出。这被称为 https://en.wikipedia.org/wiki/Year_2038_problem 。
- time.time() int¶
假设底层 RTC 已按上文所述设置并维护,返回自纪元以来的秒数,以整数形式表示。如果未设置 RTC,本函数将返回自某个移植特定的时间参考点以来的秒数(对于没有电池后备 RTC 的嵌入式开发板,通常是自上电或复位以来)。如果你想开发可移植的 MicroPython 应用程序,就不应依赖本函数提供高于秒级的精度。如果你需要更高精度的绝对时间戳,请使用
time_ns()。如果可以接受相对时间,则使用ticks_ms()和ticks_us()函数。如果你需要日历时间,则不带参数的gmtime()或localtime()是更好的选择。与 CPython 的区别
在 CPython 中,本函数返回自 Unix 纪元(1970-01-01 00:00 UTC)以来的秒数,以浮点值表示,通常具有微秒精度。在 OpenMV Cam 上,它返回一个具有一秒精度的 整数——硬件无法在一个浮点数中同时表示长时间范围和亚秒精度——而且纪元因开发板而异(见上文的 时间纪元)。如果没有已设置的电池后备 RTC,它将转而统计自上电/复位以来的秒数。