Unix 时间戳转换
时间戳与日期时间双向转换,秒/毫秒自动识别,可切换 UTC 与本地时区。
下面把日期时间转成时间戳
Unix 时间戳:秒与毫秒、2038 问题与八个常见 bug
它是什么
Unix 时间戳是从 1970-01-01 00:00:00 UTC 起经过的秒数,是一个绝对时刻,与时区无关。这个起点(称为 Unix epoch)没有深意,只是 Unix 系统设计时取的一个便于计算的整数值。
| 时间戳 | 对应时刻(UTC) |
|---|---|
0 | 1970-01-01 00:00:00 |
1000000000 | 2001-09-09 01:46:40 |
1700000000 | 2023-11-14 22:13:20 |
2147483647 | 2038-01-19 03:14:07(32 位有符号整数上限) |
它不是"经过了多少秒"
一个常被忽略的细节:Unix 时间不计闰秒。也就是说它并不是精确的"自 1970 年起的物理经过秒数",而是一个与 UTC 大致对齐、但通过"每天固定 86400 秒"推进的计数。POSIX 标准就是这样定义的,好处是不会因为插入闰秒而导致时间倒流或需要额外处理。
后果:Unix 时间与真正经过的物理时间之间,历史上累计差了 27 秒左右(截至目前的闰秒总数)。对绝大多数应用没有影响,但天文计算、需要严格物理时间间隔的场景要注意这个差异。
2038 年问题
32 位有符号整数的最大值是 231 − 1 = 2147483647,对应 2038-01-19 03:14:07 UTC。超过这个值之后,用 32 位整数存时间戳的系统会因为溢出而回绕到 1901 年 12 月 13 日。
受影响的典型位置:
- 数据库里的
INT列存时间戳(MySQL 的INT是 32 位有符号)。应改用BIGINT、DATETIME或TIMESTAMP类型。 - C 语言的
time_t在 32 位平台上仍是 32 位。64 位平台已经不受影响。 - 嵌入式设备、老系统、文件格式(部分格式的时间字段位宽固定)。
- 协议字段:某些协议规定时间用 32 位秒表示,这类协议需要新版本才能跨过 2038。
好消息是主流 64 位系统全部无此问题——64 位有符号整数的上限对应约 2920 亿年后,远超实际需要。所以排查重点应该放在数据存储的列类型和老设备上。
秒与毫秒的混淆
这是时间戳使用中最常见的错误。同一个时刻用两种单位表示,相差 1000 倍,症状非常典型:
| 错误 | 显示结果 | 原因 |
|---|---|---|
| 把毫秒当秒 | 年份跑到 5 万年以后 | 数值大了 1000 倍 |
| 把秒当毫秒 | 年份停在 1970 年 1 月 | 数值小了 1000 倍 |
快速判断参考:秒级时间戳现在是 10 位数(约 1.7 × 10⁹),毫秒级是 13 位数(约 1.7 × 10¹²)。数位数就能立刻区分。
本工具按数值自动判断:大于等于 1012 视为毫秒,否则视为秒。这里有一个诚实的边界要说明:1970 年到 2001 年之间的毫秒时间戳小于 1012,会被这个规则误判为秒。如果你在处理这个时间段的历史数据且单位是毫秒,需要手动换算——这是启发式判断的固有限制,不是可以通过调参解决的问题。
八个常见 bug
- 时区当成了时间戳的一部分。时间戳本身没有时区,它只是一个时刻。所谓"存错了 8 小时"实际是:把一个本地时间当作 UTC 来生成时间戳,或者反过来把 UTC 时间戳按本地时间显示。中国(UTC+8)的典型症状就是差 8 小时。
- 月份从 0 开始。JavaScript 的
Date.getMonth()返回 0 表示 1 月,所以 12 月是 11。"日期差了一个月"往往就是这里。 - 日期字符串解析不一致。
new Date("2026-01-15")按 UTC 解析(ISO 格式视为 UTC),而new Date("2026/01/15")按本地时区解析。同一个日期两种写法差 8 小时,可能跨天。所以尽量用显式的 ISO 格式或时间戳,避免依赖解析器的默认行为。 - 用 32 位字段存秒。见 2038 问题。
- 浮点存储的精度。用浮点数存秒级时间戳时,小数部分表示毫秒,但精度会随数值增大而下降。要毫秒精度就用整数毫秒。
- 本地时间与 UTC 混存。一个字段里既有 UTC 又有人工填入的本地时间,排序和比较就会出错。数据库里统一存 UTC,只在展示时转换。
- 夏令时导致的小时重复或缺失。有夏令时的地区,某一天的 02:00 可能出现两次或不存在。用 UTC 存储就不受影响。中国不实行夏令时,但面向海外用户的系统会碰到。
- 只存日期不存时刻。生日、账单日这类"日期"语义的值,如果存成时间戳,跨时区读取时可能变成前一天。这类值应该用日期类型,不要用时间戳。
实用换算参考
| 单位 | 秒数 |
|---|---|
| 1 分钟 | 60 |
| 1 小时 | 3,600 |
| 1 天 | 86,400 |
| 1 周 | 604,800 |
| 1 年(365 天) | 31,536,000 |
| 1 年(365.25 天,含闰年) | 31,557,600 |
前端缓存过期时间、限流窗口、会话有效期这类计算都基于这些数。注意用 31,536,000 还是 31,557,600 会带来约 0.07% 的差异——小到通常无所谓,但在按年计费的场景里会积少成多。
工具怎么用
- 时间戳 → 日期:输入秒或毫秒(自动识别),切换 UTC 或本地时区查看结果。同一时刻在两种时区下显示不同,但指向的是同一个瞬间。
- 日期 → 时间戳:按
YYYY-MM-DD HH:mm:ss格式输入,得到对应的时间戳。 - 页面顶部显示当前时间戳,可用于校准服务器与本机的时间差。
全部计算在浏览器本地完成,时间戳通常不含敏感信息,但涉及业务数据校验时仍建议本地处理。
常见问题
秒和毫秒怎么区分?
本工具按数值大小自动识别:大于等于 1e12 视为毫秒,否则视为秒(当前秒级时间戳约 1.7e9)。
UTC 和本地时区有何不同?
UTC 是全球统一时间;本地时区按你所在地区显示。转换结果会随选项变化,但底层时刻一致。