使用方法
- 把要换算的数值粘进上面的输入框,一行一个值。页面打开时框里已经放好四行示例,分别是
1735689600、2147483647、1704067200000和2024-01-01 00:00:00,结果区显示的就是它们的换算结果,不用先点按钮。 - 第一行
1735689600是 10 位,判定为秒制式,对应 UTC 2025-01-01 00:00:00,北京时间晚 8 小时即 08:00:00。 - 第三行
1704067200000是 13 位,判定为毫秒制式,换算出来同样是 2024-01-01 00:00:00 UTC,折合 1704067200 秒——两行说的是同一个时刻,只是单位不同。 - 第四行是日期串,本工具统一按 UTC 解析没有带时区的写法,所以它等于
1704067200秒。2024-01-01T00:00:00Z或2024-01-01T08:00:00+08:00这类自带时区的写法则按串里标注的时区解析。 - 想固定按某一种制式解释数字,把「制式」改成「强制按秒」或「强制按毫秒」;想换看别的时区,改右边的时区偏移,允许 −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。四行实测结果如下:
- 第 1 行
1735689600:10 位 → 秒制式,UTC 2025-01-01 00:00:00,北京时间(UTC+8)2025-01-01 08:00:00,毫秒写法1735689600000; - 第 2 行
2147483647:10 位 → 秒制式,UTC 2038-01-19 03:14:07,北京时间 11:14:07,正好是2^31−1这个 32 位有符号上限; - 第 3 行
1704067200000:13 位 → 毫秒制式,UTC 2024-01-01 00:00:00,折合1704067200秒; - 第 4 行
2024-01-01 00:00:00:日期串且未带时区,按 UTC 解析,得到1704067200秒 /1704067200000毫秒。
四行里第 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 | 说明 |
|---|---|---|---|---|
| 0 | 1 位 · 秒 | 1970-01-01 00:00:00 | 1970-01-01 08:00:00 | Unix 纪元零点,负值代表它之前 |
| 1 | 1 位 · 秒 | 1970-01-01 00:00:01 | 1970-01-01 08:00:01 | 纪元之后第 1 秒 |
| 86400 | 5 位 · 秒 | 1970-01-02 00:00:00 | 1970-01-02 08:00:00 | 86400 秒正好是 1 天 |
| 31536000 | 8 位 · 秒 | 1971-01-01 00:00:00 | 1971-01-01 08:00:00 | 365×86400,整 365 天 |
| 946684800 | 9 位 · 秒 | 2000-01-01 00:00:00 | 2000-01-01 08:00:00 | 九位数的最后一年 |
| 1000000000 | 10 位 · 秒 | 2001-09-09 01:46:40 | 2001-09-09 09:46:40 | 十位数从这个时刻开始 |
| 1234567890 | 10 位 · 秒 | 2009-02-13 23:31:30 | 2009-02-14 07:31:30 | 顺号数字串,跨了零点 |
| 1704067200 | 10 位 · 秒 | 2024-01-01 00:00:00 | 2024-01-01 08:00:00 | 等于 1704067200000 毫秒 |
| 1735689600 | 10 位 · 秒 | 2025-01-01 00:00:00 | 2025-01-01 08:00:00 | 本页示例第 1 行 |
| 2147483647 | 10 位 · 秒 | 2038-01-19 03:14:07 | 2038-01-19 11:14:07 | 2^31−1,32 位有符号上限 |
| 2147483648 | 10 位 · 秒 | 2038-01-19 03:14:08 | 2038-01-19 11:14:08 | 2^31,越过上限 1 秒即溢出 |
| 4294967295 | 10 位 · 秒 | 2106-02-07 06:28:15 | 2106-02-07 14:28:15 | 2^32−1,32 位无符号上限 |
| 4102444800 | 10 位 · 秒 | 2100-01-01 00:00:00 | 2100-01-01 08:00:00 | 2100 年不是闰年,仍可精确表示 |
| 1704067200000 | 13 位 · 毫秒 | 2024-01-01 00:00:00 | 2024-01-01 08:00:00 | 毫秒制式写法,同理 13 位 |
| 253402300799 | 12 位 · 秒 | 9999-12-31 23:59:59 | 年份溢出到五位数 | 四位年份能表示的最后一秒 |
输入范围、边界与精度说明
- 10 位及以下按秒,11 位及以上按毫秒。这条规则在真实数据上几乎不会判错,但你随时可以在「制式」下拉框里强制指定。判错制式最典型的表现就是年份离谱:把 13 位的
1704067200000强制按秒解释,得到的是公元 55969 年附近——它仍在 Date 的 ±273790 年范围内,所以不会报错,只会安静地给你一个荒唐的年份,改回「自动按位数判别」即可。 - 秒值超过 2147483647 就要当心溢出。这不是本工具的边界,而是 32 位有符号
time_t的边界。结果里出现「已到 2^31−1 上限」提示时,说明这个值放进这类系统会翻成 1901 年,需要确认对方用的是 32 位还是 64 位。 - 毫秒值必须落在 ±8.64×1015 之内。这是 ECMAScript 规定 Date 的可表示范围,约 ±273790 年。超出之后
new Date()得到 Invalid Date,toISOString()会抛RangeError: Invalid time value。另外2^53 = 9007199254740992之后双精度浮点不再保证每个整数都精确可表示,毫秒序列会开始跳过数值。 - 时区偏移允许 −12 到 +14 小时,步长 0.5。上限取 +14 是因为现实中存在 UTC+14 的行政区(基里巴斯莱恩群岛),下限 −12 是 UTC−12。印度、尼泊尔这类半小时或 45 分钟偏移用 5.5、5.75 填即可;注意偏移只改显示,不影响时间戳本身的数值。
- 无时区的日期串统一按 UTC 解析。这是本工具的明确口径,写出来是为了避免「为什么我和别人算的不一样」。JavaScript 里
new Date('2024-01-01T00:00:00')按标准会被当成 UTC,而new Date('2024-01-01 00:00:00')(中间是空格)属于引擎自行决定的非标准输入,同一个串在不同浏览器、不同机器上可能被当成 UTC 也可能被当成当地时间,这正是必须显式约定时区的原因。 - Unix 时间不含闰秒。UTC 时标从 1972 年到 2016 年底一共插入 27 次闰秒,而 Unix 时间把每天固定为 86400 秒。两者当前相差的整数秒就是
TAI − UTC = 37 秒。做秒级对账、抓包时间戳比对时如果出现固定 37 秒的偏差,原因就在这里,而不是时钟没同步。 - 结果的精度止于毫秒。秒制式输入只会得到整秒的结果,微秒、纳秒在换算成毫秒时会先被截断;13 位毫秒输入回推秒时若不能整除,这个 Unix时间戳转换器会保留三位小数显示。
常见问题
为什么我的 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 面板连点几次「插入当前时间」,可以看到没有新请求发出。