跳到主要內容
楓葉工具箱 ToolMaple

開發最後更新:

Unix 時間戳轉換

時間戳和日期互轉,秒和毫秒會自動認。

目前時間戳

時間戳 → 日期

日期 → 時間戳

這個時間是

什麼是 Unix 時間戳?你會在哪裡碰到它

Unix 時間戳(Unix timestamp / epoch time)是「從 1970-01-01 00:00:00 UTC 起算經過的秒數」,又稱POSIX 時間。1970 年由貝爾實驗室 Ken Thompson、Dennis Ritchie 等人設計 Unix 時選定為時間原點,沿用至今。後端、API、資料庫、log 檔最常用這種表示法,因為它與時區無關(永遠以 UTC 起算),整數比對快,儲存只要 4 或 8 個 bytes, 完全沒有「2026-06-22 是哪個格式」這種解析歧義。

你通常是在這些地方被迫跟它打交道:

  • JWT token:iat(簽發時間)、exp(過期時間)、nbf(生效時間)都用 Unix 秒
  • API response 時間欄位:許多 REST / GraphQL API 回傳 created_at: 1700000000 而非 ISO 字串,節省 byte
  • Server log 解析:log 行首常以時間戳開頭,貼進來換成可讀時間
  • Cookie / Cache TTL 計算:算「現在 + 3600 秒」是哪個時間點
  • Cron job 排程驗證:debug 排程時對照 server 時鐘是否正確
  • 檔名時間戳:backup-1700000000.tar.gz 這種命名,轉回人類時間

手上的數字是什麼單位?要帶走哪一列結果?

用這頁只要決定兩件事:貼進來的數字是什麼單位,以及你要把哪一種寫法帶出去。單位照位數認:

  • 秒(10 位數):Unix 系統呼叫、PHP time()、Gotime.Now().Unix()、Linux date +%s、JWT 預設
  • 毫秒(13 位數):JavaScript Date.now()、JavaSystem.currentTimeMillis()、Kotlin、Android
  • 微秒 / 奈秒(16 / 19 位數):PostgreSQL、Go 的 UnixNano()、高精度 tracing 工具(OpenTelemetry)會用
  • 本工具自動偵測長度,不必手動切換,也不用先自己除以 1,000

自動偵測的規則很單純:10 位以下當秒、13 到 15 位當毫秒、16 到 18 位當微秒、19 位以上當奈秒;11 到 12 位這種曖昧長度不猜,維持你原本選的單位。任何時候都可以用「單位」選單手動改回來。19 位的奈秒已經超過 JavaScript 數字能精確表示的範圍,這頁改用整數運算換算,ISO 那列會把 6 位或 9 位小數完整印出來,不會掉尾數。

常見坑:把毫秒當秒丟給 new Date(ts) 在 JavaScript 中只會差 1000 倍,看起來像 「年份是 55000+」這種離譜結果。看到 13 位數時記得確認一下。

結果那幾列則是看「接下來要給誰看」。本地是你這台機器的時鐘,適合自己核對事件對不對得上記憶;UTC是丟進事故頻道或工單時該引用的那列,因為只有它兩個不同時區的人不會吵;ISO是貼進程式碼、設定檔或試算表的那列,結尾的 Z 把時差寫得清清楚楚,現代的解析器都吃得下。最後一列固定是台灣時間與民國年,不管你的電腦設在哪個時區都照台灣算:連到國外的遠端桌面、設成 UTC 的開發機,或人在國外查台灣的訂單時間,都不用再自己加 8 小時。要更完整的取捨,看下面這組對照:

  • Unix 時間戳:例 1750579200。優:整數、快、無歧義;劣:人類讀不出來,需工具轉換
  • ISO 8601:例 2026-06-22T10:00:00+08:00。優:人類可讀、含時區資訊;劣:字串較長,需解析
  • 實務:機器之間(資料庫、API、log)用 Unix; UI、設定檔、文件,跟人溝通時用 ISO 8601

實戰教學:把 log 裡的時間戳讀成看得懂的時間

  1. 只複製數字本身。前面的欄位名稱、後面的逗號與引號都會讓它解析不出來;帶小數點的形式(例如 1735689600.482)可以直接留著,小數部分就是秒。
  2. 貼進「輸入時間戳」欄位。單位會依位數自動切換,10 位以下當秒、13 位當毫秒、16 位當微秒、19 位當奈秒;11 到 12 位不會自動判斷,請自己用「單位」選單指定。
  3. 看結果那幾列。年份如果離譜(例如五萬多年後),八成是單位認錯,把「單位」切過去再看一次。
  4. 寫給別人看時引用 UTC 那列;要引用本地那列就把時區一起寫上,不然下一個人還是得再問一次。

數字是從 API 的 JSON 裡挖出來的,可以先用JSON 格式化工具把 payload 排版再找;是從 bearer token 裡看到的,JWT 解碼器會直接把 exp換算好,那通常才是你真正想問的問題。

實戰教學:把日期轉成查詢要用的時間戳

  1. 用下半部的「輸入日期時間」欄位。那是瀏覽器原生控制項,日期順序與 12 或 24 小時制跟著你的作業系統走。
  2. 在「這個時間是」選你填的是哪裡的時間。預設是本地時間,也就是你這台電腦的時區;手上是 log 或 API 文件裡的 UTC 時間就選 UTC,照字面填 00:00 就好,不用先自己加 8 小時;電腦不在台灣時區、但你要的是台灣的時間,就選台灣時間。其他國家的牆上時間先用時區轉換工具對好再回來填。
  3. 複製你要的那一列。秒是 shell 指令、PostgreSQL 的 to_timestamp() 或 JWT claim 要的;毫秒是 JavaScript 的 Date 或 Android API 要的。
  4. 要算一段區間就兩端各轉一次再相減。一天是 86,400 秒、一小時是 3,600、一週是 604,800。差值如果不是這些數字的整數倍而你以為應該是,通常是區間裡夾了日光節約時間的切換。台灣沒有日光節約,但你查的伺服器或對方的資料可能有。

把數字帶到要用的地方

頁面最上方的大數字是目前的秒級時間戳,每秒更新一次,點一下就複製;日期那一段的兩顆按鈕分別複製秒與毫秒。接下來要貼到哪裡,決定還要不要多做一步:

  • 貼進終端機:純數字,不用加引號。要做「一小時後」的測試資料,在 shell 裡直接算比複製兩次快:echo $(($(date +%s) + 3600))。
  • 貼進 SQL:PostgreSQL 用 to_timestamp()、MySQL 用 FROM_UNIXTIME() 把秒數包起來,不要拿字串去比對,這樣索引才用得到。
  • 貼進 Google 試算表或 Excel:epoch 秒數對它們來說不是日期,要用 =A1/86400+DATE(1970,1,1) 換算後再把儲存格格式設成日期。算出來是 UTC,要看台灣時間就改成 =A1/86400+DATE(1970,1,1)+8/24。
  • 貼進工單或聊天室:貼 ISO 那列而不是整數。半年後回頭看工單的人不會幫你換算,Slack、Jira、GitHub 也都只會把裸數字原樣留著。

同一件事在各語言與資料庫怎麼寫

每個執行環境兩個方向都做得到,唯一的陷阱是它回你什麼單位。JavaScript 是那個異類,從頭到尾都是毫秒:

// JavaScript:Date 一律是毫秒
Date.now()                          // 1735689600000
Math.floor(Date.now() / 1000)       // 1735689600
new Date(1735689600 * 1000).toISOString()
// '2025-01-01T00:00:00.000Z'
# Python:秒,而且是浮點數
import time
from datetime import datetime, timezone
time.time()                                       # 1735689600.482
datetime.fromtimestamp(1735689600, tz=timezone.utc)
# datetime(2025, 1, 1, 0, 0, tzinfo=timezone.utc)
int(datetime.now(timezone.utc).timestamp())       # 1735689600
  • Go:秒是 time.Now().Unix(),更細的單位有 UnixMilli() 與 UnixNano(),轉回去用 time.Unix(sec, 0)。
  • Java:System.currentTimeMillis() 給的是毫秒,Instant.now().getEpochSecond() 給的是秒。同一個專案兩種混用,就是那個「差 1000 倍」的 bug 來源。
  • PHP:time() 與 date('c', $ts)。Ruby:Time.now.to_i 與 Time.at(ts)。.NET:DateTimeOffset.UtcNow.ToUnixTimeSeconds()。
  • Shell:date +%s 在 Linux 與 macOS 都通,但反過來就不通了:Linux 的 GNU coreutils 要 date -d @1735689600,macOS 與 BSD 要 date -r 1735689600。
-- PostgreSQL
SELECT to_timestamp(1735689600);            -- timestamptz
SELECT EXTRACT(EPOCH FROM now())::bigint;

-- MySQL(依連線的時區設定算)
SELECT FROM_UNIXTIME(1735689600), UNIX_TIMESTAMP();

-- SQLite
SELECT datetime(1735689600, 'unixepoch');
SELECT strftime('%s', 'now');

設計 schema 之前有兩個平台細節值得先知道。MySQL 的 FROM_UNIXTIME() 是照連線的時區設定輸出的,所以台灣的機器設成 Asia/Taipei、CI 的容器留在 UTC 時,同一句查詢在兩邊會回不同的字串;而 MySQL 的 TIMESTAMP 欄位型別,官方文件寫明範圍到 2038-01-19 03:14:07 UTC 為止,也就是下面要談的 32-bit 上限。JWT 這邊,RFC 7519 把 exp、iat、nbf 定義成 NumericDate,意思是秒、永遠不是毫秒;用 Date.now() 而不是 Date.now() / 1000 簽出來的 token,看起來會是五萬 年後才過期。

限制、邊界情況與這頁不做的事

什麼都不會離開你的瀏覽器。所有換算都是在本機用 JavaScript 內建的 Date 物件跑的,上方的即時時鐘讀的是你自己的裝置而不是時間伺服器。貼進來的東西不會上傳、不會儲存、不會寫進紀錄,所以直接貼 production log 裡的時間戳是安全的;分析只記錄這一頁被看過。

2038 年問題(Y2K38)。32-bit 有號整數的最大值是 2,147,483,647,對應到 Unix 時間就是 2038-01-19 03:14:07 UTC,再加 1 秒就溢位變成負數(1901 年)。這就是「Y2K38 問題」。

現代主流系統(Linux 5.6+、macOS、Windows、64-bit 手機)都已改用 64-bit 整數,可撐到約西元 2920 億年,人類滅絕都不會遇到。 但仍存在風險的是:嵌入式裝置(IoT、車載系統、舊 router)、舊版資料庫 schema 用 INT(11) 而非 BIGINT,以及 2038 年之前未更新的老舊軟體。 本工具用 JavaScript 的 Date,最遠到西元 275760 年。

「本地」就是你這台機器,連同它所有的設定。本地那一列與日期欄位讀的都是作業系統回報的時鐘與時區。機器的時區設錯,或是掛了 VPN 但時鐘沒跟著改,本地讀數就會跟看著同一個整數的同事對不起來。UTC 那列不會有這個問題,這也是該引用它的理由。

精度有下限。小數秒、微秒與奈秒都收得下,ISO 那列會照輸入的精度印到 3、6 或 9 位小數,但本地、UTC 與台灣三列為了好讀截到整秒。要比較相差幾毫秒的事件,請直接比原始數值或 ISO 那列,不要比截過的字串。

閏秒不在裡面。POSIX 把每天定義成剛好 86,400 秒,所以 1972 年以來插進 UTC 的 27 次閏秒是被跳過而不是被計入。一般應用不受影響;真的要量精確間隔,本來就該用單調時鐘。

常見問題

時間戳要不要含時區?

Unix 時間戳本質就是 UTC 秒數,不需要也不能附時區。需要顯示給某地區使用者時,再依該使用者的時區把秒數轉成本地時間。這種「儲存 UTC、顯示時轉本地」是業界標準做法。

負數時間戳代表什麼?

1970-01-01 之前的時間。例如 -1000000000 是 1938-04-24。多數現代語言支援,但古老 C 函式庫或某些資料庫可能不接受。

為什麼我從資料庫拿到的時間是 1970?

通常是欄位被填了 0(或 NULL 被轉成 0)導致顯示成 epoch 起點。檢查資料寫入邏輯,確認時間欄位有沒有正確寫入。

我手上這個數字是秒還是毫秒?

看它有幾位數。10 位是秒,涵蓋 2001 年 9 月到 2286 年 11 月;13 位是毫秒,同一段期間,JavaScript 的 Date.now() 與 Java 的 System.currentTimeMillis() 回的就是這個。16 位是微秒(PostgreSQL 內部與不少 tracing 系統用),19 位是奈秒(Go 的 UnixNano 與 OpenTelemetry 常見)。本頁會依位數自動判斷這四種單位,微秒與奈秒不用先自己換算,直接貼進來就好。

Unix 時間戳會把閏秒算進去嗎?

不會。POSIX 把每一天定義成剛好 86,400 秒,所以插進 UTC 的閏秒是被跳過而不是被計入。1972 年以來 UTC 一共插了 27 次閏秒,最後一次在 2016 年底,因此 Unix 時間戳並不是「自 epoch 起實際經過的秒數」。這對一般應用沒有影響;真的要量精確間隔,本來就該用單調時鐘(monotonic clock)。

怎麼在終端機拿到目前的 Unix 時間戳?

macOS 與 Linux 都可以用 date +%s,印出來是秒。反過來從時間戳轉日期兩邊不一樣:Linux 的 GNU coreutils 是 date -d @1735689600,macOS 與其他 BSD 系統是 date -r 1735689600。要毫秒的話,Linux 可以用 date +%s%3N;macOS 內建的 date 沒有毫秒格式,改用 python3 或 node 比較快。

資料來源

  • MDN 的 Date 文件:毫秒 epoch 與上面提到的可表示範圍。
  • IETF RFC 3339:第三列 ISO 8601 採用的網際網路寫法。
  • IETF RFC 7519(JSON Web Token):NumericDate 的定義,也就是 exp 與 iat 為什麼是秒。
  • IANA Time Zone Database:瀏覽器套在本地那列上的時區規則。
  • 「每天 86,400 秒」來自 Open Group Base Specifications 對 Seconds Since the Epoch 的定義;閏秒次數由 IERS 公布。資料庫行為引自各家官方手冊,不同版本可能不同。

更多開發者工具

數字是從 token 裡看到的,JWT 解碼器會把過期時間直接換算好;是從 API 回應裡看到的,先用JSON 格式化工具把 payload 排開比較好找。要對的是各地的牆上時間,時區轉換工具更合用。

相關工具

隱私:時鐘與所有換算都在你的瀏覽器裡用 JavaScript 內建的 Date 物件完成,貼進來的數字不會上傳、不會儲存、也不會寫進紀錄;頁面載入後就算斷網也照常運作,分析只記錄這一頁被看過。