如果你上个月浏览过新闻网站,就会知道埃隆·马斯克和他那群十几岁的程序员正在联邦政府的IT系统中大肆搜集资源,寻找浪费之处。任何像联邦政府这样规模庞大的系统都存在浪费,这是不言而喻的。至于浪费的普遍程度、如何识别、如何削减等等,这些都属于政治问题,我们在此不做赘述。
但值得注意的是右侧的表格。它声称统计的是领取社会保障福利的人数。从表格上看,似乎有数百万120岁以上的老人在领取支票,这只能是由于社会保障署的失职,再加上受益人存在欺诈行为……对吧?
埃隆在推特上谈到了这件事:
根据社会保障数据库,以下是每个年龄段中死亡字段设置为 FALSE 的人数!
还有另一种可能:马斯克手下的这群天才少年以前从未接触过 COBOL 语言。
旧数据库
联邦政府拥有大量可以追溯到计算机诞生之初的系统。当人们谈论数百万行仍在使用的 COBOL 代码时,其中很大一部分都用于联邦和州政府系统。大型机在华盛顿特区依然运行良好,尽管到了 2025 年,没有人会使用该平台及其相关技术启动全新的项目,但这些系统仍在继续运行。“如果没坏就别修”这句话恰如其分地适用于那些经过数十年不断修复漏洞、已被充分理解的复杂 IT 系统。
千禧年危机(涉及大量COBOL代码的修复)过后不久,ISO 8601:2004标准发布。它将一些早已存在的规范正式化,并以1875年5月20日为零点(纪元)。换句话说,所有日期都从1875年开始计算。
《时代》
1875年5月20日似乎是一个奇怪的零点,但每个系统都有零点:
- Linux/Unix 的“纪元”是 1970 年 1 月 1 日。截至撰写本文时,它内部跟踪的是“自纪元以来的秒数”(1,740,088,474)。
- 在 .NET 领域,今天是公元 1 年 1 月 1 日。
- UUID 版本 1 使用 1582 年 10 月 15 日,这是格里高利历改革的日期。
- MUMPS 是美国一些医疗保健机构使用的编程系统,采用 1840 年 12 月 31 日作为起始日期。
- DOS 时代的文件系统(FAT16、FAT32 等)使用 1980 年 1 月 1 日。
这样做的原因是,计算机存储日期时并不使用“1976年2月16日”这样的格式,而是存储一个表示自某个固定时间点以来经过的时间的整数。在Unix系统中,这个整数曾是著名的32位整数(导致了“ Y2038问题”),但现在通常使用64位整数,其中某些数字用于提供亚秒级的精度。
COBOL
COBOL 没有特定的日期类型或格式。它有一些函数(例如 USAGE DISPLAY)可以将日期存储为数字或字母数字字符串(例如,PIC 9(8) 表示 YYYYMMDD)。日期操作通常需要自定义代码或第三方库。这就是 Y2K 问题如此严重的原因:在内存只有 16K 的时代,日期通常以 YYMMDD 格式存储,这会导致溢出风险。
千禧年之后,大多数系统都采用了 ISO 8601 标准,特别是 2004 版。而这些系统中的许多系统很可能将缺失的日期解释为零点。这会导致日期显示为 1875 年,以及 150 岁的人的数据。
想象一下这种情况:由于某种原因,死亡日期字段为空(记住,我们讨论的是数亿人,其中许多人的记录可以追溯到纸笔时代)。有人天真地查询数据库——这很可能不是 SQL,而是一个大型机数据集。由于不了解情况,他们编写了某种“死亡日期 - 出生日期 = 寿命”的逻辑,但由于死亡日期为空,这个逻辑出错了。如果你出生于 2025 年,死于 1875 年,那就是 -150 年,但你的代码可能只是假定死亡日期必须是一个正数。
或者假设出生日期由于某种原因为空。结果是一样的。
这并不能解释所有问题,但COBOL圈子里确实对此有一些讨论。你可能会认为,我们25年前应对千年虫问题的种种难题应该已经解决了这类问题,但技术债务却是一个出乎意料难以驯服的顽疾。