Unix时间戳转换器

一行一个值,按位数判别是 10 位秒还是 13 位毫秒,同时给出 UTC 与北京两个时刻;遇到 2 的 31 次方减一会提醒你已经站在 2038 溢出临界点上。

有效行数4 行
秒制式2 行
毫秒制式1 行
日期串1 行
[1] 1735689600(10 位 · 秒制式)
    UTC 2025-01-01 00:00:00 | UTC+8 2025-01-01 08:00:00 | 毫秒 1735689600000 | ISO 2025-01-01T00:00:00.000Z
[2] 2147483647(10 位 · 秒制式 · 已到 2^31−1 上限)
    UTC 2038-01-19 03:14:07 | UTC+8 2038-01-19 11:14:07 | 毫秒 2147483647000 | ISO 2038-01-19T03:14:07.000Z
[3] 1704067200000(13 位 · 毫秒制式)
    UTC 2024-01-01 00:00:00 | UTC+8 2024-01-01 08:00:00 | 秒 1704067200 | ISO 2024-01-01T00:00:00.000Z
[4] 2024-01-01 00:00:00(日期串 · 无时区,按 UTC 解析)
    秒 1704067200 | 毫秒 1704067200000 | UTC 2024-01-01 00:00:00 | UTC+8 2024-01-01 08:00:00

提示:输入框里按 Ctrl + Enter(macOS 用 Cmd + Enter)立刻换算一次;Enter 留给换行,因为这里一行就是一个独立的值。文本框为空时不会自动填示例,点「载入示例」可以随时还原上面那四行。

使用方法

  1. 把要换算的数值粘进上面的输入框,一行一个值。页面打开时框里已经放好四行示例,分别是 1735689600、2147483647、1704067200000 和 2024-01-01 00:00:00,结果区显示的就是它们的换算结果,不用先点按钮。
  2. 第一行 1735689600 是 10 位,判定为秒制式,对应 UTC 2025-01-01 00:00:00,北京时间晚 8 小时即 08:00:00。
  3. 第三行 1704067200000 是 13 位,判定为毫秒制式,换算出来同样是 2024-01-01 00:00:00 UTC,折合 1704067200 秒——两行说的是同一个时刻,只是单位不同。
  4. 第四行是日期串,本工具统一按 UTC 解析没有带时区的写法,所以它等于 1704067200 秒。2024-01-01T00:00:00Z 或 2024-01-01T08:00:00+08:00 这类自带时区的写法则按串里标注的时区解析。
  5. 想固定按某一种制式解释数字,把「制式」改成「强制按秒」或「强制按毫秒」;想换看别的时区,改右边的时区偏移,允许 −12 到 +14 之间、以 0.5 小时为步长。

这个 Unix时间戳转换器能解决什么问题

一是一眼认出这串数字是秒还是毫秒。日志、埋点、数据库里同一个字段在不同系统里可能存 10 位也可能存 13 位,直接丢进 new Date(值),13 位的毫秒会被当成秒来读,结果跑到公元五万多年去。本工具先数位数再决定要不要乘 1000,并在结果里写明判定成了哪一种。

二是同时看到 UTC 和本地两个时刻。后端日志几乎都打 UTC,而你对时间的第一反应是北京时间,两者差 28800 秒。工具把两个时间并排写在同一行,不用自己心算加 8 小时,也不用担心跨零点算错一天。

三是提前发现 2038 溢出。用 32 位有符号整数存秒的系统,能表示的最后时刻是 2^31−1 = 2147483647,也就是 2038-01-19 03:14:07 UTC。任何超过这个数的秒值,在有符号 32 位环境里会翻成 1901 年。本页遇到这个临界值会直接标出来。

四是把日期串反算成时间戳。写测试用例、造数据、比对接口返回时经常需要「给我 2024-01-01 00:00:00 对应的秒」,手算容易差一天。这里直接写日期串就能得到秒与毫秒两种写法。

本页不做这些事:不显示多个城市的当地钟表、不改变时间戳本身的数值、不处理农历与节气。换算结果只是同一个瞬间的两种写法,任何一步都不会产生新的时刻。

换算原理:Unix 纪元、秒与毫秒的判别、2038 溢出

这个 Unix时间戳转换器的折算规则只有两条:毫秒 = 秒 × 1000,以及 new Date(毫秒).toISOString()。纪元取 1970-01-01 00:00:00 UTC,它是 POSIX 与 Unix 共同约定的零点,0 就代表那一瞬,1 代表它之后第 1 秒,负数代表它之前。toISOString() 的输出恒定是 UTC,末尾一定带 Z,不会随你的系统时区变化;这一点和 toLocaleString() 正好相反,后者跟随本地时区,两者对同一个毫秒值会给出相差 28800 秒(中国是 UTC+8)的两个字符串。

位数判别是本页最需要说清的一步。做法是:纯数字输入先数位数,10 位及以下按秒,11 位及以上按毫秒。为什么 11、12 位不给秒留位置?因为 11 位的秒要等到 2286-11-20 才出现,12 位的秒更是 5138 年的事,现实中几乎不可能遇到;而 11 位的毫秒对应 1970-04-26 到 1973-03-03,12 位的毫秒对应 1973-03-03 到 2001-09-09,都是完全可能的真实数据。13 位则对应 2001-09-09 之后的毫秒,也就是现在最常见的写法。所以「10 位及以下算秒、11 位以上算毫秒」在实际数据上几乎不会判错;万一判错,用「制式」下拉框强制指定即可。

2038 问题来自 32 位有符号整数的上限。有符号 32 位能表示的最大值是 2^31−1 = 2147483647。把它当秒换算,得到 2038-01-19 03:14:07 UTC,距今只剩约 12 年。再往后一秒,2147483648 在 32 位有符号环境里会翻成 1901-12-13,很多老系统、嵌入式设备、以及还在用 32 位 time_t 的库会因此算出完全错误的时间。作为对比,32 位无符号能撑到 4294967295,也就是 2106-02-07 06:28:15 UTC;而 64 位有符号秒的极限是 2^63−1 = 9223372036854775807,约合 2922 亿年,实际等于永不溢出。

JavaScript 的 Date 内部只存毫秒,范围比 32 位大得多也有限。ECMAScript 规定日期值的可表示区间是 ±8.64×1015 毫秒(±8640000000000000),换算过来约 ±273790 年,正向端点对应 +275760-09-13、负向端点对应 −271821-04-20。超出这个区间的毫秒值,new Date() 会得到一个 Invalid Date,继续取 toISOString() 就直接抛错,本工具会在这一步之前拦下并说明原因。另一个更早的隐性边界是 2^53 = 9007199254740992:IEEE 754 双精度能精确表示的最大连续整数就是它,毫秒值一旦超过这个数就不再是每个整数都有对应表示,相邻可表示值之间会开始跳过一些毫秒。

Unix 时间不含闰秒,这是它和 UTC 显示值始终对不上的原因。UTC 时标为了跟上地球自转,会不定期插入闰秒,从 1972 年到 2016 年底一共插了 27 次;但 Unix 时间把每天固定当作 86400 秒,从不插入,所以两者之间存在一个不断累加的整数差。TAI − UTC 自 2017-01-01 起恒为 37 秒,也就是说同一物理瞬间,Unix 时间戳与严格 UTC 计数之间正好差 37 秒。日常业务用不到这个差别,但做高精度授时、卫星导航或天文计算时不能忽略。

完整示例(数值仅作演示)

示例条件与页面打开时的默认状态完全一致:输入框四行、制式选「自动按位数判别」、时区偏移填 8。四行实测结果如下:

四行里第 3 行和第 4 行说的是同一个时刻:13 位毫秒值 1704067200000 除以 1000 就是 1704067200 秒,而 2024-01-01 00:00:00 按 UTC 解析也等于这个秒数,三个写法互相对得上。统计条显示 4 行有效、2 行秒制式、1 行毫秒制式、1 行日期串,加起来正好 4 行。

再算一遍最容易搞错的 2147483647:它除以 86400 得到 24855.13 天,再除以平均回归年 365.2425 天,约 68.05 年,1970 加上 68 年刚好落在 2038 年初,这就是「2038 问题」名字的由来。而被它加上 1 之后的 2147483648,换算出来是 2038-01-19 03:14:08 UTC,北京时间 11:14:08——只差一秒,却是「还能表示」和「已经溢出」的分界。

常见时间戳与 UTC/北京时间对照表

下表给出本工具最常被问到的一批时间戳。注意「位数/制式」一列,同为 10 位的数值可以跨越 1970 到 2286 年,而毫秒制式必须 13 位才对应到现代时间。

时间戳位数/制式UTC 时间北京时间 UTC+8说明
01 位 · 秒1970-01-01 00:00:001970-01-01 08:00:00Unix 纪元零点,负值代表它之前
11 位 · 秒1970-01-01 00:00:011970-01-01 08:00:01纪元之后第 1 秒
864005 位 · 秒1970-01-02 00:00:001970-01-02 08:00:0086400 秒正好是 1 天
315360008 位 · 秒1971-01-01 00:00:001971-01-01 08:00:00365×86400,整 365 天
9466848009 位 · 秒2000-01-01 00:00:002000-01-01 08:00:00九位数的最后一年
100000000010 位 · 秒2001-09-09 01:46:402001-09-09 09:46:40十位数从这个时刻开始
123456789010 位 · 秒2009-02-13 23:31:302009-02-14 07:31:30顺号数字串,跨了零点
170406720010 位 · 秒2024-01-01 00:00:002024-01-01 08:00:00等于 1704067200000 毫秒
173568960010 位 · 秒2025-01-01 00:00:002025-01-01 08:00:00本页示例第 1 行
214748364710 位 · 秒2038-01-19 03:14:072038-01-19 11:14:072^31−1,32 位有符号上限
214748364810 位 · 秒2038-01-19 03:14:082038-01-19 11:14:082^31,越过上限 1 秒即溢出
429496729510 位 · 秒2106-02-07 06:28:152106-02-07 14:28:152^32−1,32 位无符号上限
410244480010 位 · 秒2100-01-01 00:00:002100-01-01 08:00:002100 年不是闰年,仍可精确表示
170406720000013 位 · 毫秒2024-01-01 00:00:002024-01-01 08:00:00毫秒制式写法,同理 13 位
25340230079912 位 · 秒9999-12-31 23:59:59年份溢出到五位数四位年份能表示的最后一秒

输入范围、边界与精度说明

常见问题

为什么我的 13 位数字算出来是 1970 年?

因为它被当成了秒。毫秒和秒差 1000 倍,1704067200000 若按秒解释会跑到公元 53999 年,反过来一个 10 位秒值按毫秒解释则只会得到 1970 年 1 月附近的时刻。本工具按位数自动判别,10 位及以下当秒、11 位以上当毫秒;如果数据来源的规矩不一样,用「制式」下拉框强制指定。

2038 问题到底是什么?现在需要处理吗?

用 32 位有符号整数存秒的系统,最大值是 2^31−1 = 2147483647,对应 2038-01-19 03:14:07 UTC,再往后一秒就会溢出成 1901 年。64 位系统早已不受影响,但老设备、嵌入式固件、老版本数据库和部分 time_t 为 32 位的库仍会遇到。现在需要做的是排查:找出所有把秒存进 32 位整数的字段,确认存储类型是否需要放宽。

为什么同一个时刻,UTC 和北京时间差 8 小时?

因为北京时间是 UTC+8,固定偏移 8×3600 = 28800 秒。这不是数据错误,而是时区定义:Unix 时间记录的是一个绝对瞬间,任何时区都是对同一瞬间的不同本地写法。本页把两行并排输出,就是为了避免你手动加 8 小时时算错跨零点的情形,例如 1234567890 的 UTC 是 2009-02-13 23:31:30,北京时间已经翻到 02-14 07:31:30。

为什么不用 toLocaleString() 显示时间?

因为它跟随运行环境的本地时区。同一段代码在你电脑上显示北京时间、在海外服务器上显示当地时间,排查问题时反而多一层变量。toISOString() 恒定输出 UTC 并带 Z,是唯一不受环境影响的写法;要换时区,就自己加上偏移毫秒数再格式化,本工具就是这么做的。

时间戳可以是负数吗?

可以,负值表示 1970-01-01 00:00:00 UTC 之前。例如 −1 对应 1969-12-31 23:59:59 UTC,北京时间则是 1969-12-31 07:59:59。日期串也可以填 1969 年以前,只要落在 Date 的 ±273790 年范围内都能换算。

闰秒会让我的时间戳对不上吗?

会,但只在秒级精度下看得出来。Unix 时间不插入闰秒,而 UTC 从 1972 年到 2016 年底共插入 27 次,两者当前相差 37 秒(TAI − UTC)。如果你在做需要严格对齐物理时刻的工作(授时、导航、天文),这个固定偏差必须显式补偿;普通业务系统按 Unix 时间统一即可,不要混用两套时标。

为什么我的日期串在不同浏览器算出来不一样?

因为带空格的写法 2024-01-01 00:00:00 不在 ECMAScript 规定的标准格式里,属于各引擎自行决定的行为,有的当 UTC、有的当当地时间。标准写法只有 2024-01-01T00:00:00Z(UTC)和 2024-01-01T08:00:00+08:00(带偏移)这类。本工具对没写时区的串一律按 UTC 解析,口径固定,所以结果可复现。

输入的内容会不会被上传?

不会。位数判别、乘除 1000、时区偏移、格式化全部在页面里完成,没有网络请求,也不写浏览器存储。想自己确认,打开开发者工具的 Network 面板连点几次「插入当前时间」,可以看到没有新请求发出。

相关工具