基础知识

Unix 时间戳完全指南:从 Epoch 到毫秒精度的演进

📅 2026-07-19 ⏱️ 阅读约 10 分钟

如果你写过代码或者看过服务器日志,一定见过类似 1704067200 这样的数字。这就是 Unix 时间戳(Unix Timestamp)——一种用纯数字表示时间的方式。它看起来简单,但背后有不少值得了解的细节:为什么从1970年开始算?10位和13位到底差在哪?32位系统为什么在2038年会出问题?本文把这些事一次性说清楚。

一、Unix 时间戳的起源:为什么是1970年?

Unix 时间戳的起点是 1970年1月1日 00:00:00 UTC,这个时刻被称为 Unix Epoch(Unix 纪元)。从这一刻起,每过一秒,时间戳就加1。

选1970年并不是因为它有什么特殊的历史意义,纯粹是因为 Unix 操作系统在1969到1971年间开发,开发者需要一个足够早且方便记忆的整数起点。1970年1月1日零点刚好是个整点,而且在此之前计算机系统尚未大规模普及,不用担心负时间戳的历史数据问题。

在早期的 Unix 系统中,时间戳用 32 位有符号整数存储,以秒为单位。这意味着它能表示的最大值是 2^31 - 1 = 2147483647,对应的时间是 2038年1月19日 03:14:07 UTC。这就是著名的 Y2K38(2038年问题),后面会详细讲。

二、10位 vs 13位:秒级与毫秒级的区别

在实际开发中,你会遇到两种常见的 Unix 时间戳格式:10位数字和13位数字。它们的区别很直接:

10位时间戳(秒级)

为单位,是传统 Unix 系统的标准格式。例如 1704067200 表示从1970年1月1日零点起经过了约17亿秒。这种格式在 Linux 系统命令、MySQL 的 UNIX_TIMESTAMP() 函数、PHP 的 time() 函数中广泛使用。

优点是存储空间小,一个 32 位整数就能装下;缺点是精度只到秒,无法表示秒以下的时间(如毫秒、微秒)。

13位时间戳(毫秒级)

毫秒为单位,是现代前端开发(尤其是 JavaScript)的事实标准。JavaScript 的 Date.now() 返回的就是13位数字,例如 1704067200000。末尾三位表示毫秒精度。

优点是精度高,适合需要精确计时的场景(如动画帧率、接口响应耗时、高并发订单排序);缺点是需要 64 位整数存储,占用空间更大。

💡 快速换算:13位时间戳 ÷ 1000 = 10位时间戳。如果你拿到一串数字不确定是哪种,先看位数:10位左右是秒,13位左右是毫秒。

三、2038年问题:32位时间戳的末日

这是 Unix 时间戳历史上最著名的漏洞之一。在 32 位系统中,时间戳通常用有符号 32 位整数存储,最大值是 2147483647

按秒计算,这个值对应的时间是:

当时间超过这个点后,32 位整数会溢出变成负数,系统时间突然跳回 1901年12月13日。这会导致依赖时间戳的程序出现严重故障:证书验证失败、定时任务错乱、日志时间倒转、数据库排序异常等。

哪些系统会受影响?

理论上,所有使用 32 位有符号整数存储秒级时间戳的软硬件都可能受影响,包括:

解决方案

最简单的解决办法就是把存储类型从 32 位升级到 64 位。64 位有符号整数能表示到 2920亿年左右,对人类来说基本等于无限。现代 Linux 内核、主流编程语言和数据库早已完成迁移,但如果你维护的是老旧的嵌入式系统或遗留代码,这确实是个需要提前排查的隐患。

⚠️ 注意:2038年问题只影响秒级的 32 位存储。如果你使用的是 64 位整数或毫秒级时间戳,基本不用担心这个问题。

四、现代开发为什么推荐毫秒级?

虽然秒级时间戳更省空间,但现代开发中越来越多的场景需要毫秒甚至微秒精度。以下是几个典型例子:

当然,如果你只是记录用户的注册时间、文章的发布日期,秒级精度完全够用,没必要为了毫秒而牺牲存储空间。关键是根据业务场景选择合适的精度。

五、时区:Unix 时间戳本身没有时区

这是一个非常容易混淆的点。Unix 时间戳本身是绝对时间,与时区无关。 无论你是北京时间、纽约时间还是伦敦时间,同一时刻的 Unix 时间戳数字完全相同。

时区差异只在转换为人可读格式时才体现出来。例如时间戳 0

所以当你看到两个系统显示的时间不一样时,不要急着怀疑时间戳错了,先检查一下它们的时区配置格式化逻辑。很多前后端联调的 bug 都是因为这个原因。

六、代码实战:常用语言中的时间戳操作

不同编程语言获取和处理时间戳的方式略有差异,以下是几个常见语言的对比:

JavaScript

// 获取当前毫秒时间戳(13位)
const ms = Date.now();           // 1704067200000

// 获取当前秒级时间戳(10位)
const s = Math.floor(Date.now() / 1000);  // 1704067200

// 时间戳转日期对象
const date = new Date(1704067200000);
console.log(date.toLocaleString('zh-CN'));  // 2024/1/1 08:00:00

// 日期转时间戳
const ts = new Date('2024-01-01T00:00:00Z').getTime();

Python

import time
from datetime import datetime

# 获取当前秒级时间戳(10位)
s = int(time.time())           # 1704067200

# 获取当前毫秒时间戳(13位)
ms = int(time.time() * 1000)   # 1704067200000

# 时间戳转日期
date = datetime.fromtimestamp(1704067200)
print(date.strftime('%Y-%m-%d %H:%M:%S'))  # 2024-01-01 08:00:00

# 日期转时间戳
ts = int(datetime(2024, 1, 1, 8, 0, 0).timestamp())

MySQL

-- 获取当前秒级时间戳
SELECT UNIX_TIMESTAMP();        -- 1704067200

-- 时间戳转日期
SELECT FROM_UNIXTIME(1704067200);  -- 2024-01-01 08:00:00

-- 日期转时间戳
SELECT UNIX_TIMESTAMP('2024-01-01 08:00:00');  -- 1704067200

💡 注意:Python 的 time.time() 返回浮点数(带小数秒),MySQL 的 UNIX_TIMESTAMP() 只返回秒级整数。跨语言交互时要确认精度是否一致。

七、总结

Unix 时间戳看似简单,但背后涉及存储精度、系统位数、时区转换和跨语言兼容性等多个维度的考量。作为开发者,理解这些细节能帮你避免很多低级错误:

如果你需要快速转换时间戳和日期,可以直接使用我们的 在线时间戳转换器,支持10位和13位自动识别,即输即转,结果一键复制。

需要快速转换时间戳?

支持10位秒级和13位毫秒级自动识别,输入即转,一键复制结果

立即使用转换器 →