← 返回文章列表

Unix 时间戳入门:秒与毫秒、UTC 与时区的正确打开方式

时间避坑

时间戳是什么

Unix 时间戳是自 1970-01-01 00:00:00 UTC 起经过的秒数。它与时区无关:全球同一时刻读到的值完全相同,这正是它适合存储和传输的原因。

秒、毫秒、微秒

  • 10 位 = 秒(Linux/数据库常用);
  • 13 位 = 毫秒(Java、JavaScript 常用);
  • 16 位 = 微秒(部分高精度系统)。

位数混用会得到 1970 年或五万年后的日期。看到离谱的转换结果,先数一下位数。

时区只影响显示

同一时间戳在 UTC+8 显示 19:00,在 UTC 显示 11:00。变的是显示,不是值。多数“时间对不上”的问题,本质是把本地时间当 UTC 存了(或反过来)。

实用建议

  1. 数据库统一存 UTC 时间戳或 ISO 8601 字符串;
  2. 前端拿到后再按用户时区本地化显示;
  3. 留意 2038 问题:32 位秒级时间戳会溢出,新系统一律用 64 位。

各语言与数据库

环境获取当前时间戳位数
JavaScriptDate.now()13 位(毫秒)
Pythontime.time()10 位(秒,带小数)
JavaSystem.currentTimeMillis()13 位(毫秒)
Gotime.Now().Unix()10 位(秒)
MySQLUNIX_TIMESTAMP() / FROM_UNIXTIME(1700000000)秒
PostgreSQLEXTRACT(EPOCH FROM now())秒(带小数)

命令行验证

# Linux / macOS:时间戳 → 可读 UTC 时间
date -u -d @1700000000

# 反向:可读时间 → 时间戳
date -u -d '2023-11-14 22:13:20' +%s

# 按指定时区渲染
TZ=Asia/Shanghai date -d @1700000000

四个最容易踩的坑

  • 位数混用:把 10 位秒当毫秒读得到 1970-01-01;把 13 位毫秒当秒读得到五万年后;
  • 把本地时间当 UTC 存:接口只传 "2026-09-23 10:00" 不带时区,跨时区用户就会看到错误时刻;
  • 直接比较字符串:拿 ISO 字符串和时间戳比、或比带不同偏移量的时间,结果都不可靠;
  • 忽略夏令时:部分地区一年两次跳变,跨时区调度务必以 UTC 为基准。

几个常被问到的问题

该存秒还是毫秒?存整数秒或毫秒都行,关键是全链路统一;毫秒精度更高,前端 Date.now() 默认毫秒。时间戳能为负吗?能,表示 1970 年之前。怎么避免 2038 问题?用 64 位整数存储。展示给用户该用什么?按用户时区格式化,别直接甩一串时间戳。

动手试试:时间戳转换、Cron 表达式