"现在几点了?"这个问题对人来说轻而易举,对计算机却出奇地棘手。时间是连续的、依赖地理位置的、还要受各种历法规则的影响。为了让机器能可靠地存储、比较和传递时间,工程师们发明了一种朴素而强大的方案:Unix 时间。它用一个数字来表示某个时刻,看似简单,背后却凝结着关于参照点、时区和精度的一系列深思熟虑的取舍。
用一个数字表示时刻
Unix 时间的核心想法极其简洁:选定一个固定的起点,然后用"从那一刻起流逝了多少秒"这个整数来表示任意时刻。这个起点被称为纪元,约定为协调世界时 1970 年 1 月 1 日零点。任意一个时刻,都可以表达成相对纪元的秒数。
这种表示的优雅之处在于,它把复杂的"年月日时分秒"压缩成了一个单一的、可以直接比较和运算的数字。两个时刻谁先谁后,只需比较两个整数的大小。
为什么选择一个固定参照点
用相对某个固定纪元的偏移量来表示时间,避开了无数麻烦。如果直接存储年月日,就得处理月份长度不一、闰年、不同历法等繁琐规则;而存储一个相对纪元的秒数,所有这些复杂性都被收敛进了"计算偏移量"这一件事里。
更重要的是,这个数字本身不含时区信息。它表示的是一个绝对的、全球统一的时刻——无论你在地球哪个角落,同一时刻对应的 Unix 时间都相同。这正是它适合在系统间传递时间的关键。
绝对时刻与本地时间的分离
Unix 时间最值得理解的一点,是它把"绝对时刻"和"本地显示"彻底分开了。这个整数本身不属于任何时区,它只是一个全球一致的瞬间。要把它变成某人看到的"上午九点",是另一步独立的工作:根据观看者所在的时区,把这个绝对时刻翻译成本地的钟表时间。
把存储和显示分开,是处理时间最重要的原则之一。系统内部统一用绝对时刻,只在面向用户展示的最后一刻才转换成本地时间,能避免绝大多数时区相关的混乱。
秒够用吗:精度的取舍
最初的 Unix 时间以秒为单位,对很多场景已经足够。但现代应用常常需要更细的精度——毫秒、微秒甚至纳秒,用于排序事件、测量耗时或满足金融、科学计算的要求。于是出现了用毫秒数或更小单位表示的变体。
精度更高意味着数字更大、占用更多空间,也带来一个隐患:不同系统可能用不同的单位。一个以秒为单位的时间戳和一个以毫秒为单位的时间戳,数值会相差一千倍。混淆单位是这类系统里非常常见的 bug 来源。
整数会有用完的一天吗
用固定大小的整数存储时间,会带来一个著名的隐患:当秒数增长到超出整数所能表示的上限时,数值会溢出,时间会突然"跳回"或出错。历史上用较小整数存储 Unix 时间的系统,就面临一个明确的临界年份。
解决之道是使用更大的整数来存储时间戳,把这个上限推到极其遥远的未来。这提醒我们:再简洁的表示方案,也要考虑它在数值边界上的行为,而不能假设它永远够用。
它不能替你回答的问题
Unix 时间擅长表示"哪个绝对时刻",但它故意不回答一些问题。它不告诉你这个时刻在某地是几点、是不是处于夏令时、那天是星期几。这些都需要结合时区数据库和历法规则另行计算。
认清这条边界很重要:Unix 时间是一个干净的、绝对的基准,而所有与人类可读时间相关的复杂性,都被推到了它之外的转换层。把这两层分清,是设计可靠时间系统的基础。
朴素方案的持久价值
Unix 时间之所以历经数十年仍是事实标准,正是因为它在简单与实用之间找到了极佳的平衡。一个绝对的、与时区无关的整数,便于存储、便于比较、便于传输,把人类时间的全部混乱都隔离在了转换层之外。
下次当你看到一个长长的时间戳整数时,不妨记住它代表的含义:从 1970 年那个固定起点至今流逝的时间,一个全世界都认同的瞬间。理解了这层含义,许多关于时间的设计难题就有了清晰的起点。