你有沒有看過 API 回傳這樣的欄位:"created_at": 1745913600?這個看似神秘的整數,其實是 UNIX 時間戳記(Unix Timestamp)——一種在程式世界中傳遞時間的標準格式。理解它為什麼存在、如何正確使用,是每位工程師都需要掌握的基礎技能。
一、什麼是 UNIX 時間戳記?為什麼程式要用它?
UNIX 時間戳記是指「從 1970 年 1 月 1 日 00:00:00 UTC(稱為 Unix Epoch)到某個時刻,所經過的秒數」。例如,1745913600 代表 2026 年 4 月 29 日 00:00:00 UTC。
程式選擇用這個整數而非人類可讀的字串,主要有三個理由:
- 時區中立:
1745913600在全球所有系統上代表同一個絕對時刻,不存在「台北時間」還是「紐約時間」的歧義 - 易於計算:要計算兩個時間點相差幾秒、排序事件發生順序,直接比較整數即可
- 格式統一:不同語言、不同資料庫系統都能毫無歧義地解讀同一個整數
二、秒 vs 毫秒:最常見的混淆陷阱
UNIX 時間戳記本身定義為「秒數」,但現代系統(特別是 JavaScript 和許多 API)常用毫秒(milliseconds),在數字後面多加了三位數:
| 格式 | 範例值 | 說明 |
|---|---|---|
| 秒(seconds) | 1745913600 |
10 位數,UNIX 標準,C/Python/PHP 常用 |
| 毫秒(milliseconds) | 1745913600000 |
13 位數,JavaScript Date.now() 回傳此格式 |
| 微秒(microseconds) | 1745913600000000 |
16 位數,部分資料庫與高精度系統使用 |
識別方式:看數字的位數。10 位數 → 秒;13 位數 → 毫秒。如果你收到一個 13 位的時間戳記,卻用秒的方式解讀,換算出來的時間會是 56000 年後——這是初學者最常踩的坑之一。
三、API 設計:時間欄位的最佳實踐
設計 REST API 時,時間欄位的格式選擇影響深遠。以下是主流方案的比較:
| 格式 | 範例 | 優點 | 缺點 |
|---|---|---|---|
| UNIX 時間戳記(秒) | 1745913600 |
計算快、無時區歧義、資料量小 | 人類無法直接閱讀 |
| ISO 8601 字串 | 2026-04-29T00:00:00Z |
人類可讀、含時區資訊(Z 表示 UTC) | 字串解析有語言差異 |
| RFC 2822 字串 | Wed, 29 Apr 2026 00:00:00 +0000 |
Email/HTTP 標準格式 | 較冗長,不適合 JSON API |
業界建議
- 內部系統間的 API:優先使用 UNIX 時間戳記(整數),避免時區解讀錯誤
- 對外公開 API:使用 ISO 8601 格式(
2026-04-29T00:00:00Z),方便開發者直接閱讀 - 回傳兩者:部分 API 同時回傳
"created_at": "2026-04-29T00:00:00Z"與"created_at_unix": 1745913600,兼顧可讀性與計算便利 - 統一使用 UTC:無論選哪種格式,儲存與傳遞都應使用 UTC,在前端才根據使用者時區顯示
四、資料庫儲存:INT、DATETIME 還是 TIMESTAMP?
在資料庫中儲存時間,主要有三種欄位類型選擇:
| 類型 | 範例值 | 適用情境 | 注意事項 |
|---|---|---|---|
INT UNSIGNED |
1745913600 |
高效能查詢、跨資料庫移植 | 2038 年問題(32 位元);使用 BIGINT 避免 |
DATETIME |
2026-04-29 00:00:00 |
需要人類可讀、不依賴時區設定 | MySQL DATETIME 不儲存時區資訊,需應用層統一 |
TIMESTAMP |
2026-04-29 00:00:00 |
MySQL 自動轉換時區,適合多時區系統 | 範圍限制到 2038 年;MySQL 8 已部分改善 |
2038 年問題是什麼?
UNIX 時間戳記最初設計為 32 位元有號整數,最大值為 2147483647,對應 2038 年 1 月 19 日 03:14:07 UTC。超過這個時間,32 位元整數會溢位歸零。解決方法是改用 64 位元整數(BIGINT)儲存,可以安全使用到西元 292 億年後。
五、跨語言時間戳記操作速查
JavaScript
// 取得當前時間戳記(毫秒) const ms = Date.now(); // 1745913600000 // 轉為秒 const sec = Math.floor(Date.now() / 1000); // 1745913600 // 時間戳記轉 Date 物件 const date = new Date(1745913600 * 1000); // Date 物件轉時間戳記(秒) const ts = Math.floor(date.getTime() / 1000);
Python
import time, datetime, timezone # 取得當前時間戳記(秒,浮點數) ts = time.time() # 1745913600.123 # 轉為 datetime(UTC) dt = datetime.datetime.fromtimestamp(1745913600, tz=timezone.utc) # datetime 轉時間戳記 ts = dt.timestamp() # 1745913600.0
PHP
// 取得當前時間戳記(秒)
$ts = time(); // 1745913600
// 格式化輸出
echo date('Y-m-d H:i:s', $ts); // 2026-04-29 00:00:00
// 字串轉時間戳記
$ts = strtotime('2026-04-29 00:00:00 UTC'); // 1745913600
Go
// 取得當前時間戳記(秒) ts := time.Now().Unix() // 1745913600 // 取得毫秒 ms := time.Now().UnixMilli() // 1745913600000 // 時間戳記轉 Time t := time.Unix(1745913600, 0).UTC()
六、JSON API 中的時間欄位
當 API 回傳 JSON 並包含時間欄位時,常見的結構如下:
{
"id": "post_123",
"title": "Hello World",
"created_at": "2026-04-29T00:00:00Z",
"updated_at": 1745913600
}
這份 JSON 混合了兩種時間格式——ISO 8601 字串與 UNIX 時間戳記整數。在實際專案中,同一 API 應統一格式,避免消費端需要同時處理兩種解析邏輯。
七、常見錯誤整理
| 錯誤情境 | 症狀 | 解決方式 |
|---|---|---|
| 毫秒當秒處理 | 時間顯示為西元 56000 年 | 除以 1000 或確認欄位單位 |
| 忘記時區,本地時間存入 UTC 欄位 | 時間差 8 小時(台灣 UTC+8) | 統一在 UTC 儲存,顯示時轉換 |
| 32 位元整數儲存 | 2038 年後溢位歸零 | 使用 BIGINT 或 64 位元整數 |
| 日光節約時間(DST)未處理 | 部分國家時間差一小時 | 使用時區資料庫(如 IANA tzdata)換算 |
| 字串時間未指定時區 | 不同伺服器解讀不一致 | ISO 8601 結尾加 Z 或 +08:00 |
總結
UNIX 時間戳記的設計邏輯很簡單:用一個絕對的整數代表時間,讓所有系統有共同語言。掌握它,能讓你的 API 設計更穩健、資料庫欄位更合理,也能在除錯時快速識別時區錯誤。
- 時間戳記是「從 1970-01-01 00:00:00 UTC 起的秒數」,與時區無關
- 10 位數 = 秒;13 位數 = 毫秒,混淆是最常見的 bug
- API 設計:內部用整數,對外用 ISO 8601,統一使用 UTC
- 資料庫:用 BIGINT 儲存時間戳記,避免 2038 年問題
- 所有語言都有標準函式庫處理時間戳記轉換,不需要手動計算