前阵子联调一个第三方接口,返回的 JSON 里有个字段长这样:「expireTime」:1787097600000。我问同事这到底啥时候过期,他头也不抬丢给我一句「自己算去」。盯着这串 13 位数字,我愣了半天——后来才搞明白,它叫 Unix 时间戳,是程序员眼里最常用的时间格式。
时间戳是什么?
Unix 时间戳,指的是从 1970 年 1 月 1 日 00:00:00 UTC 到某个时刻经过的秒数(或毫秒数)。它不关心你在北京还是纽约,不区分东八区还是西五区——同一个时刻,全世界的时间戳数值完全一样。
为什么从 1970 年算起?因为 Unix 操作系统当年就是从这一刻开始计时的,大家沿用至今。1970 年之前的时刻会得到负数,程序同样支持。
秒级和毫秒级,别搞混
时间戳有两位「表亲」,位数不一样:
- 秒级:10 位数字,如 1787097600。PHP 的 time()、MySQL 的 UNIX_TIMESTAMP() 返回这种;
- 毫秒级:13 位数字,如 1787097600000。JavaScript 的 Date.now()、Java 的 System.currentTimeMillis() 返回这种。
判断方法很简单:数位数,10 位是秒、13 位是毫秒。接口联调里最常见的坑,就是把毫秒当秒用、或者反过来,导致时间差了 1000 倍——约 17 分钟,排查起来非常迷惑。
怎么手动换算
公式就一句话:时间戳 = 目标时刻 − 1970-01-01 00:00:00 UTC 的差值(秒或毫秒)。
以 2026-08-19 08:00:00 北京时间为例(即 2026-08-19 00:00:00 UTC):
- 秒级:1787097600
- 毫秒级:1787097600000 = 1787097600 × 1000
反过来,把秒级时间戳除以 86400(一天的秒数)得到天数,再加上 1970 年就能反推日期。手算容易出错,日常直接用本站的时间戳转换工具,任输一边自动出另一边,秒级、毫秒级自动识别。
为什么大家都爱用时间戳
- 与语言无关:不管什么编程语言、前端后端,都是同一个整数,没有格式差异;
- 天然可比较:数字越大时间越晚,排序、范围查询直接比大小,比字符串日期可靠;
- 不受时区影响:存储和传输都不带时区,展示时再转本地时间;
- 节省空间:一个 4 或 8 字节整数,比「2026-08-19 08:00:00」这种字符串小得多。
常见的使用场景
- 接口签名:用毫秒时间戳标记请求时间,防止重放攻击;
- 缓存过期:Redis 给 key 设置过期时间,用的就是秒级时间戳;
- 数据库字段:建表存 BIGINT 时间戳,比 datetime 省空间、排序更快;
- 日志与统计:按时间戳分段统计访问量、计算接口耗时。
2038 年问题
32 位有符号整数最大是 2147483647,对应的时刻是 2038 年 1 月 19 日 03:14:07 UTC。也就是说,还在用 32 位整数存秒级时间戳的老系统,到那一刻会溢出。好在现代系统普遍用 64 位或直接存毫秒,2038 问题更像是一个「历史遗留提醒」,而不是眼前危机。
三个常见误区
- 误区一:时间戳分「北京时间」和「世界时间」。没有。时间戳就是个数字,全球统一;差别只发生在「显示成日期」这一步。
- 误区二:13 位比 10 位更精确。毫秒级精度确实更高,但数据本来就是秒级的话,硬乘 1000 不会增加任何精度,只会让数字看着吓人。
- 误区三:1970 年 1 月 1 日 0 点 0 分 0 秒。注意基准是 UTC 时间,对应北京时间 1970-01-01 08:00:00,换算时别把基准搞错。
常见问题(FAQ)
怎么快速判断接口返回的是秒还是毫秒?
数位数:10 位是秒、13 位是毫秒;或者看量级,超过 100 亿的基本是毫秒。也可以直接丢进本站时间戳转换工具,它自动识别。
时间戳是负数是怎么回事?
1970 年之前的时刻时间戳就是负数,比如 1949-10-01 对应约 -639072000(秒)。处理历史日期时,注意别用无符号整数类型。
为什么显示出来的时间总差 8 小时?
因为把时间戳按 UTC 显示了,而北京时间是 UTC+8。转成日期时一定要带上时区(如 Asia/Shanghai),否则看到的永远是「少了 8 小时」的时间。
时间戳是每个开发者迟早要打交道的「暗号」。记住两条:10 位是秒、13 位是毫秒;转换的活儿交给时间戳转换工具。还想补点开发常识?可以接着读:Base64 是什么?一文看懂编码原理与在线转换。