来源 | 最后一个bug
1
Unix时间戳
Unix时间戳说白了就是一个计数器:从1970年1月1日 00:00:00 UTC开始,每过一秒就加一。你在代码里调用 time() 拿到的那个整数,就是这个计数器的当前值。
/******************************************
* Fuction: 获取Unix时间戳
* Author : (最后一个bug)
******************************************/
#include <stdio.h>
#include <time.h>
int main(void)
{
time_t now = time(NULL);
printf("当前时间戳: %ld\n", (long)now);
return 0;
}
运行一下:
当前时间戳: 1781184000
这个数字看着挺大,但理解起来极其简单——就是个秒计数器。任何两个时间点的差值,两个整数一减就出来了,不需要考虑闰年、月份天数、时区这些乱七八糟的东西。这也非常符合Unix 时间戳最核心的设计哲学:用最简单的数据结构搞定时间表达问题。
2
为什么是1970?
这应该是大部分人最开始了解Unix时间的疑问吧,为何是距离1970年的秒数,不是其他的比如2000年呢?
我大致了解了下背景,感觉挺有意思的:
Unix 操作系统是 1969 年夏天 Ken Thompson 在贝尔实验室的一台 DEC PDP-7 上开始写的。当时他老婆带着刚出生的儿子去加州探亲了,他一个人在家"闭关"了一个月,写下了 Unix 的文件系统、Shell、编辑器和汇编器——时间处理自然也是他一手搞的,简直不要太强~
但 1970 年这个日期,不是一开始就定好的。
早期的 Unix(大约 1971 年的第一版),epoch 实际上是 1971 年 1 月 1 日,计数器单位用的还是 1/60 秒;但是32 位无符号整数,用 1/60 秒做单位,只能覆盖大概两年半,这显然不够用;后来他们把单位改成了整秒,范围一下扩展到了 136 年左右,算是能用了。
但 epoch 本身还在不停地变,因为计数器老是溢出,所以大家一直在改时间的起点,最后开发者们选了一个短期内不会溢出的日期,1970 年看起来也不错,就用它了~
到了 1973 年左右(大约 Unix 第四版),epoch 被最终锁死在 1970 年 1 月 1 日 00:00:00 UTC,一直用到了现在。
所以这个 1970 年不是什么"计算机历史的里程碑",它就是一群工程师被不停溢出的计数器搞烦了,随手选了一个整数年。
3
设计思想
Ken Thompson 和 Dennis Ritchie 这一代贝尔实验室的程序员,骨子里信奉一种设计理念—被称为 "Worse is Better"。即简单的东西比"完美"的东西更有生命力。
Unix 时间戳完美体现了这个理念:
一个整数,搞定一切。
时间差?整数减法。时间比较?整数比大小。存储成本?4 个字节。跨平台传输?不需要任何格式解析。
比如如果时间被设计成 "2026-06-12 15:30:00" 这样的字符串,那每次算时间差都得先解析字符串、处理闰年、处理月份天数差异……写个时间比较函数都能写出上百行代码来。
而 Unix 的做法呢?t2 - t1,一行完事。nice~
还有一个同样精妙的设计点:分离存储和展示。

内核只管维护一个单调递增的秒计数器,至于这个秒数怎么变成"2026年6月12日星期五下午3点45分",那是 gmtime()、localtime()、strftime() 这些库函数该操心的事。每一层只负责自己该管的,不越界。
还有一点容易被忽略——Unix 时间戳没有闰秒。
国际原子时和天文观测时间之间的微小偏移,会让 UTC 时不时需要插入或删除一秒(闰秒)。但 Unix 时间戳假装闰秒不存在——它把每一天都当作精确的 86400 秒来处理。遇到真正的闰秒时刻,系统通过 NTP 做微调来慢慢消化掉这个偏移。
这个设计看起来"不够精确",但正是这种"不精确"让 Unix 时间戳变得非常可预测、非常好做算术——你不用维护一张"闰秒发生表"来判断某个时间点到底存不存在。
4
2038年问题
int32_t),能表示的最大正数是 2,147,483,647 秒。溢出之后,时间"倒流"回了 1901 年——这就是著名的 "2038 年问题"。本质上跟千年虫一样,都是固定位宽的计数器溢出。

大家在搞嵌入式软件开发,特别是单片机开发中这个问题尤其值得留意。bug菌见过不少还在跑的工业设备软件不少还是 32 位的 time_t,这些大部分用的是比较早期的组件或者库,或者是开发者没有考虑到此问题。这些设备如果设计寿命超过 15 年,到 2038 年确实有可能踩到这个坑。
现代系统已经把 time_t 升级到了 64 位有符号整数,最大值大约是 2920 亿年。宇宙目前的年龄大概是 138 亿年——也就是说 64 位的 Unix 时间戳足够覆盖宇宙年龄的 20 多倍,那就不用再担心了~
------------ END ------------