alltools.one
Development
2025-07-05
7 min
alltools.one Team
UnixTimestampEpochTime ZoneDate

Unix 時間戳解析:換算方法與常見陷阱

Unix 時間戳是程式設計中最簡單卻最常被誤解的概念之一。它是自 1970 年 1 月 1 日 00:00:00 UTC 以來經過的秒數——這個時間點被稱為 Unix Epoch。儘管概念簡單,時間戳卻是與時區、精度和溢位相關 Bug 的常見來源。

什麼是 Unix Epoch?

Unix Epoch——1970 年 1 月 1 日 00:00:00 UTC——被選為 Unix 時間的起始點。每個時間戳都是相對於此刻的測量值:

時間戳日期與時間 (UTC)
01970 年 1 月 1 日 00:00:00
864001970 年 1 月 2 日 00:00:00
10000000002001 年 9 月 9 日 01:46:40
17000000002023 年 11 月 14 日 22:13:20
20000000002033 年 5 月 18 日 03:33:20

負數時間戳代表 Epoch 之前的日期。例如,-86400 是 1969 年 12 月 31 日。

使用我們的時間戳轉換器即時轉換時間戳。

秒 vs. 毫秒

這是最常見的混淆來源。不同的系統使用不同的精度:

系統精度範例
Unix/POSIX1700000000
JavaScript毫秒1700000000000
Java (System.currentTimeMillis)毫秒1700000000000
Python (time.time)秒(浮點數)1700000000.123
PostgreSQL (extract epoch)秒(浮點數)1700000000.123456

經驗法則:如果數字有 13 位數,那是毫秒。如果有 10 位數,那是秒。

// JavaScript returns milliseconds
const nowMs = Date.now();          // 1700000000000
const nowSec = Math.floor(nowMs / 1000);  // 1700000000

時區處理

Unix 時間戳一律是 UTC。它們不包含時區資訊。這其實是一個優點——它提供了一個通用的參考點。

混淆通常發生在將時間戳轉換為人類可讀的日期時:

const ts = 1700000000;
const date = new Date(ts * 1000);

date.toUTCString();      // "Tue, 14 Nov 2023 22:13:20 GMT"
date.toLocaleString();    // Depends on user's local time zone
date.toISOString();       // "2023-11-14T22:13:20.000Z"

最佳實踐:以 UTC 儲存和傳輸時間戳。只在展示層將其轉換為本地時間,越靠近使用者越好。

2038 年問題

傳統 Unix 系統將時間戳儲存為 32 位元有號整數。最大值為 2,147,483,647,對應到 2038 年 1 月 19 日 03:14:07 UTC

超過這個時刻,32 位元時間戳會溢位為負數,回繞到 1901 年 12 月 13 日。這類似於千年蟲問題。

目前狀況

  • 大多數現代系統使用 64 位元時間戳(可用到 2920 億年後)
  • Linux 核心自 5.6 版本(2020 年)起已完成 64 位元時間戳的清理
  • 嵌入式系統和舊版資料庫仍有風險
  • 如果你正在開發處理 2038 年以後日期的軟體,請驗證你的時間戳儲存方式

各語言中的轉換

JavaScript

// Current timestamp (seconds)
const now = Math.floor(Date.now() / 1000);

// Timestamp to Date
const date = new Date(1700000000 * 1000);

// Date to timestamp
const ts = Math.floor(new Date('2023-11-14').getTime() / 1000);

Python

import time, datetime

# Current timestamp
now = int(time.time())

# Timestamp to datetime
dt = datetime.datetime.fromtimestamp(1700000000, tz=datetime.timezone.utc)

# Datetime to timestamp
ts = int(dt.timestamp())

SQL (PostgreSQL)

-- Current timestamp
SELECT EXTRACT(EPOCH FROM NOW());

-- Timestamp to date
SELECT TO_TIMESTAMP(1700000000);

-- Date to timestamp
SELECT EXTRACT(EPOCH FROM '2023-11-14'::timestamp);

常見陷阱

1. 混淆秒和毫秒

如果日期顯示為 1970 年 1 月,表示你可能在預期毫秒的地方傳入了秒(或反過來)。務必確認 API 預期的精度。

2. 日期字串中忽略時區

解析 "2023-11-14" 但未指定時區,會以本機時區建立日期,而這會因伺服器位置不同而改變。務必包含時區:"2023-11-14T00:00:00Z"

3. 浮點數精度

將時間戳儲存為浮點數時,超過毫秒可能會失去精度。對於需要微秒或奈秒精度的場景,使用整數並搭配適當的倍數。

4. 閏秒

Unix 時間戳不考慮閏秒。Unix 的一天永遠是剛好 86,400 秒,即使實際的 UTC 天偶爾有 86,401 秒。對大多數應用來說這不影響。對於科學或衛星應用,請改用 TAI(國際原子時)。

ISO 8601:人類可讀的替代方案

雖然時間戳很適合計算,ISO 8601 是人類可讀日期表示的標準:

2023-11-14T22:13:20Z          # UTC
2023-11-14T17:13:20-05:00     # Eastern Time
2023-11-14                     # Date only

大多數 API 應該接受並回傳 ISO 8601 字串。在內部使用時間戳進行計算和儲存。

常見問題

為什麼 Unix 時間從 1970 年 1 月 1 日開始?

這個日期是在 1970 年代初期 Bell Labs 開發 Unix 時隨意選擇的。需要一個夠近的日期來避免將位元浪費在遙遠的過去。由於 32 位元整數在兩個方向各能儲存約 68 年,從 1970 年開始可以涵蓋 1901 年到 2038 年。

我應該在資料庫中將日期存為時間戳還是格式化字串?

將日期存為時間戳(整數或原生 datetime 類型),以便高效排序、比較和運算。格式化字串更難正確查詢和排序。大多數資料庫都有原生 datetime 類型能妥善處理。將字串格式化保留給展示和 API 回應。

相關資源

Published on 2025-07-05
Unix Timestamps Explained: Conversion and Common Pitfalls | alltools.one