紀元時間詳解:Unix 時間戳零是什麼?
Unix 時間戳 0 是 1970 年 1 月 1 日 00:00:00 UTC。了解為什麼那個日期成為 Unix 紀元、負時間戳代表什麼、紀元的秒/毫秒/微秒有何不同,以及現代系統適用哪些限制。
Unix 時間戳 0 代表什麼
每個 Unix 時間戳都計數自單一參考點——1970 年 1 月 1 日 00:00:00 UTC——以來經過的時間。在 Unix 的原始定義中單位是秒,所以時間戳 0 正好是 1970 年 1 月 1 日的 UTC 午夜,86400 正好是 24 小時之後。現代系統也使用毫秒、微秒與奈秒,但參考點相同。每個正值表示紀元之後的某個瞬間;每個負值表示之前的瞬間。
為什麼是 1970 年 1 月 1 日?
這個日期由貝爾實驗室的 Unix 開發者在 1970 年代初選定,作為略早於 Unix 本身誕生的實用參考點。它需要足夠早,使一般系統時間戳為正;足夠近,以適配早期電腦的小整數範圍;又足夠簡單,便於心算核對。當 Unix 變得有影響力後,語言、作業系統、資料庫、日誌格式與網路協定都繼承了同一個起點。
- Unix 自 1969 年起在貝爾實驗室開發 —— 紀元設在其稍前
- 選擇 1 月 1 日作為乾淨的日曆邊界
- 用 1970 年而非 1969 年,因為實作使用從零開始的計數器
- 現代所有作業系統、語言與協定都繼承了這個紀元 —— 沒有競爭標準
負時間戳:1970 年之前的日期
負的 Unix 時間戳表示 1970 年 1 月 1 日 00:00:00 UTC 之前的瞬間。大多數現代 64 位系統都能正確支援,這對歷史資料、出生日期、封存記錄,以及與紀元早於 Unix 的系統互通很有用。較舊的函式庫與資料庫可能拒絕負值,因此歷史資料集值得做一次顯式的相容性檢查。
- -1 = December 31, 1969 23:59:59 UTC (one second before the epoch)
- -86400 = December 31, 1969 00:00:00 UTC (one day before the epoch)
- -2208988800 = January 1, 1900 00:00:00 UTC
- JavaScript: new Date(-86400 * 1000).toISOString() → '1969-12-31T00:00:00.000Z'
格式的實際上限
Unix 紀元本身不是上限;儲存型別才是。儲存在 32 位有號整數中的時間戳,會比儲存在 64 位整數、JavaScript 數字或資料庫原生 timestamp 型別中的時間戳早得多地失效。評估時間戳安全性時,既要問使用什麼單位,也要問用什麼數值型別儲存。
- 32-bit signed integer max: 2,147,483,647 = January 19, 2038 03:14:07 UTC (the Year 2038 problem)
- JavaScript Date max: 8,640,000,000,000,000 ms = September 13, 275760 CE
- 64-bit signed integer max: ~9.2 × 10^18 seconds = year ~292 billion
- PostgreSQL TIMESTAMPTZ max: January 1, 294276 CE
其他系統的紀元
Unix 並不是運算中使用的唯一紀元。其他系統出於歷史或技術原因定義了自己的參考點。
- GPS 紀元:1980 年 1 月 6 日 00:00:00 UTC —— 由 GPS 衛星與接收機使用
- Windows FILETIME:1601 年 1 月 1 日 00:00:00 UTC —— 以 100 奈秒為間隔
- Apple Cocoa / NSDate:2001 年 1 月 1 日 00:00:00 UTC
- Excel / Lotus 1-2-3:1900 年 1 月 1 日 —— 為相容 Lotus 而刻意保留的閏年差一錯誤
- FAT 檔案系統:1980 年 1 月 1 日 00:00:00 本地時間
紀元的秒、毫秒、微秒與奈秒
timestamp 這個詞並不總是表明單位。Unix 秒在作業系統與後端語言中常見。Unix 毫秒在 JavaScript 與瀏覽器 API 中常見。微秒與奈秒出現在資料庫、追蹤系統與高解析度日誌中。單位同時決定精度與你看到的位數。
- Seconds: 1700000000 — common in Python, PHP, Go, Ruby, C, and Unix command-line tools
- Milliseconds: 1700000000000 — common in JavaScript Date and many web analytics systems
- Microseconds: 1700000000000000 — common in some databases and event pipelines
- Nanoseconds: 1700000000000000000 — common in high-resolution tracing and systems languages
- Always document the unit when storing epoch values in APIs, CSV files, and database columns
紀元時間 vs UTC vs 本地時間
紀元時間是一個計數。UTC 是定義參考時刻的全球時間標準。本地時間是基於 America/New_York 或 Asia/Tokyo 這類時區的顯示選擇。Unix 時間戳不會因使用者出行而改變;改變的只是格式化的日曆日期與時間。
- 同一個時間戳在不同時區可能顯示為不同的本地日期
- UTC 輸出最適合日誌、API 與跨伺服器偵錯
- 本地輸出最適合日曆、使用者介面與報表
- 時區偏移在格式化時套用,並不儲存在 Unix 時間戳內部
紀元時間常見問題
- 紀元時間總是以秒計嗎?
- 經典的 Unix 時間戳是自 1970-01-01 00:00:00 UTC 起的秒,但許多系統在同一紀元下使用毫秒、微秒或奈秒。
- Unix 時間戳能表示 1970 年之前的日期嗎?
- 可以。負時間戳表示 Unix 紀元之前的日期,不過較舊的函式庫與資料庫可能不支援。
- Unix 時間戳包含時區嗎?
- 不包含。Unix 時間戳表示 UTC 中的一個瞬間。只有在把該瞬間轉換為可讀的本地日期時才會使用時區資訊。
- Unix 時間戳的最大值是多少?
- 取決於儲存型別,而非紀元。32 位有號整數最大為 2,147,483,647(2038 年 1 月 19 日),而 64 位整數或 JavaScript 數字可達數億年。詳情見 2038 年問題。