写代码的人查 Unix 时间戳该看的那一页:讲清楚 unix timestamp 是什么——从 1970-01-01 00:00:00 UTC 起算的秒数、与时区无关、一天恒为 86400 秒;配一个够用的双向换算,位数自动认成 10 位秒、13 位毫秒、16 位微秒、19 位纳秒;主体是 Linux shell、Python、JavaScript、Java、Go、PHP、C#、MySQL、PostgreSQL、SQLite 与表格软件各自怎么取、怎么转的写法速查,每条带一键复制。全在本机算。
怎么用
这一页是给写代码、查日志、对接口的人用的:先把 Unix 时间戳这件事讲清楚,再给够用的换算和各环境的现成写法。整页的计算都在你这台设备的浏览器里完成,不发任何网络请求。
首屏:走秒的当前时间戳
进页面第一眼是两个实时跳的大读数——秒(10 位)与毫秒(13 位),下面一行同时给出同一瞬间的北京时间与 UTC。这三个数字来自同一次取值,所以彼此严格对得上,不会出现「秒读数跳了、日期还没跳」的错位。
秒读数是对齐整秒边界刷新的:每次都先算到下一个整秒还差多少毫秒,再安排下一次刷新,而不是每隔 1000 毫秒盲跳一次。这样它和系统时钟的秒位是同一刻翻的。
两个读数各带一个复制按钮,点一下就进剪贴板。要用鼠标选中数字,先点暂停让读数停住,再点一次恢复走秒。页面切到后台标签页时走秒会停下来,回来自动接上——读的是系统时钟不是累加,切走再回来不会走偏。
如果只是想要一个「打开就是现在的时间戳」的大读数,去 当前时间戳,那一页就做这一件事。
一、时间戳 → 日期
把一串数字填进输入框,结果即时出来(点转换或按回车也行)。
- 位数自动识别:11 位及以下当秒、12 到 14 位当毫秒、15 到 17 位当微秒、18 位及以上当纳秒。负数表示 1970 年以前。输入里的正负号、空格、下划线和千位逗号都会被忽略,从别处复制来的带格式数字可以直接贴。
- 手动指定单位:自动认错了(比如你手上是一串只有四五位的秒数),在「单位」下拉里直接选秒、毫秒、微秒、纳秒。
- 出来的读数是:北京时间、UTC 世界时、换算成秒的写法、换算成毫秒的写法,以及「距现在多久」这一句人话。选了「本机时区」时最上面还会多一行本机时区的显示,括号里写着你这台设备当前的偏移。
16 位与 19 位的输入会保留毫秒以下的精度,结果的小数位上能看到完整的微秒或纳秒。这一步在支持 BigInt 的浏览器里走 BigInt,老浏览器退回按字符串截位,两条路算出来的结果逐字符一致(单元测试两条都跑)。
二、日期 → 时间戳
选日期、选时刻(可精确到秒),再选这个时刻属于哪个时区,就得到对应的时间戳,秒、毫秒、微秒、纳秒四种写法各一行,外加换算后的 UTC 时刻。点填入现在可以一键填成此刻。
时区这里只给三档:UTC、北京时间、本机时区。前两档是纯算术(北京时间恒等于 UTC+8,中国大陆自 1991 年起不再实行夏令时),第三档交给你设备上的浏览器自己算——它知道自己的时区规则和夏令时,所以本页源码里一个夏令时日期都没有写死。要按别的城市或 IANA 时区换算,去 时间戳转换,那一页有二十来个时区可选,还能一次转一整批。
三、各语言与数据库怎么取、怎么转
这是本页的主体:一张十一行的速查表,分「取当前时间戳」和「时间戳转日期」两栏,覆盖 Linux 与 macOS 的 shell、Python、JavaScript、Java、Go、PHP、C#、MySQL、PostgreSQL、SQLite,以及表格软件里的公式。每一条代码后面都有一个复制按钮,不用手抄也不怕抄错。每行末尾有一句说明,写清这条给的是秒还是毫秒、有没有小数、跨系统要注意什么。
四、常识卡
页面最下面五张小卡,分别讲起点在哪、为什么与时区无关、四档位数怎么分辨、2038 年问题、闰秒为什么不算。2038 那张卡上的日期是页面现算出来的,不是写死的文字——由 2 的 31 次方减 1 直接推出来,单元测试核过。
怎么看结果
先数位数,再看年份
拿到一串来路不明的数字,判断顺序永远是:先数位数定单位,再看换出来的年份合不合理。年份一旦落在下面这几个位置,基本可以断定不是时间戳本身的问题,而是单位或口径错了。
| 换出来大约是 | 多半的原因 |
|---|---|
| 1970 年 1 月中下旬 | 把毫秒数当成秒读了(该除以 1000) |
| 五万多年以后 | 把秒数当成毫秒读了(该乘 1000) |
| 1970 年 1 月 1 日前后几秒 | 这串数字根本不是时间戳,多半是自增 ID 或计数器 |
| 1970-01-01 08:00:00 | 值是 0,某个字段忘了赋值——北京时区下的 0 时间戳正是这一刻 |
| 页面直接报「超出范围」 | 数值超过了正负 8.64e15 毫秒,多半是把纳秒当毫秒读了 |
「距现在」那一行怎么用
这一行给的是「3 天前」「2 小时后」这类人话,用途是快速判断合不合理:查一条刚发生的日志,它应该是几秒前到几分钟前;如果显示「3 年后」,那不是时钟问题就是数据问题。注意这一行会随时间变化,不适合截图存档,要留证据请用上面那几行绝对时刻。
毫秒以下的那几位
16 位和 19 位的时间戳,毫秒以下还有 3 位或 6 位。这些位在网页里只能显示,不能生成——浏览器能拿到的最细时刻就是毫秒,所以「日期 → 时间戳」给出的微秒、纳秒写法末尾是补零的,页面上也写了这句话。真要纳秒精度,得在服务端用支持纳秒的接口取(Go 的 UnixNano、Java 的 Instant 一类)。
页面提示的那几句
- 「这串有 13 位,按位数认成毫秒级时间戳」:自动识别的结论,不对就去「单位」里手动选。
- 「这一串里有不是数字的字符」:时间戳只能是整数。带小数点的(比如 Python 的 time.time() 直接打印出来的那种)先把小数点后面去掉,或者改填 13 位毫秒。
- 「超出了 JavaScript 能表示的时刻范围」:数值绝对值超过 8.64e15 毫秒,对应大约公元前 27 万年到公元 27 万年之外。正常业务数据不会到这儿,八成是单位选错了。
- 「这个浏览器没有 BigInt」:很老的浏览器会看到这条,此时 16 位与 19 位改走字符串截位,结果一样,只是慢一点。
负数不是错误
负时间戳表示 1970 年以前,是合法的。−1 秒是 1969-12-31 23:59:59,−2208988800 秒是 1900 年 1 月 1 日。本页对负数按向下取整处理,所以 −1 纳秒给出的是 1969-12-31 23:59:59.999999999,而不是把余数算反。但要提醒一句:不是所有语言和数据库都接受负时间戳,跨系统传 1970 年以前的时间,最好改用日期字符串。
常见问题
Unix 时间戳和「时间戳」是一回事吗?
日常说的「时间戳」在中文语境里通常就是指 Unix 时间戳,本页讲的也是它。但要小心两个同名的东西:一个是编程语言或数据库里的 timestamp 类型(比如 MySQL 的 TIMESTAMP 列、PostgreSQL 的 timestamptz),它们内部存的不一定是那个整数,显示出来也是日期格式;另一个是电子存证里的「可信时间戳」,那是第三方机构对文件摘要签发的时间凭证,用来证明文件在某时刻之前已存在,需要向相应机构申请,不是网页能生成的。三者名字撞车,做的事完全不同。
Linux 时间戳和 Unix 时间戳有区别吗?
没有区别,是同一个东西的两种叫法。Linux 继承了 Unix 的时间体系,命令行里 date +%s 取到的就是标准的 Unix 时间戳。会让人以为有区别的是两件周边的事:一是 Linux 里的 文件时间戳(atime、mtime、ctime 三个时间),那是文件系统记录的三个时刻,底层存的仍是 Unix 时间;二是 GNU 版 date 和 BSD 版 date 的参数不一样,同一条转换命令在 Linux 上能跑、在 macOS 上报错,于是被误当成「两种时间戳」。
为什么各家取到的时间戳末尾差几毫秒?
因为取的是各自机器的系统时钟,而机器之间本来就有偏差。浏览器里取到的时间戳来自你这台设备的时钟,它准不准取决于系统的自动对时有没有开、上次同步是什么时候。想知道自己这台设备快了还是慢了,用 时间校准 量一下;要看 NTP 服务器地址,去 NTP 时间服务器。顺带说一句,在浏览器里做性能计时别用时间戳相减——系统时钟可能被对时服务往前往后拨,测出负数都有可能,那种场合要用单调递增的性能计时接口。
能一次转一大批吗?能按纽约、伦敦的时区看吗?
本页只做核心的一进一出,批量和多时区在 时间戳转换:那一页可以一次贴进上千行、时间戳与日期混着放,结果导出成制表符分隔的文本直接贴进表格软件,时区下拉里有二十来个常用城市。想查某个时区缩写是什么意思(EST、PST、GMT、UTC 这些),去 时区缩写对照;想弄明白东八区那套说法,去 UTC+8。
和 epochconverter、time.is 一类的站比,这一页有什么不一样?
那类站多半是英文界面、功能堆在一屏里。这一页把中文用户最常卡住的三件事拆开讲:这串数字到底是什么(概念与四档位数)、我这个环境怎么写(十一种语言与数据库的现成代码,带复制)、换出来不对怎么排查(上面那张错位对照表)。换算功能本身刻意做得克制,复杂的留给兄弟页。
手机上好用吗?
好用。页面在 400 像素宽的屏幕上完整可见,两块换算会自动叠成一列,长数字会换行显示,速查表的每一行改成上下排。手机上最常见的用法是:把某条通知或截图里的一串数字长按复制,贴进来立刻知道那是什么时候。
原理与冷知识
-
起点为什么是 1970 年。 这个日子被称作 Unix 纪元(epoch)。选它没有任何宇宙意义——Unix 正是在 1970 年前后成型,取一个刚过去不久的整年,计数器的数值就不会太大。早期实现甚至用过六十分之一秒作单位,那样一个 32 位整数只能装下两年多,很快就改成了秒。顺带一提,1970 年 1 月 1 日是星期四,所以时间戳 0 对应的是一个星期四的零点,本页的单元测试就拿这一点当基准。
-
一天恒为 86400 秒,是约定不是事实。 地球自转并不均匀,靠闰秒把原子时和地球自转拉齐。Unix 时间选择完全忽略这件事:每天铁定 86400 秒,闰秒被抹平。好处是时间戳与日期之间的换算成了纯算术,任何语言几行代码就能实现;代价是它严格说来不等于「从纪元起真实流逝的秒数」,两个时间戳相减得到的差值里少掉了这期间的闰秒。不同系统抹平闰秒的方式还不一样:有的让那一秒的时间戳重复一次,有的把它摊到前后若干小时里慢慢走。
-
为什么很多接口要毫秒。 秒的粒度在今天太粗:一秒里可能有成千上万条日志,靠先后顺序去重或排序时秒级根本不够分。毫秒是个折中——13 位数字仍在 JavaScript 能精确表示的整数范围内(安全整数上限约 9007 万亿,而毫秒时间戳才 1.8 万亿);到微秒 16 位还没超,纳秒 19 位就超了,必须用 BigInt 或字符串处理,否则末几位会悄悄变成 0。本页处理 16 位与 19 位输入时优先走 BigInt,正是为了避开这个坑。
-
表格软件里的 25569 是怎么来的。 Excel 和 WPS 把日期存成「从 1900 年 1 月 1 日起的天数」,1970 年 1 月 1 日在这套序号里正好是第 25569 天。所以日期转秒是先减 25569 再乘 86400,秒转日期是除以 86400 再加 25569。这两条公式都是 UTC 口径,本机时区要自己加减;算出来的是个日期序列值,得把单元格格式设成日期才看得懂。另外那套序号里有个著名的历史包袱:1900 年被错当成了闰年,所以 1900 年 3 月以前的日期会差一天——这是为了兼容更早的表格软件故意留下的。
-
时间戳里没有时区,这是它最大的优点。 夏令时规则各国自己定、还时不时修改,历史上更是五花八门。系统里一旦把时间存成「本地时间字符串」,那些规则就全成了自己的负担;存时间戳,规则只在显示那一刻用一次,而且用的是操作系统或浏览器自带、会随系统更新的时区数据库。本页也是这么做的:本机时区那一档直接问浏览器要,源码里没有写死任何一年的调钟日期。
-
2038 那个数不用背。 2 的 31 次方减 1 等于 2147483647,这串数字很多人眼熟,因为它在各种「最大值」「永不过期」的字段里出现过太多次。看到一批记录的时间齐刷刷停在 2038-01-19,通常不是那天真有事发生,而是某处把「最大值」当成了「永远」。同理,看到满屏 1970-01-01,那多半是某个字段忘了赋值。
-
epoch 这个词的其它去处。 英文里 epoch time、Unix time、POSIX time 指的都是同一件事。但 epoch 在机器学习里是「把训练集完整过一遍」的意思,在天文里是「历元」,跟时间戳没有关系——检索英文资料时这几个意思会混在一起。
-
为什么 date 命令没有毫秒。 秒级的 date +%s 是 POSIX 里就有的东西,各家实现一致;毫秒和纳秒属于各家扩展,GNU 有自己的写法、BSD 没有,所以跨平台脚本里通常不直接用,而是转手交给脚本语言去取。本页的速查表因此只列了各环境里稳定存在的那几条——写得少是故意的,能到处跑比花哨重要。