2000年问题

<image>

2000年问题（Year 2000 Problem，别称Millennium Bug或Year 2000 Bug，简称Y2K），中国大陆及香港常称千年虫问题，台湾则称千禧虫危机，是一个计算机程序在日期表达上的设计缺陷。许多早期设计的计算机程序为节省存储空间而普遍采用以6位数字存储日期，只取4位年份数字中的后2位数字用于表示年份的做法。由于表示世纪的前两位年份数字被剔除，受此影响的计算机无法正确区分不同世纪的同一两位数年份，最终导致其在处理跨世纪信息时日期识别紊乱，并在日期比较、排序、期限计算、排程或数据交换等操作中产生错误，进而影响相关系统的运行。

使用6位数日期的做法普遍于20世纪中期的程序设计中，这些程序设计时并未设想长期使用，也鲜有处理涉及前两位年份变更的长跨度日期信息需求，随着这些早期设计发展为行业标准，这种带有缺陷的日期表示法也被保留下来。直到21世纪和2000年临近，而6位数日期正会导致计算机在前两位年份数由19跨越至20时出现问题，该缺陷由此被广泛称为2000年问题。

自20世纪90年代开始，2000年问题逐渐透过媒体的传播引起了人们的关注，同时也引发了大众恐慌。政府部门、军队、基础设施与许多重要行业直到那时都还使用着老旧的信息系统。一旦工作在这些领域的计算机出现问题，将可能导致从停水、断电，银行瘫痪到核电厂事故和军械失控等一系列灾难性的后果。针对该问题进行的全球性大规模修复行动随之开始，全世界为修复2000年问题前后累计投入了约3080亿美元，而在持续数年的修复之后，该问题最终得到了有效解决，没有在新千年到来之际大规模爆发。

## 问题起因

发布于1959年的IBM 1401的磁芯存储器模块，其容量仅为4KB的版本即要价20100美元，换算到2015年时即为162000美元。

虽然2000年问题的中文别称“千年虫”常使人将其误认为电脑病毒，但实际上它只是一个计算机程序在日期表示方面的缺陷，并非恶意编写的电脑病毒。该问题起始于20世纪50年代到60年代，那时计算机的存储器价格极为昂贵，并且容量很小，早期的磁芯存储器价格平均为每比特1美元，而在同一时期，一块大小仅为1MB的硬盘价格即为761,000美元。且当时的计算机仍旧采用打孔卡来存储和输入数据，编写好的程序本身就要被存储到打孔卡上，然后再被机器读取并编译，有时候向程序输入数据也需要使用打孔卡。一个最常见的IBM打孔卡只有80列宽，能储存的数据也极其有限。因此，程序员在编写程序时需要尽可能的节省存储空间，以最大程度的降低存储成本。因此，当时的程序员普遍在编写程序时采用六位数来存储日期，只取四位数年份中的后两位来表示年份，例如，1976年10月23日就会被简短的存储为23/10/76或10/23/76。对当时的情况来说，六位数日期表示法有很大的作用，它能够减小程序的内存需求，并减少一个程序所要占用的打孔卡卡数。这种做法沿袭自更早之前的穿孔制表机时代，当时的操作员就已经在打孔卡上惯用六位日期来节省时间和空间。在要处理大量日期数据时，六位数日期表示法还能发挥更大的作用。例如，一间拥有一千万客户，每个客户每天交易三次的银行若要在计算机中存储一周所有客户的交易时间，只取两位数年份能为这家银行省下60兆字节的存储空间。

后来，随着科技的发展，到了20世纪70年代，磁芯存储器的价格逐渐下降到仅为每比特1美分。而同时，具有同样价格，但尺寸更小且更便于使用的DRAM也开始逐步替代了磁芯存储器，打孔卡亦被逐渐淘汰，存储技术的进步和其价格的大幅降低使得存储空间不再是一个紧迫的问题。在程序中使用六位数日期的必要性也越来越低，但其并没有就此退出历史舞台，尽管存储介质历经数次变迁，但其中的数据结构通常变化不大。出于程序员们的习惯，以及为了保持向上兼容，六位数日期表示法就被一直沿用下来。即使当时已经有人提出过在这一日期表示法下潜藏的问题，但当时的程序员普遍都认为自己编写的程序不可能被持续使用到新千年，并没有理会这些警告。

IBM于1964年推出的大型机System/360是世界上首款实现多规格机型相互兼容的计算机，它定义了包括直到今日都仍在使用的单字节8比特长度在内的多项计算机行业标准，对之后多年的计算机设计都产生了深远的影响，而IBM在这套系统中继续沿用了六位数日期表示法；1967年，美国政府要求美国国家标准局统一计算机日期信息处理标准，以结束围绕该问题多年的争论，出于对存储空间成本的考量，使用有大量计算机的美国国防部在标准局的意见征集中强烈要求使用两位数年份，迫于国防部的压力，美国国家标准局宣布，从1970年1月1日开始，美国联邦机构将优先采用两位数年份来标示日期。随后，美国国家标准局将六位数日期表示法写入了1968年11月发布的联邦信息处理标准4号出版物《Calendar Date》中，在这份标准给出的示例中，1967年1月15日应当被编码为“670115”。不过，这份标准亦有规定，若日期信息跨度超过一个世纪，则可将年份位数扩展至四位；与之相对的，若日期信息跨度不超过十年，则年份位数还能被缩短为一位。

六位数日期表示法虽然的确在节省存储空间上发挥出了作用，但这种模糊的表示法也为未来埋下了隐患。人们在日常生活中为了方便有时也会只用两位数简称一个年份，但计算机和人类的区别在于，人类拥有能够理解并补全前两位年份数字的能力，但计算机没有。它们只会读取被实际写出来的后两位年份，无法自行变更没被实际写出来的前两位年份数字。这就导致了一个问题：在六位数日期下，2000年1月1日将会被转为01/01/00，然而，由于前两位年份没被实际写出来，也就无法进行变更，计算机就会继续将前两位默认为“19”，如此一来，就使得计算机会将2000年误认为1900年，认为被缩写为“00”的2000年要早于被缩写为“99”的1999年。这会导致计算机在执行任何涉及到日期的任务时出错，甚至引发崩溃。例如，医生要使用一台计算机为一位出生在2000年的婴儿计算要为他使用的药物剂量，若这台计算机仍旧使用六位数日期表示法，其就会将这位婴儿误认为是一位出生在1900年的百岁老人，并对医生给出一个适用于老年人的药物剂量，但这个剂量对新生儿来说可能会是致命的。若不对该问题加以修复，诸如此类的故障会在2000年到来时在全球爆发，影响到所有依赖这些有缺陷的计算机运作的基础设施和军政机关，导致停电停水、医疗瘫痪、通信终止到导弹误射等一系列灾难性的后果。

此外，2000年问题也会在除2000年1月1日以外的日期造成影响，并且也不止会导致计算机误判2000年及之后的年份。因为问题的根源是计算机无法变更前两位的年份数字，其也会导致计算机错误判定1900年之前的年份；而一些使用数字99或9999作为文档终止标记或停止代码，且仍旧采用两位数年份表示法的计算机则可能会在1999年9月9日出现故障；另外，一些计算机由于采用了不正确的闰年识别算法，致使其无法将2000年正确识别为闰年，从而导致它们在2000年2月29日、3月1日、12月31日、2001年1月1日出现故障，该问题也在2000年问题的范畴之内。其它会致使2000年问题的原因还包括将“19”硬编码到软件子程序中、采用可能会致使存储寄存器溢出的日期数据类型。

## 修复方式

对2000年问题最直接，也最彻底的修复方式就是直接将六位日期纠正为完整的八位日期。但这种方式的成本通常也是最高的，因为要完全纠正所有引用和使用了两位数年份的程序，以及所有引用了日期数据的数据库，确保整套系统内所有程序都被正确转换到了新日期格式，因为未经修改的旧程序可能会直接访问到日期字段的内部结构，从而又再度破坏掉修正后的系统；此外还需要调整所有数据字段的位置引用，以及所有包含日期数据的记录的格式；重新格式化并重写包括过往数据文件在内的所有数据文件，同时还可能需要修改各个系统之间的消息格式。

为了降低修复所需的时间和人力成本，另有一些更为简便的修复方式被广泛使用：

日期窗口化：保留原始的两位数年份写法，在此基础上设定一个年份数字的“基准点”，例如40。并使计算机将高于这个基准点的数字识别为以“19”开头的年份，例如“89”就会被这套系统辨识为“1989”。而低于这个基准点的数字则会被识别为以“20”开头的年份，例如“15”就会被辨识为“2015”。从而达到使计算机顺利度过2000年，维持正常年份更迭的目的。由于之前的两位数年份写法被保留，程序员只需要基于这个方式找到找到系统内所有涉及日期的引用，并适当调整它们的计算方式即可，故而相较于直接补全日期减少了很多工作量和修复成本，也是最为常用的2000年问题修复方式。但这种方式并非永久性的解决方案，一旦计算机运作至超过基准点年份时，便会再次出现问题，且经这种方式处理的计算机也没有办法处理任何时间跨度超过100年的数据，应用这种修复方式的初衷只是期望可使计算机延寿至下一次系统更迭。在实际应用上，基准点是视具体情况而定的，例如，对某间成立于1928年的公司来说，要修改他们用来处理发票数据的系统，就最好将“25”应用为基准点。还有一些程序员在此基础上拓展出了“浮动窗口化（sliding windows）”的修复技术，可使基准点随时间而变动。

编码世纪信息：同样不改变原有的日期字段长度，在此基础上改变计算机的日期编码逻辑，将世纪更迭的信息存入6个字符中。例如，在每个字符均占8个比特的情况下，6位字符在计算机底层中就是48个比特。如此一来，就可以将从儒略日起点（公元前4713年1月1日）到现今为止经过的总日期数转换成一个48位的纯二进制数字存入计算机，就能使计算机存下非常遥远的日期，顺利过渡到2000年以后。这种修改方式具有多种实现策略，可行的实现还包括：将占两个字节的两位数年份作为一个整体的二进制数字存入完整的四位数年份、或是改变日期的表达方式，将六位数中的第一位用于代表世纪（如0=18开头年份、1=19开头年份、2=20开头年份），之后的两位继续用于表示年份后二位数字，剩下的三位数就以“一年中的第X天”（001——366）的表达方式来表示日期。

倒转系统时钟：直接将系统时钟倒转28年，这个倒转量可以保证在改变年份的情况下，星期和月份天数不变，且通常具有相同的闰年安排。之后再另于系统内部设置转换层，在输入和输出数据时减去和加回28年，衔接系统日期和当前正确日期。如此一来，到2000年1月1日星期六时，计算机里仍然是不会使原有的六位数日期发生紊乱的1972年1月1日星期六。因为不涉及到对系统本身的修改，故这种修复方式适用于源代码丢失或无法再次编译的系统。此外，这种修复方式也只适用于每四年有一个闰年的时期。

## 修复行动

### 引起关注

<image>

1958年，当时在IBM工作的鲍勃·贝默在为耶稣基督后期圣徒教会编写族谱记录软件时意识到了六位数日期写法的缺陷，他在之后的数十年里不断在各处做出对六位数日期写法缺陷的担忧，并呼吁人们转用标准的八位完整日期写法，但都没有起到很大的作用。其于1971年在《霍尼韦尔计算机期刊》上发表了一篇题为《What's the Date？》的文章，建议人们使用ISO 2014标准的规范日期写法，这被认为是世界上第一篇公开发表的2000年问题警告文章，1979年，他再于杂志《界面时代》发表另一篇文章《Time and the computer》，进一步深入探讨了日期表示问题，他在这篇文章中也提到了2000年闰年问题，他还为COBOL添加了PICTURE子句，以允许程序员能够在程序中使用四位数年份，此后他一直坚持向业界发出警告，直到他在1982年退休。

1993年，美国杂志《Computerworld》刊登了一篇题为《Doomsday 2000》的文章，这篇文章的作者是加拿大程序员彼得·德·耶格，他在1977年于IBM上任操作员，在职期间，他意识到两位数的年份写法会为未来埋下隐患，并向管理层反馈了这一问题，但同样也没有起到作用。1993年，Computerworld的一名编辑在他进行了一场关于个人电脑的演讲之后找到他，希望他能就这场演讲的主题在Computerworld上发表一篇文章，不过彼得则提议要另写一篇“真正的问题”，于是便以2000年问题为主题，撰写了《Doomsday 2000》，这篇文章普遍被认为是引起计算机行业开始关注2000年问题的文章，彼得·德·耶格也因此被认为是将2000年问题带入行业及大众视野的人，此后也作为2000问题专家大量获利。

1995年，IBM承认2000问题的存在，并开始向其客户提供问题的解决服务、工具与支持，而微软公司的联合创始人与时任CEO比尔·盖茨则在1996年7月表示2000年问题只是由“老化的大型机软件”所导致的，并坚称微软的软件不会受到2000年问题的影响，而后其又于1997年的一次行业峰会上表示对2000年问题的担忧要归咎于“喜欢讲恐怖故事”的人，直到一年后，微软才承认他们在2000年问题上“反应迟缓”。