Unix 時間戳是自 1970-01-01T00:00:00Z (UTC) 起所經過的秒數整數值,SQL 資料庫通常會直接以這個原始數字來儲存、比較和篩選資料列。當這個整數出現在欄位、查詢結果或日誌行中時,您通常會想將它轉回人類可閱讀的日期,以便進行除錯、報表或檢查實際儲存的內容。每個主流資料庫引擎都提供雙向轉換的函式 —— MySQL 提供 UNIX_TIMESTAMP() 與 FROM_UNIXTIME(),PostgreSQL 使用 EXTRACT(EPOCH FROM …) 與 TO_TIMESTAMP(),而 SQL Server 則透過 DATEDIFF_BIG 與 DATEADD 以 epoch 錨點來表達相同概念。各家語法不同,單位也有些微差異,選錯正是讓一整欄完全有效的時間戳看起來像落在西元 55000 年的經典原因。本指南將逐步說明在每個引擎中運作的 SQL 模式,點出容易讓開發人員措手不及的單位陷阱,並展示在您只想快速讀取值時,基於瀏覽器的 Unix 時間戳轉換器 如何派上用場。

how to convert unix timestamp in sql
how to convert unix timestamp in sql

SQL 資料中的 Unix 時間戳從何而來

如今大多數 SQL 平台偏好使用原生的 DATETIME 或 TIMESTAMP 欄位型別來儲存時間點,但原始的 epoch 整數仍然非常普遍。伺服器端框架經常將 API 請求時間、JWT 過期聲明、訊息佇列發布時間及稽核事件,以 BIGINT 欄位儲存,來源包含 Java 的 System.currentTimeMillis()、Python 的 time.time(),或 JavaScript 的 Date.now()。將 JSON 日誌扁平化載入資料倉儲的 ETL 工作,經常會將 epoch 欄位直接轉型為數值欄位而不重新格式化。您也會在快取失效欄位、排程任務的下次執行欄位,以及需要跨數百萬列達到毫秒精度的分析事件表中看到原始整數。

因為整數只是計數,既精簡、又可排序且不含歧義,這就是資料庫能妥善處理它的原因。缺點是像 1699999999 這樣的值,在您知道它代表 2023-11-14T22:13:19Z 之前,看起來只是一串毫無意義的十位數字。辨識這個模式 —— 一整欄無明顯意義的大整數 —— 是寫出您真正需要的轉換查詢的第一步。

Unix 時間戳轉換的內建 SQL 函式

每個主要引擎透過不同的成對函式來處理轉換,且每對函式各有不同的預設單位。準確知道您的平台需要哪個函式,以及它會回傳什麼,可以避免大多數 SQL 時間戳工作中常見的混淆。

資料庫引擎 數值 → epoch Epoch → 數值 預設單位
MySQL / MariaDB UNIX_TIMESTAMP(date) FROM_UNIXTIME(seconds) 秒
PostgreSQL EXTRACT(EPOCH FROM timestamptz) TO_TIMESTAMP(seconds) 秒 (浮點數)
SQL Server DATEDIFF_BIG(SECOND, '1970-01-01', dt) DATEADD(SECOND, ts, '1970-01-01') 依選擇為秒或毫秒
SQLite strftime('%s', col) datetime(col, 'unixepoch') 秒

MySQL 的成對函式最符合人體工學,因為函式名稱與慣例一致:UNIX_TIMESTAMP() 讀取日期並回傳秒數,而 FROM_UNIXTIME() 反向操作,並可接受格式字串,讓您將輸出格式化為特定形狀,例如 '%Y-%m-%d %H:%i:%s'。PostgreSQL 的成對函式使用 EXTRACT 關鍵字處理整數,TO_TIMESTAMP 處理浮點秒數,當您的欄位已含有次秒精度且想加以保留時,這格外方便。SQL Server 沒有專屬成對函式,因此您需要從基本函式組合出轉換:DATEDIFF_BIG 測量到 epoch 錨點的距離,而 DATEADD 則依相同量向前推進。SQLite 則完全透過其 strftime 與 datetime 修飾符處理,並以字面字串 'unixepoch' 標示應將哪個欄位讀為自 1970 年起的秒數。

如何在 SQL 中轉換 Unix 時間戳

確切的查詢取決於您的引擎,但每種情況的工作流程完全相同。每當您面對一整欄整數並需要將其轉為可讀日期時,請依循以下步驟。

  1. 確認欄位的宣告型別以及您所儲存的單位。打開資料表定義,查看欄位是 INTEGER、BIGINT 還是 NUMERIC。10 位數的值強烈暗示為秒;13 位數的值強烈暗示為毫秒。請取樣一列來加以確認。
  2. 挑選符合您引擎的轉換函式。在 MySQL 中使用 FROM_UNIXTIME(),在 PostgreSQL 中使用 TO_TIMESTAMP(),在 SQL Server 中使用 DATEADD(SECOND, @ts, '1970-01-01'),或在 SQLite 中使用 datetime(col, 'unixepoch')。
  3. 在呼叫函式前先統一單位。若欄位儲存的是毫秒而函式預期是秒,請先除以 1000:MySQL 中為 FROM_UNIXTIME(ts / 1000),PostgreSQL 中為 TO_TIMESTAMP(ts::double precision / 1000)。
  4. 將結果轉型為字串或您的報表型別。依引擎使用 DATE_FORMAT、TO_CHAR 或 FORMAT 包住函式,使輸出為人類可讀,而不是原始的內部 datetime。
  5. 對照已知錨點進行驗證。以您已知的值 —— 例如 1700000000 —— 執行查詢,確認引擎回傳 2023-11-14T22:13:20Z,再將輸出套用至資料表的其他部分。

使用最常見模式的 MySQL 範例如下:

SELECT id, FROM_UNIXTIME(created_at) AS created_at_human FROM events LIMIT 10;

若您的欄位實際儲存的是毫秒,請先除以單位,讓函式接收到的是秒:

SELECT id, FROM_UNIXTIME(created_at / 1000) AS created_at_human FROM events LIMIT 10;

PostgreSQL 採用相同的形式但使用 TO_TIMESTAMP,其接受 double-precision 引數,因此當小數秒數重要時可加以保留:

SELECT id, TO_TIMESTAMP(created_at::double precision / 1000) AT TIME ZONE 'UTC' AS created_at_human FROM events LIMIT 10;

SQL Server 透過將 epoch 計數加到 epoch 錨點日期來達到相同結果:

SELECT id, DATEADD(SECOND, created_at, '1970-01-01') AS created_at_human FROM dbo.events;

在任何引擎中,當您看到日期落在西元 55000 年附近或鎖定在 1970 年時,就代表單位錯誤,此時除以 1000 —— 或視方向乘以 1000 —— 即可修正。

在瀏覽器中以視覺方式轉換 Unix 時間戳

SQL 函式非常適合用於正式環境的查詢,但大多數除錯時刻都是臨時性的:您從查詢結果中複製單一整數、貼到對話視窗中,或盯著一行日誌想知道它實際代表什麼時間。這正是 Unix 時間戳轉換器的任務 —— 它完全在瀏覽器中執行,並將任何 epoch 值一次轉換為四種可讀形式。

  1. 將 Unix 值貼到「Timestamp → Date」欄位,並選擇其單位為秒或毫秒。
  2. 讀取結果:ISO 8601 與 UTC 與時區無關,「Local time」(本地時間)反映您裝置的時區,而「Relative」(相對時間)則顯示與現在的距離。
  3. 若要反向操作,請在「Date → Timestamp」欄位中輸入或貼上日期 —— 例如 2023-11-14T22:13:20Z —— 即可取得以秒和毫秒表示的 epoch 值。

此工具會依據位數自動偵測單位,因此 10 位數會被視為秒,13 位數則被視為毫秒;若您知道欄位情況特殊,仍可使用秒/毫秒選擇器手動覆寫單位。每次轉換皆透過平台 Date 物件與明確的 UTC 格式在本地端運算,代表原始值不會離開頁面 —— 當該整數來自您不想上傳到任何地方的正式環境資料時,這項特性非常實用。

秒與毫秒:SQL 最常見的陷阱

SQL 中處理 Unix 時間戳時,最昂貴的單一錯誤,就是忘記 JavaScript、Java 及多數現代 API 以毫秒表示時間,而底層的 Unix 慣例則使用秒。這兩種數字相差 1000 倍,因此一個以毫秒儲存的有效 epoch 值 1699999999000,將會被 FROM_UNIXTIME() 讀為西元 55831 年左右 —— 約 Unix epoch 之後的 53,861 年。兩個習慣可以避免大多數事故發生。第一,清楚命名欄位 —— created_at_epoch_ms 能讓團隊一眼看出應預期的單位。第二,在邊界處統一單位:資料進入資料表的當下轉為秒,或資料離開的當下轉為毫秒,但絕不在查詢中混用兩者而未加上明確的 CAST。

若您接手了一個單位不清的資料表,單一診斷查詢通常就能釐清問題。對該欄位執行 MAX(ts):落在數十億的值幾乎可確定為秒,接近十兆的值則為毫秒。介於兩者之間的值,強烈暗示該資料表混合了單位,這是在進行任何報表工作前值得先行修正的問題。多數引擎也允許您將已知時間戳透過轉換函式進行比對 —— SELECT ts FROM events WHERE FROM_UNIXTIME(ts) = '2023-11-14 22:13:20' —— 以確認特定資料列是否解析為您預期的日期。

Practical Tips and Debugging Habits

A few small habits save a lot of time when timestamps and SQL collide. Keep the epoch anchor in a comment at the top of every migration so the next developer sees the assumption on sight. Treat the FROM_UNIXTIME() result as UTC by default and only convert to a local zone at the presentation layer, since the database has no business knowing which city the analyst sits in. When writing WHERE clauses against epoch columns, always pass the bound through UNIX_TIMESTAMP() on the right side, never against a magic integer that someone hand-calculated from a calendar.

Unix-style epoch values also leak into adjacent formats you may eventually meet in SQL. ULIDs, for instance, embed a 48-bit millisecond timestamp at the front of the identifier, so reading the prefix gives you a usable Unix timestamp in milliseconds; if you start seeing those in your warehouse, the Decode a ULID Timestamp guide walks through the bit-level extraction. For the underlying definition of Unix time and the formal rules around the 32-bit overflow, the Unix time entry on Wikipedia is the most reliable starting point.

Finally, remember that the Year 2038 problem is not a theoretical curiosity: it is a real bug waiting in any signed 32-bit INTEGER column whose value crosses 2147483647 on 19 January 2038 at 03:14:07 UTC. Modern databases default to 64-bit types and are unaffected, but legacy schemas, embedded devices, and some older ORM mappings still store timestamps in 32-bit fields. Spotting that column type before it ships to production is one of the cheapest wins available, and a Unix Timestamp Converter gives you a way to read any raw value in plain English long before the rollover date arrives.

If you're weighing options, How to Parse Query Params Without Breaking Signatures covers this in detail.