想知道某件事到底发生在什么时候,不用在脑子里做时间戳算术。把值粘进来,选好你关心的那个时区,剩下的交给转换器。

真正要留意的是:你手里这个数到底是什么单位。Unix 时间戳通常指纪元以来的秒数,但在 JavaScript 里它往往是毫秒。两者描述的是同一类瞬间,可是少了或多了三个零,一件平平无奇的 2026 年的事就被送回了 1970 年。

从你手里的那个值开始

方向明确的话,直接用下面这几个工具:

上方的实时时钟也很顺手:给 Unix 风格的接口用就复制秒,给 JavaScript 和浏览器端代码用就复制毫秒。

Unix 时间戳到底是什么

Unix 时间是从纪元开始计的秒数,纪元定义为 1970-01-01 00:00:00 UTC。这个数字是 UTC 时间轴上的一个点,它本身不携带时区、语言环境或显示格式。

正因为分得这么干净,时间戳在系统之间传递才特别省心。新加坡的服务器、法兰克福的数据库、芝加哥的笔记本可以持有同一个整数,并且对它代表的那个事件毫无分歧。至于显示,各自按本地时间来就好,事件发生的那一刻不会因此改变。

Unix 时间不计闰秒。做应用开发时这通常正是你想要的:它和操作系统、数据库、日志、API 所遵循的 POSIX 惯例保持一致。

秒还是毫秒:三个零的坑

大多数命令行工具、数据库函数和写着 “Unix timestamp” 的接口字段,指的都是;而 JavaScript 的 Date.now() 返回的是毫秒。两个值看上去都挺合理——这正是这个 bug 如此常见的原因。

单位 对应的同一瞬间
1700000000 Unix 秒 2023-11-14T22:13:20Z
1700000000000 Epoch 毫秒 2023-11-14T22:13:20.000Z

对于当代的日期,位数是个好用的第一判断:

  • 10 位基本就是 Unix 秒。
  • 13 位多半是 JavaScript 风格的 epoch 毫秒。

本站的转换器就是靠这个经验规则加快日常转换的。但它只是便利,不是铁律。归档记录、故意设得很远的未来测试日期,或者从某个用微秒的系统里复制出来的值,都需要你明确指定单位。当接口文档写明某个字段是秒或毫秒时,相信文档,别相信位数。

写成代码,这件事无聊得恰到好处:

const seconds = 1700000000;
const milliseconds = seconds * 1000;

new Date(milliseconds).toISOString();
// "2023-11-14T22:13:20.000Z"

反方向就是除以一千。只有当接收方要求整数秒时,才需要 Math.floor()

把 epoch 值转成日期

日志、webhook、数据库记录或接口响应里给了你一个时间戳,那就从 Epoch 转日期开始。转换器会给出:

  • ISO 8601 格式的 UTC 字符串,适合日志和接口;
  • UTC 日期和时间,方便跨系统比对;
  • 你选定时区下的可读日期;
  • 一句相对时间描述,快速建立直观感受。

比如 1700000000 就是 2023-11-14T22:13:20Z。换个显示时区,钟面时间会变,时间戳不会变。可以把时间戳想成地图上的那枚图钉,时区只是旁边贴的标签。

把日期和时间转成 Unix 时间

反过来做,多出一个必须回答的问题:这个时间是哪个时区的?

“6 月 20 日上午 9:25” 在配上时区之前不是一个确定的瞬间。它可能是 09:25Z,可能是纽约的上午 9:25,也可能是东京的上午 9:25——三个完全不同的时刻。切到 日期 → Epoch 标签页,填入日历日期、时间和 IANA 时区,点转换,然后复制结果。

这件事在定时任务、活动报名、账单截止和提醒功能里尤其要紧,在夏令时切换的当口更是如此——那时候某个本地钟面时间可能出现两次,也可能根本不存在。事件本身存转换出来的时间戳;如果这个日程是按当地钟面时间定义的,那就把 IANA 时区名一起存下来。

UTC、本地时间和具名时区

技术场景下最安全的通用显示方式是 UTC,因为它让每个读者面对同一块钟。ISO 8601 字符串结尾带 Z 的就是 UTC:

比如:

2026-06-20T09:25:00Z

但面向用户的输出,最好选一个 IANA 时区名,比如 America/New_YorkEurope/LondonAsia/Tokyo。IANA 名字优于 -05:00 这样的固定偏移,因为它会自动跟随当地的夏令时规则。

有个习惯能挡掉出乎意料多的事故:在服务端渲染的页面、定时邮件、截图和测试里,把时区显式写出来。否则运行时会拿它自己的本地时区来解释——还是同一个瞬间,只是你的服务器决定换一块钟来读它。

比秒更细的精度

日常用的是秒和毫秒,但有些系统会用更小的单位:

单位 常见场景 说明
Unix 接口、JWT claims、shell 工具 经典的 Unix 时间戳
毫秒 JavaScript、浏览器事件、大量 Web API Date.now() 返回的就是这个
微秒 部分数据库和链路追踪系统 最好确认一下字段约定
纳秒 高精度运行时与测量场景 通常需要 BigInt 或字符串承载

看到 16 位或 19 位的数字,别当成不言自明。它可能是 Unix 纪元以来的微秒或纳秒,也可能压根用的是另一个纪元。Chrome / WebKit 时间戳、.NET ticks 以及好几种数据库格式都有各自的起算日。单位和纪元是绑在一起的,得一起确认。

还有一点:如果你要测的是“过了多久”,而不是记录“什么时候发生”,请用单调时钟——浏览器和 Node.js 里是 performance.now()。墙上时钟是可以被校准的,而经过的时间不该因为机器纠正了自己就往回跳。

2038 年这道坎

时间戳最有名的边界情况就是 2038 年问题。有符号 32 位整数最多能装 2147483647 秒,也就是:

2038-01-19T03:14:07Z

还在用这种表示的软件,会在下一秒溢出。现代平台大多换成了更宽的时间类型,但老的文件格式、上了年纪的嵌入式设备和兼容层仍然可能受制于此。如果你的系统数据保留期很长,或者要和老系统对接,现在就拿这个边界附近的日期测一测(而不是等到 2038 年,那时候大家都忙着假装自己早就料到了)。

常用时间戳速查

下面这些值适合写测试、翻日志或者做个快速自检,均以 UTC 显示。

Unix 秒 ISO 8601 UTC 为什么会用到
0 1970-01-01T00:00:00Z Unix 纪元本身
86400 1970-01-02T00:00:00Z 纪元之后一天
1000000000 2001-09-09T01:46:40Z 第一个 10 位里程碑
1700000000 2023-11-14T22:13:20Z 顺手的当代示例
2147483647 2038-01-19T03:14:07Z 有符号 32 位上限

一套靠谱的时间戳工作流

碰到不认识的时间值,别靠眼力猜。按下面四点过一遍:

  1. 纪元: 零点对应的是哪一天?
  2. 单位: 这个值是秒、毫秒、微秒还是纳秒?
  3. 时区: 输入的是一个确定瞬间,还是一个需要配时区才成立的本地日期时间?
  4. 输出约定: 下游系统要什么——数字还是 ISO 8601 字符串?秒还是毫秒?

这四个答案一清楚,转时间戳就是例行公事。算术交给转换器,你只需要在交接的边界上守住含义不走样。

Frequent questions:

Q: 我该用哪个转换器?
A: 手里已经有时间戳、想看懂它是哪一天,用 Epoch 转日期;反过来,手里是日历日期和时间、需要得到 Unix 秒或毫秒,用日期转 Epoch。
Q: 转换器能同时认秒和毫秒吗?
A: 可以。它按数值大小自动判断:常见的秒级时间戳一般是 10 位,毫秒级一般是 13 位。但如果是很久以前或者很遥远的未来的日期,最好先确认单位再转。
Q: 怎么把 Unix 时间戳转成可读日期?
A: 切到「Epoch → 日期」标签页,粘贴时间戳,选好时区,点转换。结果里会同时给出 ISO 8601、UTC 字符串和适合人看的本地格式。
Q: 怎么把日期或时间转成 Unix 时间戳?
A: 切到「日期 → Epoch」标签页,填入日期、时间和时区,然后按秒或毫秒复制结果。
Q: Unix 时间和 UTC 有什么区别?
A: Unix 时间是从纪元 1970-01-01T00:00:00Z 开始计数的一个数字,而定义这个起点的时间标准是 UTC。时间戳本身是时间轴上的一个确定瞬间,时区只影响它被显示成什么样子。
Q: 毫秒怎么换算成秒?
A: 除以 1000。如果对方接口要的是整数秒,JavaScript 里用 Math.floor(milliseconds / 1000)。反过来,秒乘 1000 就是毫秒。
Q: Unix 时间戳可以是负数吗?
A: 可以。负值表示 1970-01-01T00:00:00Z 之前的瞬间。比如 -86400 就是 1969-12-31T00:00:00Z。
Q: 什么是 2038 年问题?
A: 如果程序用有符号 32 位整数存 Unix 秒,它就表示不了 2038-01-19T03:14:07Z 之后的时间。现在的系统大多改用了更宽的类型,但老旧的嵌入式设备和历史遗留代码仍然值得排查一遍。