日期顯示 1970 年,或者年份跑到了五萬多年後
這兩個症狀,來自同一個 bug。
你的代碼裏有個時間戳,傳給了某個函數,結果要麼輸出 1970-01-20,要麼輸出某個幾萬年後的日期。翻來覆去找不到哪裏寫錯了,但其實原因就一個字:位。
時間戳有兩種精度——秒和毫秒——差了 1000 倍。兩者混用,日期就會錯得離譜。
爲什麼 JavaScript 用毫秒,其他語言用秒?
Unix 標準定義時間戳是"從 1970-01-01 00:00:00 UTC 到現在經過的秒數",這是歷史標準,Python、PHP、Go、數據庫基本都遵循。
JavaScript 是個例外。Brendan Eich 在設計 JavaScript 的 Date 對象時,選擇了毫秒作爲單位,原因大概是當時認爲毫秒精度對前端更合適。這個決定定下來就改不了了,現在整個前端生態都是毫秒。
所以現狀是:
| 語言/系統 | 默認精度 | 典型位數 |
|---|---|---|
| Unix / Linux | 秒 | 10位 |
Python time.time() |
秒(浮點) | 10位整數部分 |
PHP time() |
秒 | 10位 |
Go time.Now().Unix() |
秒 | 10位 |
MySQL UNIX_TIMESTAMP() |
秒 | 10位 |
JavaScript Date.now() |
毫秒 | 13位 |
Java System.currentTimeMillis() |
毫秒 | 13位 |
看位數是最快的判斷方式:10位是秒,13位是毫秒。
四種混用場景和對應修復
場景1:把秒傳給了 JavaScript new Date()
// 後端返回了秒級時間戳
const ts = 1745000000; // 10位,秒
// 錯誤寫法:new Date() 期望毫秒
const date = new Date(ts);
console.log(date.toISOString());
// 輸出: "1970-01-20T20:06:40.000Z" ← 1970年!
// 正確寫法:乘以1000
const date = new Date(ts * 1000);
console.log(date.toISOString());
// 輸出: "2026-04-11T02:13:20.000Z" ← 正確
場景2:把毫秒傳給了 Python datetime.fromtimestamp()
from datetime import datetime
ts = 1745000000000 # 13位,毫秒(比如從前端拿到的)
# 錯誤寫法
dt = datetime.fromtimestamp(ts)
# ValueError: year 55916 is out of range ← 五萬多年後!
# 正確寫法:除以1000
dt = datetime.fromtimestamp(ts / 1000)
print(dt) # 2026-04-11 02:13:20
場景3:前端發給後端的時間戳沒轉換
這是最容易被忽視的場景,因爲前後端各自測試都正常,聯調纔出問題。
// 前端:用 Date.now() 獲取當前時間戳
const payload = {
created_at: Date.now() // 1745000000000,毫秒
};
fetch('/api/save', {
method: 'POST',
body: JSON.stringify(payload)
});
# 後端 Python:以爲收到的是秒
from datetime import datetime
data = request.json
ts = data['created_at'] # 實際是 1745000000000
dt = datetime.fromtimestamp(ts) # 報錯:year out of range
# 修復方案1:前端轉成秒再發
# JavaScript: Math.floor(Date.now() / 1000)
# 修復方案2:後端判斷位數自動處理
if ts > 1e10: # 超過100億,說明是毫秒
ts = ts / 1000
dt = datetime.fromtimestamp(ts)
場景4:數據庫存的是秒,Node.js 讀出來當毫秒用
// 從數據庫讀出來的是秒
const row = await db.query('SELECT created_at FROM users WHERE id = 1');
const ts = row.created_at; // 1745000000,秒
// 直接用,結果顯示 1970 年
const date = new Date(ts); // 錯!
// 正確
const date = new Date(ts * 1000);
怎麼判斷手上的時間戳是秒還是毫秒?
方法1:看位數
2026年的秒級時間戳是10位(1700000000 到 1800000000之間),毫秒是13位。這個規律在2001年之後到2286年之前都成立。
方法2:寫一個檢測函數
function detectTimestamp(ts) {
if (ts > 1e12) return 'milliseconds';
if (ts > 1e9) return 'seconds';
return 'unknown';
}
// 統一轉成毫秒給 Date 用
function toDate(ts) {
return new Date(ts > 1e12 ? ts : ts * 1000);
}
def detect_timestamp(ts):
if ts > 1e12:
return 'milliseconds'
if ts > 1e9:
return 'seconds'
return 'unknown'
def to_datetime(ts):
from datetime import datetime, timezone
if ts > 1e12:
ts = ts / 1000
return datetime.fromtimestamp(ts, tz=timezone.utc)
根治這個問題的兩條原則
原則1:API 邊界用 ISO 8601 字符串,不傳裸時間戳
ISO 8601 格式是 2026-04-11T02:13:20Z,帶時區、精度明確、人類可讀,不存在秒/毫秒歧義。如果你能控制接口設計,優先用這種格式傳時間。
// 前端發
const payload = {
created_at: new Date().toISOString() // "2026-04-11T02:13:20.123Z"
};
// 後端收
from datetime import datetime
dt = datetime.fromisoformat("2026-04-11T02:13:20.123Z".replace('Z', '+00:00'))
原則2:如果必須傳時間戳,在接口文檔裏明確寫單位
在接口文檔裏寫清楚 created_at: Unix timestamp (seconds) 還是 (milliseconds),並且在入口處做校驗:
def validate_timestamp(ts: int, unit: str = 'seconds'):
if unit == 'seconds' and ts > 1e12:
raise ValueError(f"期望秒級時間戳,收到了疑似毫秒的值: {ts}")
if unit == 'milliseconds' and ts < 1e10:
raise ValueError(f"期望毫秒級時間戳,收到了疑似秒的值: {ts}")
遇到不確定的時間戳,粘到 時間戳在線轉換工具 裏,自動識別秒還是毫秒,直接顯示對應的日期,比自己心算省事。



加載中...