很多人应该还记得,在迎来2000年之前,全世界都因“千年虫”而紧张不已。当时担心的是,只用两位数记录年份的程序可能会把2000年误认为1900年。然而在计算机世界里,还留着另一个与之相似的日期问题,那就是“2038年问题(Year 2038 Problem)”。
简单来说,2038年问题是指以秒数来计时的计算机无法正确表示2038年1月19日之后时刻的现象。它源于存放数字的空间大小是固定的,在英语圈也被称为Y2K38,或者由纪元(epoch)和末日(apocalypse)合成的“Epochalypse(纪元末日)”。
本文将依次介绍计算机计时的方式、2038年问题产生的原理、已经出现的案例与高风险设备,以及迄今为止准备好的解决办法。
时钟倒转的那一天:2038年问题
计算机如何计时

我们用年、月、日来记录日期,但很多计算机把时间记作一个很大的数字。
(1)Unix时间
Unix类操作系统以及受其影响的众多系统,以1970年1月1日0时0分0秒(协调世界时)为起点,通过计算自那时起经过了多少秒来表示时间。这种方式称为“Unix时间(Unix Time)”,起点称为“Unix纪元(Unix Epoch)”。
(2)用一个数字表示的好处
把时间归结为一个数字后,只需做一次减法就能求出两个时刻之间的间隔,存储和比较也很方便。因此,在文件创建时间、日志记录、计划任务等一切需要时间的地方,都使用了这种方式。
(3)固定大小的空间
问题在于存放这个数字的空间。长期以来,许多系统把Unix时间存放在“有符号32位整数”中,而这个空间能容纳的最大数字是2,147,483,647。
2038年问题产生的原理

如果不断往固定大小的空间里累加数字,总有一天会迎来溢出的时刻。
(1)最后一秒
从1970年起经过2,147,483,647秒的时刻,是协调世界时2038年1月19日3时14分7秒,换算成韩国时间是同一天中午12时14分7秒。这正是32位空间所能表示的最后时刻。
(2)溢出与符号翻转
再加上1秒,数字就会溢出空间,最前面的符号位随之改变。这称为“整数溢出(Integer Overflow)”,原本最大的正数瞬间变成最小的负数。
(3)倒回1901年的时钟
变成负数的时间会被解读为纪元之前的过去。结果,系统会误以为现在是1901年12月13日,依赖日期计算的功能可能出现异常。
不同之处在于,千年虫是因为用两位十进制数记录年份而产生的,而2038年问题则是因为把时间存放在32位二进制空间中而产生的。

已经出现的案例与高风险设备

2038年虽然还没到来,但在计算遥远未来日期的程序中,问题已经显现。
- 网页服务器超时(AOLserver):2006年5月,一款网页服务器程序把“永远不应结束”的请求的截止时间设定在10亿秒之后,当这个截止时间超过2038年时,计算出错,导致服务器停止运行。
- 邮件服务器的新年故障(Exchange Server):2022年1月1日,微软的邮件服务器程序用32位数字处理更新版本号,当数值超过上限时,恶意软件扫描功能停止了工作。
- 长期使用的嵌入式设备(Embedded Systems):汽车制动装置、路由器、网络摄像头等一旦安装就会使用几十年、却很少更新的设备,被视为最令人担忧的对象。
当然,并非所有设备都有风险。不需要日期、或只计算两个时刻之差的设备,可能不会遇到大问题。
防范2038年问题的方法

2038年问题没有一劳永逸的万能解决办法,因此多种方法正在同时使用。
(1)把空间扩大到64位
最广泛使用的方法是把存放时间的空间扩大到64位。这样一来,要发生溢出需要约2,920亿年,实际上成了一只没有尽头的时钟。大多数64位计算机的操作系统已经采用了这种方式。
(2)改造旧的32位环境
Linux从2020年的5.6版本开始,让32位设备也能使用64位时间,NetBSD和OpenBSD等操作系统也很早就转向了64位时间。不过,改变空间大小可能导致与现有程序不兼容,因此需要谨慎推进。
(3)在剩余时间里做好检查
即使操作系统改变了,如果运行在其上的程序或数据库仍用32位数字处理时间,问题依然存在。因此,提前找出并修复旧代码和旧设备的持续检查,才是最可靠的准备。
2038年问题表明,一个非常小的设计选择,可能在几十年后变成一个大难题。我们需要记住,眼下看似足够的空间,随着时间累积也终有溢出的一天,并提前做好准备。
认识到今天的便利选择可能成为明天的局限,正是设计经久耐用系统的第一步。