時間戳 bug 很常見,因為 Unix 時間戳本身簡單,但圍繞它的系統並不簡單。同一個數字可能代表秒,也可能代表毫秒。資料庫可能存 UTC,前端卻按本地時區顯示。日誌可能用 ISO 8601,API 卻傳 epoch time。到了夏令時附近,「本地午夜」也可能變得不穩定。
這篇指南給出一套穩定的排錯流程。
第一步永遠是確認單位
先問:這是秒還是毫秒?
傳統 Unix 時間是從 1970-01-01 00:00:00 UTC 開始經過的秒數。JavaScript Date、Java System.currentTimeMillis() 和很多前端 API 使用毫秒。
常見長度:
- 10 位:秒,例如
1721347200 - 13 位:毫秒,例如
1721347200000
如果日期跑到 1970 年附近,通常是把秒傳給了需要毫秒的函式。如果日期跑到很遠的未來,通常是把毫秒又乘了一次 1000。
// 秒級時間戳轉 JavaScript Date
new Date(1721347200 * 1000)
// 毫秒級時間戳轉 JavaScript Date
new Date(1721347200000)
UTC 儲存,本地展示
Unix 時間戳表示一個絕對時刻,本身不包含時區。時區只在展示為日曆日期時出現。
同一個時間戳,在紐約、倫敦、上海、東京會顯示成不同本地時間。這是正常現象。變的是展示規則,不是時間戳。
排查時同時比較兩種輸出:
const d = new Date(1721347200 * 1000)
console.log(d.toISOString()) // UTC
console.log(d.toString()) // 本地時間
如果 UTC 正確但 UI 看起來差一天,問題多半在本地時區展示,而不是時間戳本身。
日期型值不要隨便轉時間戳
2026-07-19 這種只有日期、沒有時間的字串很容易被誤用。有些系統把它當作 UTC 零點,有些系統當作本地零點。轉成時間戳後,不同時區使用者可能看到前一天或後一天。
生日、帳單日期、活動日期這類「日曆概念」,最好存原始日期。如果你需要的是具體瞬間,再轉成時間戳。
夏令時問題
夏令時會讓本地時間不連續。某個本地小時可能不存在,也可能出現兩次。如果任務排程設在本地 02:30,有些日期可能根本沒有這個時間點。
可靠系統建議:
- 用 UTC 時間戳儲存
- 需要時另存使用者時區
- 只在展示層轉換為本地時間
- 使用者期待「本地日曆的一天後」時,不要簡單加
86400秒
資料庫和 API 排查清單
當時間戳跨越資料庫和 API 時,寫下:
- 原始值
- 單位:秒還是毫秒
- UTC ISO 字串
- 本地展示字串
- 資料庫欄位型別
- API 契約
大多數問題會在這裡暴露。BIGINT 可能存毫秒,timestamp with time zone 可能已經存了規範化後的時刻。API 文件可能寫秒,前端程式碼卻傳送毫秒。
日誌和監控
日誌裡經常混用格式:
2026-07-19T10:30:00Z
1721385000
1721385000000
Jul 19 18:30:00
比較事件順序前,先把格式統一。建議以 UTC ISO 字串作為排查基準,因為可讀且不容易誤解。
當兩個系統的事件順序對不上,檢查:
- 兩台機器時間是否同步
- 兩邊時間戳單位是否一致
- 一個欄位是接收時間,另一個是否是事件發生時間
- 一個日誌檢視器是否顯示本地時間,另一個是否顯示 UTC
JavaScript 解析提醒
JavaScript 的日期解析比較寬鬆,但並不總是跨環境一致。優先使用明確的時間戳、帶時區的 ISO 字串,或由表單欄位主動轉換。
推薦:
new Date('2026-07-19T10:30:00Z')
new Date(1721385000000)
風險較高:
new Date('2026/07/19 10:30:00')
new Date('07/19/2026')
模糊格式在不同瀏覽器或執行環境中可能得到不同結果。
實用排錯流程
用 Unix 時間戳轉換工具 把原始值雙向轉換。如果工具顯示的 UTC 時刻正確,檢查你的 UI 展示邏輯。如果工具顯示的時刻就不對,檢查單位和來源系統。
如果時間戳在 JSON payload 裡,先用 JSON 格式化工具 驗證結構。如果時間戳在 URL 參數中,例如 ?expires=1721385000,可以用 URL 參數解析工具 單獨提取再轉換。