土薯工具 Toolshu.com 登錄 用戶注冊

Unix時間戳完全指南:原理、時區陷阱與各語言互轉代碼

原創 作者:bhnw 於 2026-04-06 22:49 發佈 1次瀏覽 收藏 (0)

時間戳是什麼

打開數據庫,你可能會看到 created_at 字段存着 1712345678 這樣一串數字。這就是 Unix 時間戳——一個看上去毫無意義的整數,卻是計算機世界裏最通用的時間表示方式。

Unix 時間戳的定義:從 1970 年 1 月 1 日 00:00:00 UTC(協調世界時)到某一時刻所經過的秒數。

這個起點被稱爲 Unix 紀元(Unix Epoch),也叫"epoch time"。之所以選 1970 年,是因爲 Unix 操作系統誕生於 1969-1970 年前後,這個時間點對早期開發者來說足夠"現代",又方便計算。


爲什麼用時間戳,而不直接存日期字符串

"2024-04-06 10:30:00" 不香嗎?實際上在系統開發中,時間戳比日期字符串有幾個關鍵優勢:

1. 沒有時區歧義

"2024-04-06 10:30:00" 到底是北京時間還是紐約時間?不知道。而時間戳 1712374200 永遠只代表一個確定的時刻,不依賴任何時區。

2. 計算方便

兩個時間戳相減,直接得到秒數差。判斷某事件是否在一小時內:now - event_time < 3600,簡單明瞭。

3. 存儲高效

一個 32 位整數只需 4 字節,而日期字符串至少需要 19 字節。

4. 跨語言、跨平臺通用

所有編程語言、所有操作系統都支持 Unix 時間戳,不存在格式兼容問題。


秒 vs 毫秒:最常見的踩坑

時間戳有兩種精度,這是新手最容易搞混的地方:

精度 示例值 位數 常見場景
秒級(s) 1712374200 10位 Unix標準、數據庫、服務器日誌
毫秒級(ms) 1712374200000 13位 JavaScript、前端、高精度日誌

JavaScript 的 Date.now() 返回的是毫秒,這是很多 bug 的來源。把毫秒時間戳當秒傳給後端,後端會解析出一個幾萬年後的時間。

快速判斷方法:看位數,10位是秒,13位是毫秒。

// JavaScript 獲取秒級時間戳
Math.floor(Date.now() / 1000)  // 正確
Date.now()                      // 毫秒,注意區分
# Python 獲取時間戳
import time
time.time()        # 浮點數,秒級(含小數)
int(time.time())   # 整數秒

各語言獲取與轉換時間戳

JavaScript / Node.js

// 獲取當前時間戳(秒)
const ts = Math.floor(Date.now() / 1000);

// 時間戳轉日期
const date = new Date(ts * 1000);
console.log(date.toISOString()); // "2024-04-06T02:30:00.000Z"

// 日期轉時間戳
const ts2 = Math.floor(new Date("2024-04-06T10:30:00+08:00").getTime() / 1000);

Python

import time
from datetime import datetime, timezone

# 獲取當前時間戳(秒)
ts = int(time.time())

# 時間戳轉日期(UTC)
dt_utc = datetime.fromtimestamp(ts, tz=timezone.utc)
print(dt_utc.isoformat())  # "2024-04-06T02:30:00+00:00"

# 時間戳轉本地時間
dt_local = datetime.fromtimestamp(ts)
print(dt_local)  # 按系統時區顯示

# 日期字符串轉時間戳
dt = datetime.fromisoformat("2024-04-06T10:30:00+08:00")
ts2 = int(dt.timestamp())

Java

import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.ZoneId;

// 獲取當前時間戳(秒)
long ts = Instant.now().getEpochSecond();

// 時間戳轉日期
Instant instant = Instant.ofEpochSecond(ts);
ZonedDateTime zdt = instant.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(zdt); // 2024-04-06T10:30:00+08:00[Asia/Shanghai]

// 日期轉時間戳
ZonedDateTime zdt2 = ZonedDateTime.parse("2024-04-06T10:30:00+08:00");
long ts2 = zdt2.toEpochSecond();

Go

import (
    "fmt"
    "time"
)

// 獲取當前時間戳(秒)
ts := time.Now().Unix()

// 時間戳轉日期
t := time.Unix(ts, 0).UTC()
fmt.Println(t.Format(time.RFC3339)) // "2024-04-06T02:30:00Z"

// 日期轉時間戳
layout := "2006-01-02T15:04:05Z07:00"
t2, _ := time.Parse(layout, "2024-04-06T10:30:00+08:00")
ts2 := t2.Unix()

PHP

// 獲取當前時間戳(秒)
$ts = time();

// 時間戳轉日期
echo date('Y-m-d H:i:s', $ts);          // 按服務器時區
echo gmdate('Y-m-d H:i:s', $ts);        // UTC

// 日期轉時間戳
$ts2 = strtotime('2024-04-06 10:30:00'); // 按服務器時區

MySQL

-- 獲取當前時間戳
SELECT UNIX_TIMESTAMP();

-- 時間戳轉日期
SELECT FROM_UNIXTIME(1712374200);
SELECT FROM_UNIXTIME(1712374200, '%Y-%m-%d %H:%i:%s');

-- 日期轉時間戳
SELECT UNIX_TIMESTAMP('2024-04-06 10:30:00');

時區:最容易出錯的地方

時間戳本身是時區無關的,但把時間戳轉成人類可讀的日期時,必須指定時區。不指定,就用系統默認時區,這在服務器部署時是隱患:本地開發機可能是 CST(+8),服務器是 UTC(+0),同一個時間戳顯示出來差 8 小時。

最佳實踐:

  1. 存儲和傳輸統一用 UTC 時間戳,不要存帶時區的字符串
  2. 顯示給用戶時再轉換到用戶所在時區,轉換邏輯放在前端或明確指定時區
  3. 代碼裏永遠不要依賴系統默認時區,顯式傳入時區參數
# 錯誤示範:依賴系統時區
datetime.fromtimestamp(ts)  # 行爲取決於部署環境

# 正確做法:顯式指定時區
datetime.fromtimestamp(ts, tz=timezone.utc)

2038年問題

Unix 時間戳最初用 32 位有符號整數存儲,最大值是 2147483647,對應的時間是 2038年1月19日 03:14:07 UTC。超過這個時刻,32 位整數會溢出,變成負數,時間會回到 1901 年。

這就是 Y2K38 問題(2038年問題),類似於當年的千年蟲。

好消息是:大多數現代系統已經遷移到 64 位整數存儲時間戳,64 位時間戳能表示到約 2920 億年後,完全不用擔心。需要關注的是老舊系統、嵌入式設備,以及部分數據庫的 TIMESTAMP 類型(MySQL 的 TIMESTAMP 就有 2038 上限,DATETIME 沒有)。


在線工具

需要快速把一個時間戳轉成日期,或者把某個日期轉成時間戳,可以使用 toolshu.com 的時間戳在線轉換工具。支持秒級和毫秒級,支持全球時區切換,無需安裝任何軟件。

发现周边 发现周边
評論區

加載中...