time --- 時間相關函式

time 模組提供取得目前時間與日期、測量時間間隔以及延遲的函式。

時間紀元(Epoch):以 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.sleep_us(us: int) None

延遲指定的微秒數,應為正數或 0。

此函式會嘗試提供至少 us 微秒的準確延遲,但若系統有其他更高優先權的處理要執行,則可能耗時更久。

time.ticks_ms() int

回傳一個遞增的毫秒計數器,其參考點任意,並在達到某個值後回繞。

回繞值並未明確公開,但為方便討論我們將其稱為 TICKS_MAX。這些值的週期為 TICKS_PERIOD = TICKS_MAX + 1TICKS_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 發生。否則表示 ticks1ticks2 之後發生。此關係ticks1ticks2 彼此相距不超過 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,它則改為計算自上電/重置起算的秒數。

time.time_ns() int

time() 類似,但回傳自紀元起算的奈秒數,以整數表示(通常為大整數,因此會在堆積(heap)上配置記憶體)。

建構子

class time.clock

回傳一個 clock 物件。

方法

tick() None

開始追蹤經過的時間。

fps() float

停止追蹤經過的時間,並回傳目前的 FPS(每秒影格數)。

在呼叫此函式之前,務必先呼叫 tick

avg() float

停止追蹤經過的時間,並回傳目前的平均經過時間(以毫秒為單位)。

在呼叫此函式之前,務必先呼叫 tick

reset() None

重設 clock 物件。