時間戳是什麼
打開數據庫,你可能會看到 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 小時。
最佳實踐:
- 存儲和傳輸統一用 UTC 時間戳,不要存帶時區的字符串
- 顯示給用戶時再轉換到用戶所在時區,轉換邏輯放在前端或明確指定時區
- 代碼裏永遠不要依賴系統默認時區,顯式傳入時區參數
# 錯誤示範:依賴系統時區
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 的時間戳在線轉換工具。支持秒級和毫秒級,支持全球時區切換,無需安裝任何軟件。



加載中...