做开发的人应该都见过这种数字:1719484800、1686240000。一串看起来没什么意义的整数,其实它代表的是一个精确的时间点。这就是 Unix 时间戳(Unix Timestamp),也叫 Epoch Time。几乎所有编程语言和数据库都用它来存储和传递时间。
什么是 Unix 时间戳
Unix 时间戳的定义很简单:从 1970 年 1 月 1 日 00:00:00 UTC 到现在经过了多少秒(或毫秒)。这个起始时间点叫作 "Unix Epoch",是 Unix 系统诞生的时间,被选作为时间的零点。
比如 1719484800 这个数字,换算成人类能看懂的时间就是 2024 年 6 月 27 日 16:00:00 UTC(北京时间是当天午夜 0 点)。
用整数表示时间有几个好处:
- 存储空间小:一个 64 位整数就够了
- 比较和排序方便:数字大的就是更晚的时间
- 跨语言通用:不管用什么编程语言,同一个时间戳代表的时刻是一致的
- 计算简单:两个时间戳相减就能得到时间差
秒级和毫秒级时间戳有什么区别
这是新手最常搞混的地方。
秒级时间戳是 10 位数字(当前大概是 17 亿左右),表示从 Epoch 到现在经过的整秒数。毫秒级时间戳是 13 位数字,表示经过的毫秒数。两者之间差 1000 倍。
快速判断一个时间戳是秒还是毫秒的方法:数位数。
- 10 位数字 = 秒级(范围大约到 2286 年才溢出)
- 13 位数字 = 毫秒级(范围大概到 2286 年溢出)
- 如果位数在中间,可能是其他精度或者格式不对
不同系统和语言用的精度不一样。JavaScript 的 Date.now() 返回的是毫秒级;Python 的 time.time() 返回的是浮点数秒(带小数的);Java 的 System.currentTimeMillis() 是毫秒级;MySQL 的 UNIX_TIMESTAMP() 函数返回的是秒级;大多数 REST API 的接口文档会明确说明用的是哪种精度,开发之前一定要确认。
把秒级转成毫秒级就乘以 1000,反过来除以 1000 取整就行。但要注意精度丢失——毫秒级转成秒级再转回来,最后三位就变成 000 了。
各语言怎么获取当前时间戳
下面列出几种常见语言获取当前时间戳的写法,方便查用。
JavaScript / TypeScript
// 毫秒级
const timestampMs = Date.now();
// 或者
const timestampMs2 = new Date().getTime();
// 秒级
const timestampS = Math.floor(Date.now() / 1000);
Python
import time
# 秒级(浮点数)
timestamp_s = time.time()
# 秒级(整数)
timestamp_s_int = int(time.time())
# 毫秒级
timestamp_ms = int(time.time() * 1000)
Java
// 毫秒级
long timestampMs = System.currentTimeMillis();
// 秒级
long timestampS = System.currentTimeMillis() / 1000L;
// Java 8+ 还可以用 Instant
long timestampMs2 = Instant.now().toEpochMilli();
Go
package main
import (
"fmt"
"time"
)
func main() {
// 秒级
timestampS := time.Now().Unix()
// 毫秒级
timestampMs := time.Now().UnixMilli()
// 纳秒级
timestampNs := time.Now().UnixNano()
fmt.Println(timestampS)
}
PHP
// 秒级
$timestampS = time();
// 毫秒级
$timestampMs = round(microtime(true) * 1000);
这些代码片段随时可以用Unix 时间戳转换工具验证结果对不对。
时间戳的时区怎么处理
Unix 时间戳本身不带时区信息,它永远是 UTC 时间的偏移量。但转换成可读字符串的时候就必须考虑时区了。
同一个时间戳 1719484800:
- 在 UTC 时区显示为:2024-06-27 16:00:00
- 在北京时间 (UTC+8) 显示为:2024-06-28 00:00:00
- 在纽约时间 (UTC-4/-5) 显示为:2024-06-27 12:00:00
所以当你发现前端显示的时间和后端存的时间差几个小时,大概率是时区没处理对。常见的问题包括:
- 后端存的是 UTC 时间戳,前端直接 new Date(timestamp) 转换后按本地时区显示了,看起来跟预期不一致
- 数据库里存的是时间戳,但是查询的时候用了带时区的函数导致结果偏移
- 前后端约定好用秒级,结果一方给了毫秒级,时间差了 1000 倍(这个比时区问题还离谱,但确实经常发生)
最佳实践是在服务端统一用 UTC 存储,展示的时候根据用户的时区偏好再做转换。不要在数据层混入时区逻辑。
2038 年问题
如果你听说过 "Y2K"(千年虫)问题,那 2038 问题就是它的 32 位版。
早期的 Unix 系统用 32 位有符号整数来存时间戳(秒级)。32 位有符号整数的最大值是 2147483647,对应的时间是 2038 年 1 月 19 日 03:14:07 UTC。过了这一秒之后,整数溢出,时间戳会变成负数,系统可能认为时间回到了 1901 年。
这个问题影响的是还在用 32 位 time_t 类型的老系统。现代的 64 位系统不受影响(64 位能表示到公元 2920 亿年左右)。但如果你的代码跑在嵌入式设备上、或者跟一些遗留系统对接,还是要留意一下。
解决方法也很直接:把时间相关的变量类型升级到 64 位(比如 C 语言里用 time64_t 替代 time_t)。大部分主流操作系统和编程语言的最新版本已经解决了这个问题。
实际开发中时间戳有哪些坑
除了上面说的几点,还有一些容易踩的小坑:
- 精度不一致:前端 JavaScript 用毫秒,后端 Python 用秒,传参的时候忘了乘除 1000,查 bug 查半天
- 闰秒:Unix 时间戳不计算闰秒,所以它和实际的原子时会有几秒的差异,普通业务不用管,但对时间敏感的系统要了解
- 数据库存储:MySQL 的 TIMESTAMP 类型有范围限制(1970 到 2038,32 位系统上),超出范围会报错或变成 0000-00-00。建议用 DATETIME 或 BIGINT 存储时间戳
- JSON 序列化:时间戳是数字,JSON 传输没问题。但有些框架会把 Date 对象自动序列化成字符串,反序列化回来的时候要注意格式
常见问题
时间戳能表示未来多久的时间?
64 位系统上,秒级时间戳理论上可以表示到公元 2920 亿年,基本不用担心。但 32 位系统上,最大只能到 2038 年 1 月 19 日。另外 MySQL 的 TIMESTAMP 类型范围是 1970-2038,超出范围会报错,建议用 DATETIME 替代。
怎么快速把时间戳转成可读时间?
用 速工具 的时间戳转换工具,粘贴数字直接出结果。命令行下,Linux 可以用 date -d @1719484800,Mac 上用 date -r 1719484800。JavaScript 里 new Date(1719484800 * 1000) 就行(注意乘 1000 转毫秒)。
不同数据库的时间戳格式一样吗?
不一样。MySQL 的 UNIX_TIMESTAMP() 返回秒级整数,MongoDB 的 ISODate() 存的是 ISO 8601 字符串,PostgreSQL 的 EXTRACT(EPOCH FROM ...) 返回浮点数秒。跨数据库操作时一定要确认格式,避免差 1000 倍的问题。
小结
Unix 时间戳就是一个从 1970-01-01 开始计数的整数,用来表示时间点。记住几个关键点:10 位是秒级、13 位是毫秒级;它不带时区信息;2038 年问题只影响 32 位系统;不同语言的 API 精度不一样,跨语言调用时要统一。日常开发中遇到时间相关的问题,时间戳转换工具可以帮你快速在可读时间和时间戳之间互转,排查问题时很实用。