找回密碼
 立即註冊
    查看: 140|回覆: 0

    時間與時區實作:now、today_at、time_since 正確用法,以及怎麼在當下就測出跨午夜的 bug

    [複製鏈接]

    !lvup!   100%

    386

    主題

    27

    回帖

    2200萬

    積分

    管理員

    積分
    22006063
    發表於 2026-9-21 08:38:45 | 顯示全部樓層 |閱讀模式
    時間相關的 bug 有一個很討厭的特性:寫的時候測起來全部正常。

    你在下午三點寫一條「晚上十點到早上六點進入夜間模式」的自動化,按下儲存,一切看起來都對。然後它從那天起一次都沒有啟動過,而你要兩個月後才會發現 —— 因為「沒有啟動」不會報錯、不會亮紅燈、不會出現在任何清單上。

    這篇的重點不只是列出有哪些時間函式,而是怎麼在下午三點就把「凌晨兩點會發生什麼」測出來。

    怎麼在下午三點就把「凌晨兩點會發生什麼」測出來

    這篇會做完的事


    • 把時區設對,並且用一個會在設錯時明顯出錯的方法驗證它
    • 建一組「時間診斷卡片」—— 一眼看出系統現在對時間的所有判斷
    • 搞懂 now / utcnow / today_at / as_local 各自該用在哪
    • 學會 time_since 與 time_until(順便處理 relative_time 已經被棄用這件事)
    • 寫一個跨午夜的時段判斷,並且當場測試它在凌晨兩點的行為
    • 處理 time_pattern 最常見的兩個手滑(斜線、前導零)
    • 把上面全部組成一條真的會動的夜間模式自動化


    全部做完約 90 分鐘,不需要買任何東西。

    第零步:先把時區弄對

    0-1 設定

    設定 → 系統 → 一般,時區選 Asia/Taipei,國家選台灣,按更新。

    0-2 用一個會出錯的方法驗證它

    不要只看設定頁顯示什麼。 到 開發者工具 → 範本,把左邊輸入框清空,貼上這一整段:
    1. 本地時間  {{ now() }}
    2. UTC 時間  {{ utcnow() }}
    3. 時區名稱  {{ now().tzinfo }}
    4. 與 UTC 差幾小時  {{ ((now() - utcnow()).total_seconds() / 3600) | round(0) | int }}
    5. 今天是星期幾(0=一 6=日)  {{ now().weekday() }}
    複製代碼

    你會看到右邊即時顯示:
    1. 本地時間  2026-09-25 08:45:12.345678+08:00
    2. UTC 時間  2026-09-25 00:45:12.345678+00:00
    3. 時區名稱  CST
    4. 與 UTC 差幾小時  8
    5. 今天是星期幾(0=一 6=日)  4
    複製代碼

    四個判準,缺一不可:


    • 本地時間結尾是 +08:00
    • UTC 時間比本地時間早 8 小時
    • 「與 UTC 差幾小時」是 8,不是 0 也不是 -8
    • 本地時間的「時」跟你手錶上的一致


    「與 UTC 差幾小時」是 0 → 時區設成 UTC 了。所有排程會提早八小時執行。

    時間看起來對但時區名稱是 UTC → 這是最陰險的狀況:機器的系統時鐘被人手動撥快八小時來「修正」。這樣一來 now() 顯示的數字是對的,但 UTC 時間也跟著錯了八小時,而 last_changed 這類內部時間戳是以 UTC 儲存的 —— 所有「距離現在多久」的計算會全部錯八小時。一定要把系統時鐘調回 UTC,用時區設定去修正顯示。

    0-3 順便記住一個關鍵事實

    Home Assistant 內部所有時間戳都存 UTC。 包括:
    1. states.X.last_changed     UTC
    2. states.X.last_updated     UTC
    3. 資料庫的 last_updated_ts  Unix 秒數(本來就沒有時區)
    4. 觸發器的 trigger.now      本地時間
    5. now()                     本地時間
    6. utcnow()                  UTC
    複製代碼

    這張表是後面所有坑的根源。

    第一步:建一張「時間診斷卡片」

    這張卡片的用途是:出問題的時候,一眼看出系統現在對時間的每一個判斷。做一次,之後受用。

    1-1 加範本感測器

    加進 /config/configuration.yaml 最外層:
    1. # /config/configuration.yaml
    2. template:
    3.   - trigger:
    4.       - trigger: time_pattern
    5.         minutes: "/1"
    6.     sensor:
    7.       - name: 診斷 現在時刻
    8.         unique_id: diag_now
    9.         state: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
    10.       - name: 診斷 星期
    11.         unique_id: diag_weekday
    12.         state: >
    13.           {% set w = ['一','二','三','四','五','六','日'] %}
    14.           星期{{ w[now().weekday()] }}
    15.       - name: 診斷 UTC 位移
    16.         unique_id: diag_utc_offset
    17.         unit_of_measurement: h
    18.         state: >
    19.           {{ ((now() - utcnow()).total_seconds() / 3600) | round(0) | int }}
    20.     binary_sensor:
    21.       - name: 診斷 現在是夜間
    22.         unique_id: diag_is_night
    23.         state: >
    24.           {{ now() >= today_at('22:00') or now() < today_at('06:00') }}
    25.       - name: 診斷 現在是上班時間
    26.         unique_id: diag_is_work_hours
    27.         state: >
    28.           {{ now().weekday() < 5
    29.              and today_at('09:00') <= now() < today_at('18:00') }}
    複製代碼

    1-2 檢查設定 → 重新載入

    開發者工具 → YAML → 檢查設定,綠燈之後 重新載入範本實體(第一次加 template: 整合要完整重新啟動)。

    1-3 加卡片

    設定 → 儀表板 → 鉛筆 → 新增卡片 → 手動:

    以下還有約 80% 的內容
    登入後即可閱讀完整教學

    登入繼續閱讀使用 Google 登入
    您需要登入後纔可以回帖 登入 | 立即註冊

    本版積分規則

    延伸閱讀

    加入惟家 LINE 群 QR Code加入惟家 LINE 群

    Archiver|手機版|惟家的智能論壇

    GMT+8, 2026-11-10 00:43 , Processed in 0.136279 second(s), 25 queries .

    快速回覆 返回頂部 返回列表