时间戳转换
在线 Unix 时间戳转换器,秒/毫秒级时间戳与人类可读日期时间实时互转,并同时显示本地时区与 UTC 时间,方便开发者与数据处理场景使用。
什么是 Unix 时间戳?
Unix 时间戳(Unix Timestamp / Epoch Time)是从 1970 年 1 月 1 日 00:00:00 UTC 起经过的秒数,这个起点叫「纪元」(Epoch)。它是一个纯粹的整数,不带时区、不带格式、不带语言:同一个数在任何机器、任何国家读到的都是同一个绝对时刻。
正因如此,后端日志、数据库时间字段、缓存过期时间、API 返回的 created_at / updated_at 几乎都用时间戳,而不是「2026 年 8 月 27 日 10:30」这种字符串。字符串要解析才能比较、要额外存时区、还要考虑各地区书写格式不同;整数则天然可排序、可做差、可索引。
Unix 时间有一个常被忽略的特点:它不含闰秒。也就是说,每个「日」被固定当作 86400 秒,闰秒不插入这条时间轴。这让「加一天 = 加 86400 秒」永远精确成立(详见下方闰秒一节)。
秒级与毫秒级:先看清位数
最常见的坑不是时区,而是单位搞错。现在的秒级时间戳约 17.9 亿(10 位),毫秒级约 1.79 万亿(13 位),两者相差 1000 倍。把毫秒当秒读,日期会跑到五万多年以后;把秒当毫秒读,日期会掉回 1970 年 1 月。
| 单位 | 当前量级 | 位数 | 示例 | 误读后的结果 |
|---|---|---|---|---|
| 秒级(s) | 约 17.9 亿 | 10 | 1790090593 | 当成毫秒 → 1970-01-21 |
| 毫秒级(ms) | 约 1.79 万亿 | 13 | 1790090593958 | 当成秒 → 公元 5.8 万多年 |
本工具默认按上表规则自动判级,也可以在下拉框里手动锁定「秒」或「毫秒」。手动指定的价值在于:当某个接口返回的是非标准精度(比如微秒、或以 0.5 为步长的计数器)时,自动判级会猜错,你需要靠手动切换去试探出正确的解读方式。
10^11 这个自动判级分界是怎么来的?
工具内部用的分界值是 10^11(即 100000000000):数值大于等于它就按毫秒读,小于它就按秒读。这个取值不是拍脑袋,而是从「秒级时间戳要多久才会涨到这么大」倒推出来的。
10^11 秒约等于 3169 年,也就是公元 5138 年前后才会出现的秒级时间戳;而 10^11 毫秒只是 1157 天(约 3.2 年),对应 1973 年 3 月 3 日,一个真实存在的毫秒时间戳。也就是说,在可预见的将来,10^11 这条线以上的一定是毫秒、以下的一定是秒,判级几乎不会出错。
| 输入值 | 位数 | 自动判级 | 换算结果(UTC) | 说明 |
|---|---|---|---|---|
| 99999999999 | 11 | 秒 | 5138-11-16T09:46:39Z | 刚好在分界之下,按秒解读 |
| 100000000000 | 12 | 毫秒 | 1973-03-03T09:46:40Z | 正好等于分界,归入毫秒 |
| 100000000001 | 12 | 毫秒 | 1973-03-03T09:46:40.001Z | 刚过分界 1 毫秒 |
看这三行就能明白边界行为:分界值本身归毫秒,因为判级条件是「大于等于」。而 99999999999 虽然只有 11 位,仍在分界之下,会被当成秒 —— 它对应的是公元 5138 年,这个结果看着离谱,但恰恰证明分界线选得足够靠上。
如何使用本工具
- 在「Unix 时间戳」输入框中粘贴秒级或毫秒级时间戳,工具按上述 10^11 规则自动判级,实时显示本地时间、UTC 时间、秒值与毫秒值
- 或在「日期时间」框里直接选一个时刻,工具反向算出对应的秒/毫秒时间戳,并把单位自动切到「秒」
- 用下拉框手动锁定「秒」或「毫秒」,可以验证同一数值在另一种解读下会得到什么 —— 这是排查「差 1000 倍」类问题的最快手段
- 结果区把本地时区时间与 UTC 时间并排给出,两者差值就是你的时区偏移,可用来核对日志、数据库与接口返回是否一致
基准时间戳对照表
下面这些值可以直接当参照物,用来快速判断「一个时间戳大致对应哪一年」,也方便你拿手边的时间戳做量级估算。
| 时刻 | 秒级时间戳 | 毫秒级时间戳 |
|---|---|---|
| 纪元起点 | 0 | 0 |
| 1969 年最后一天(-1 秒) | -1 | -1000 |
| 2000-01-01T00:00:00Z | 946684800 | 946684800000 |
| 2020-01-01T00:00:00Z | 1577836800 | 1577836800000 |
| 2026-01-01T00:00:00Z | 1767225600 | 1767225600000 |
| 32 位有符号整数上限 | 2147483647 | 不适用(毫秒已溢出) |
再记住两个常量就能自己心算:一天 = 86400 秒,一个平年 = 31536000 秒。把时间戳除以 86400 得到「距纪元多少天」,余数就是当天零点起的秒数。
六个真实算例
- 秒级时间戳转可读时间:输入 1600000000,得到 2020-09-13T12:26:40Z,也就是北京时间(UTC+8)2020-09-13 20:26:40。用 1600000000 除以 86400 得 18518.518 天,余数 44800 秒正好是 12 点 26 分 40 秒,与工具结果一致。
- 毫秒级时间戳转可读时间:输入 1600000000000,自动判为毫秒,结果同样是 2020-09-13T12:26:40Z。它和算例 1 指向同一个时刻,这正是「秒与毫秒只是同一条时间轴的两种刻度」的最好证明。
- 单位搞错会怎样:把 1600000000 当毫秒读,得到的是 1970-01-19T12:26:40Z——一个「时间跑回 1970 年」的典型现象;反过来把 1600000000000 当秒读,会得到 +057971-02-25 这种五万位数的年份。只要先看位数,就能在输入前一步排掉这类错误。
- 日期时间反算时间戳:选择 2026-01-01 00:00:00(UTC 口径),工具给出秒级 1767225600、毫秒级 1767225600000。手动加上 28800 秒(UTC+8 的偏移),同一个 UTC 时刻就变成北京时间 2026-01-01 08:00:00。
- 负数时间戳:输入 -1,得到 1969-12-31T23:59:59Z,即纪元前一秒。负数不是异常值,而是合法的纪元前时刻,常出现在「日志里某个事件早于 1970」的考古场景。
- 算「过去了多少天」:2026-10-09T00:00:00Z 的时间戳是 1791504000,除以 86400 得整除结果 20735 天。若要算距今天数,用当前时间戳减去目标时间戳再除以 86400,并用向下取整而不是四舍五入,否则会出现「还有 23 小时就满一天」的别扭结果。
时区与偏移:为什么同一个时间戳看起来不一样
时间戳本身不含时区。它记录的是「绝对时刻」,偏移量是在显示层才叠加上去的。同一个 1600000000,在 UTC 下是 9 月 13 日 12:26:40,在 UTC+8 下是 9 月 13 日 20:26:40,在 UTC-5 下是 9 月 13 日 07:26:40 —— 数字没变,只是墙上时钟的读数变了。
偏移量都是整数秒,所以算时区只要做加减:UTC+8 加 28800 秒,UTC+9 加 32400 秒,UTC-5 减 18000 秒。半区和三分之四区同样如此:印度 UTC+5:30 是 19800 秒,尼泊尔 UTC+5:45 是 20700 秒。这也解释了为什么服务器和数据库普遍用分钟而不是小时来存偏移量。
需要提醒的是夏令时:实行夏令时的地区,偏移量在一年之内会变两次。所谓「UTC+8 的 28800 秒」对中国这样的全年恒定偏移区是稳定的,但对欧洲、北美并不成立。所以严谨的做法是存 UTC、只在显示时转本地,不要把本地偏移硬编码进存储。
闰秒为什么不在时间戳里
闰秒是为了修正地球自转不均匀而人为插入的额外一秒,历史上 UTC 曾多次插入闰秒。但 Unix 时间戳从不插入闰秒:它规定每个「日」恒等于 86400 秒,哪怕那天真实物理长度是 86401 秒。
那闰秒去哪了?按 POSIX 约定,闰秒被摊进相邻的某一秒里(惯例是并入前一秒或后一秒),由各系统自行选择自己的处理方式。也就是说,Unix 时间戳不等于原子时的精确计数,时间戳与真实原子时之间可能相差几十秒。对绝大多数业务(订单、日志、监控)这几十秒毫无影响;但如果你在做潮汐、授时、原子钟比对这类工作,就必须意识到这条时间轴是「均匀秒」而非「物理秒」。
2038 问题:32 位存储的悬崖
Unix 时间戳的标准类型是 32 位有符号整数,最大值 2147483647,对应 2038-01-19T03:14:07Z。再过一秒,计数器就溢出成负数,时间会「回到 1970 年前后」。这就是著名的 2038 问题。
解决办法是改用 64 位存储:64 位有符号整数的上限约 9.22 × 10^18 秒,足够写到公元 2.9 亿年。现代语言和数据库的时间类型基本都是 64 位,问题主要残留在早期嵌入式设备、老式文件格式、以及用了 int32 字段的旧数据库表里。如果你的时间戳字段是 int32,尽早迁走。
写代码时的时间戳易错点
最常见的第一个坑是单位:有的接口返回 10 位秒,有的返回 13 位毫秒,混用会让结果差 1000 倍,表现为「时间跳回 1970 年」或「日期飞到几万年以后」。用位数判断,或者在代码里显式约定字段单位,是最省事的防线。
第二个坑是浏览器与 Node 里的毫秒约定。JavaScript 的 new Date() 一律只接受毫秒,所以把秒级时间戳直接塞进去必然出错:new Date(1700000000) 得到的是 1970-01-20,而正确写法是 new Date(1700000000 * 1000);反过来要拿秒级就用 Math.floor(Date.now() / 1000)。Python 走的是另一套:datetime.datetime.fromtimestamp() 默认按秒解释,而 datetime.datetime.utcfromtimestamp(1700000000) 得到 2023-11-14T22:13:20Z,int(time.time() * 1000) 则是毫秒。
第三个坑是数据库字段类型。MySQL 里的 TIMESTAMP 类型存的是秒,并且会按会话时区做转换(所以它存进去的其实是 UTC 语义);而 DATETIME 存的是字面日期时间,不做时区转换,适合存「营业时间」「生日」这种没有时区含义的数据。往 TIMESTAMP 字段里塞 13 位毫秒,会因为超出范围而被截断或报错,这是排查「时间不对」时容易漏掉的一环。MyBatis / JPA 里的 java.util.Date 与 java.sql.Timestamp 也常有毫秒与纳秒的精度差异。
第四个坑是把本地时间当 UTC 存。datetime-local 输入框给的是不带时区的本地墙上时间,你必须明确它代表哪个时区,再换算成时间戳入库。少做这一步,跨时区业务就会漂几个小时。用本工具把时间戳转成可读时间再和日志、数据库逐项核对,是定位这类问题最快的办法。
为什么计算机普遍用「时间戳」而不是可读日期
原因很实际:其一,时间戳不含时区、语言和格式歧义,服务器在新加坡还是纽约,同一个数代表的都是同一个绝对时刻;其二,数字比较和排序非常直接,数据库索引可以直接建在整数列上,也不需要每次查询都做字符串解析;其三,它天然支持时间运算,「加一小时」就是加 3600 秒,「相差多少天」就是两个数相减除以 86400,逻辑清晰且没有时区陷阱。
相比之下,可读的日期时间(如「2026 年 8 月 27 日 10:30」)在不同时区、不同语言、不同格式下含义不同,还牵涉闰年、夏令时、闰秒一堆规则,用来做程序逻辑极易出错。
时间戳转换常见问题
为什么有的接口返回 10 位、有的返回 13 位?
因为它们生成时间戳的来源不同。Unix 的 time()、Java 的 System.currentTimeMillis()、MySQL 的 UNIX_TIMESTAMP() 都是秒级(10 位);而 JavaScript 的 Date.now()、.NET 的 DateTimeOffset.ToUnixTimeMilliseconds()、Go 里把 time.Now().UnixMilli() 取出来都是毫秒级(13 位)。同一份「用户注册时间」字段,A 系统存 1790090593、B 系统存 1790090593950,落到同一个库里就会差 1000 倍。所以接口文档里必须写明单位,字段命名上也建议带后缀(如 created_at_ms)。
10^11 这个自动判级分界是怎么来的?边界值会怎么判?
分界取 10^11 是倒推出来的:秒级时间戳要到公元 5138 年才涨到 1e11,而 1e11 毫秒对应的是 1973 年 3 月 3 日 —— 一个早已存在的真实毫秒值。于是「大于等于 1e11 判毫秒、小于判秒」在可预见的将来几乎不会错。边界行为上:99999999999(11 位)落在分界之下,判为秒,读出 5138-11-16T09:46:39Z;100000000000 正好等于分界,判为毫秒,读出 1973-03-03T09:46:40Z;100000000001 判为毫秒,读出 1973-03-03T09:46:40.001Z。若你的数据里真有小于 1e11 的毫秒时间戳(早于 1973 年的毫秒),自动判级会失手,这时必须手动指定单位。
Unix 时间戳为什么不含闰秒?那闰秒去哪了?
因为 Unix 时间的设计目标是让「每天等于 86400 秒」这条规则永远成立,从而让日期算术完全确定;插入闰秒会打破这个不变量。闰秒并没有消失,按 POSIX 标准它被摊进相邻的某一秒(通常是前一秒或后一秒),具体归属由各系统自行约定。因此 Unix 时间戳是一条均匀秒时间轴,与原子时之间可能累积几十秒偏差。日常业务无需关心,但涉及授时、原子钟比对时必须知道这个前提。
MySQL 的 DATETIME 和 TIMESTAMP 存的是什么?
TIMESTAMP 存的是秒级时间戳语义,范围大致 1970 到 2038,并且会按会话时区在写入和读取时自动换算 —— 你给它一个本地时间,它按 UTC 理解并存;读出来时又按当前会话时区还原。DATETIME 存的是字面日期时间,读写都不做时区转换,范围到 1000 年,更适合「营业时间 09:00-18:00」这类没有时区含义的数据。要存精确时刻且担心 2038,用 DATETIME(3) 或 BIGINT 存毫秒。往 TIMESTAMP 字段塞 13 位毫秒会超范围,这是常见故障点。
为什么 new Date(1700000000) 在 Node 里要除以 1000?
因为 JavaScript 的 Date 内部统一用毫秒表示时刻,构造函数的参数也是毫秒 —— 传入 1700000000 会被当成 1970-01-20 附近的某一刻,而不是 2023 年。正确写法是 new Date(1700000000 * 1000),得到 2023-11-14T22:13:20Z;要反向取秒级则用 Math.floor(date.getTime() / 1000)。同一规则也适用于 Date.parse() 之外的手工构造:凡是把 Unix 秒级时间戳交给浏览器 API,都必须先乘 1000。反过来,Python 的 time.time() 返回浮点秒、time.time() * 1000 才是毫秒,与 JavaScript 正好相反,这正是跨语言排查时间偏差时最常踩的一处。
把时间戳换算成「几天前」要注意什么?
第一,要用当前时间戳相减,而不是拿目标时间戳直接除以 86400 —— 只有做差才得到「间隔」。第二,结果要向下取整:相隔 23 小时 59 分只有零点几天,四舍五入会显示成「1 天前」,语义反而不准。第三,跨夏令时地区时,「1 天」不等于 86400 秒:夏令时切换当天本地一天只有 23 或 25 小时,用固定 86400 去算会差出 1 小时。第四,如果是用固定基准时间(如「今天零点」)做参照,请确保参照基准本身是同一时区口径,否则会出现「凌晨 8 点算出来已经过了 0 天」这类困惑。用本工具取到秒级与毫秒级两个值,再拿它们做差,是最不容易出错的做法。
结果怎么用?
得到时间戳与本地/UTC 时间后,你可以核对方便于调试、日志定位、数据库校验或跨时区业务处理;也可以把某个具体时间一键转成秒/毫秒时间戳,用于接口传参或测试。建议的排查顺序是:先用秒级时间戳确认量级对不对(看年份是否落在预期区间),再确认单位(看位数是 10 还是 13),最后才去比对时区偏移 —— 前两步能排掉绝大多数「时间不对」的问题。配合日期计算器做日期加减推算,配合工作日计算器排除周末与节假日,能覆盖排期与工时的常见需求。