Unix 時間戳是自 Unix 紀元(1970-01-01T00:00:00Z,UTC)以來所經過的秒數,而 JavaScript 則以自同一刻起算的毫秒數來表示同一個瞬間,這就是為什麼同一個數值在 JS 引擎中看起來會比在伺服器記錄檔、API 酬載或 SQL 資料列中大 1000 倍。這兩種表示法幾乎存在於網頁開發人員會接觸到的每個系統中,而它們之間的落差正是 JavaScript 程式碼中日期錯誤的最常見來源:new Date() 預期接收毫秒數,所以一個原始的 epoch 秒數會讓你停留在 1970 年加上零點幾秒;而把毫秒數誤當成秒數處理,則會把解析出來的日期拋到西元 55000 年左右。本指南將逐步說明將 Unix 時間戳轉換為 ISO 8601、UTC 和本地時間的確切 JavaScript 呼叫方式,接著再介紹反向轉回 epoch 數值的方法。每一步都小到可以直接放進程式碼片段中,而 Unix 時間戳轉換器讓你可以在瀏覽器中即時驗證任何數值。

how to convert unix timestamp to date in javascript
在 JavaScript 中將 Unix 時間戳轉換為日期

為什麼 JavaScript 使用毫秒而非秒

JavaScript 的 Date 物件是在 1990 年代中期設計的,目的是對應 Java 的 java.util.Date,後者以自 Unix 紀元以來經過的毫秒數(帶正負號的 64 位元整數)來儲存時間。這個設計決定從此定型:每個現代 JavaScript 引擎在內部仍然將時間視為 64 位元浮點數的毫秒數,這就是為什麼 Date.now() 會回傳毫秒數、為什麼 Date 建構函式會將單純的數字引數解讀為毫秒數、以及為什麼 Performance.now() 會回報次毫秒級的浮點毫秒數而非秒數。

根據 維基百科關於 Unix 時間的條目,Unix 時間本身的定義是自紀元以來的秒數。因此,每當 JavaScript 程式碼與伺服器通訊、解碼 JWT、讀取資料庫欄位,或是檢查使用秒數的記錄檔時,都必須在邊界處調和這兩個單位。調和的規則很短,值得記住:

  • 秒數轉 JavaScript:乘以 1000
  • JavaScript 轉秒數:除以 1000 並將結果無條件捨去

光是這一行規則就能解決你在 JavaScript 時間處理中會遇到的大部分 bug。剩下的 bug 則來自忘記某個數值究竟處於邊界的哪一側,或是把含糊的字串傳給 Date 建構函式。

在 JavaScript 中將 Unix 時間戳轉換為日期

在 JavaScript 中,從 Unix 時間戳到人類可讀日期的核心路徑分為簡短的五個步驟。依序執行它們,你就不會不小心掉進 1970 年或西元 55000 年。

  1. 先確定你手上的單位是什麼。2001 年 9 月之後任何日期的 Unix 秒數時間戳都是 10 位數;而同一個瞬間的 JavaScript 毫秒時間戳則是 13 位數。在寫任何程式碼之前,先數一下位數。
  2. 如果你的數值是秒數,請在建構 Date 之前先轉換成毫秒:const ms = unixSeconds * 1000;。
  3. 使用該毫秒值建構 Date 物件:const d = new Date(ms);。將單純的數字傳給 Date 建構函式,是取得完全可控且與時區無關輸入的唯一方式。
  4. 選擇輸出格式。若要 ISO 8601 格式,呼叫 d.toISOString();若要易讀的 UTC 字串,呼叫 d.toUTCString();若要裝置的本地時區,呼叫 d.toLocaleString()。
  5. 將原始的秒數值貼到 Unix 時間戳轉換器中,並把它的 ISO 8601 輸出與你的腳本產生的結果進行比對,以此驗證結果。

以下是完整的範例,使用單一的秒數值並驗算一次算術。取自紀元以來的 1700000000 秒。乘以 1000 轉換為 JavaScript 毫秒數:1700000000 × 1000 = 1,700,000,000,000 毫秒。將其傳入 new Date(),接著呼叫字串方法會得到:

  • d.toISOString() → "2023-11-14T22:13:20.000Z"
  • d.toUTCString() → "Tue, 14 Nov 2023 22:13:20 GMT"
  • d.toLocaleString() → 一個取決於裝置時區的值,例如 "11/14/2023, 10:13:20 PM"

這三種輸出描述的是同一個瞬間,只是表達方式不同。ISO 字串可攜性最高;UTC 字串在跨團隊的記錄檔中最易讀;而本地化字串則適合顯示在使用者期望看到自己時鐘的 UI 上。

並排比較四種輸出檢視

Unix 時間戳轉換器會對同一個瞬間產生四種檢視,每一種都恰好對應到一個 JavaScript 方法。下表將它們並列,讓你在需要某種格式時能清楚知道該呼叫哪個方法。

檢視 JavaScript 呼叫 時區 1700000000 的範例
ISO 8601 d.toISOString() UTC(永遠) 2023-11-14T22:13:20.000Z
UTC 字串 d.toUTCString() UTC(永遠) Tue, 14 Nov 2023 22:13:20 GMT
本地時間 d.toLocaleString() 裝置時區 依裝置而異
相對時間 (與 Date.now() 比較計算) 裝置時區 約 2 年前

在排序、儲存以及傳送到後端方面,ISO 8601 是首選,因為每個消費者都以相同方式解析它。在 UI 中給人閱讀時,本地時間是讀者所期待的,因為它顯示的是他們自己的時鐘時間。在稽核軌跡、記錄檔,以及任何需要讓兩個不同城市的開發人員逐字比對數值的地方,UTC 字串能讓所有人保持一致。相對時間則最適合用於狀態動態訊息和通知文案,在那些情境中「3 小時前」比完整時間戳更有意義。

反向操作:將日期轉回 Unix 時間戳

同樣常見的情況是你需要反向處理——把 ISO 8601 字串、記錄檔的一行,或是使用者輸入的日期,轉成你的 API 或資料庫所需要的原始 epoch 數值。

  1. 使用 new Date(dateString) 解析輸入。可靠的輸入包括 ISO 8601 字串(例如 "2023-11-14T22:13:20Z")、RFC 2822 字串,以及少數幾種引擎可辨識的格式。
  2. 呼叫 .getTime() 來擷取自紀元以來的毫秒數(以數值形式回傳)。
  3. 如果目標 API 預期接收秒數,則將結果除以 1000 並無條件捨去:Math.floor(d.getTime() / 1000)。
  4. 如果需要同時擁有兩者,請將兩個數值都保留在作用域中:const ms = d.getTime(); const s = Math.floor(ms / 1000);。

有兩個陷阱值得特別提醒。第一,new Date("2023-11-14") 會被解析為 UTC 凌晨零時,但 new Date(2023, 10, 14) 則會被解析為本地凌晨零時——相同的數字,卻是兩個不同的瞬間。當你需要 UTC 時,請傳入帶有明確 Z 後綴的 ISO 字串。第二,在任何需要上線的程式碼中,請避免對 Date 建構函式使用兩位數年份、省略部分元件,或是像 "11/14/23" 這類與地區相關的字串:各引擎對這些輸入的處理方式不一致,而失敗模式是靜默無聲的。

秒與毫秒,以及 2038 年問題

有兩個危險值得獨立成節來說,因為它們是程式碼審查後仍存活下來的大部分時間戳 bug 的元凶。

第一個是秒與毫秒的混淆。目前以秒表示的 Unix 時間戳是一個 10 位數的數字,例如 1700000000。同一個瞬間以 JavaScript 毫秒表示則是 13 位數的數字,例如 1700000000000。一旦混淆,你的「未來日期」會落在 1970 年,或者日期解析器會一路走到西元 55000 年。一個小型的輔助函式可以彌補這個落差:將任何大於 10^12 的數值視為毫秒,而任何小於的則視為秒數,這也是 Unix 時間戳轉換器在貼上時用來自動偵測單位的同一種啟發式:

function toMs(value) { return value > 1e12 ? value : value * 1000; }

第二個危險是 2038 年問題。以帶正負號的 32 位元整數儲存的 Unix 時間,會在 2038 年 1 月 19 日 03:14:07 UTC 發生溢位,繞回到 1901 年。這個 bug 仍然潛伏在舊式的嵌入式韌體以及少數較舊的資料庫系統中,這也是為什麼值得在信任原始時間戳之前,先肉眼檢查它實際代表的意義。然而,現代 JavaScript 引擎以 64 位元雙精度浮點數儲存時間,能夠表示遠至太陽滅亡之後的日期,因此用戶端程式碼並不會受到影響——但來自 32 位元伺服器時鐘的時間戳,仍可能在傳輸過程中發生迴繞或出現錯誤。

在瀏覽器中測試任何時間戳

當你只需要知道某個原始數值代表什麼——例如從 JWT 抽出的 exp 聲明、從查詢結果複製的資料庫欄位,或是伺服器記錄檔的一行——將它貼到 Unix 時間戳轉換器中。該工具會讀取位數,如果猜錯了還可讓你透過「秒/毫秒」選擇器手動覆寫單位,並同時顯示四種檢視,讓你能將結果與 JavaScript 程式碼產生的結果進行合理性檢查。每次轉換都透過平台 Date 物件與明確的 UTC 格式化在本地執行,因此不會上傳任何資料,你的資料永遠不會離開這個頁面。

對於伺服器端處理同類型數值的轉換,如何在 SQL 查詢中轉換 Unix 時間戳一文涵蓋了 MySQL 中的 FROM_UNIXTIME 與 UNIX_TIMESTAMP 呼叫,以及 SQL Server 中等效的 DATEADD / DATEDIFF 模式,讓 JavaScript 與資料庫層之間的界線轉換不必再從頭重新推導。