一年多一天,全世界的 Zune 一起罢工:微软的官方修复是「等明天」
目录
配图:AI 生成的概念示意图,非真实现场、产品截图或研究数据。
TL;DR:2008 年 12 月 31 日,大批 30GB 的第一代 Zune 在同一天开机卡死。原因是时钟驱动里把「天数换算成年份」的循环,在闰年的第 366 天永远出不去。微软给的办法是让电池耗尽,等元旦过了再充电开机;设备到第二天会自己好。
发生在什么时候
2008 年 12 月 31 日,这一年是闰年,这一天是第 366 天。当天凌晨前后,不少第一代 Zune 30(30GB 版)卡在启动画面,怎么按都没反应。因为是同一天、同一款机器、全球同时出事,网上很快有了「Y2K9」的戏称。
一个循环,吃掉了一整天
Zune 的实时时钟存的不是年月日,而是「自 1980 年 1 月 1 日起过了多少天」。要显示日期,驱动就得把天数换算成年份:一年一年往外减,平年减 365,闰年减 366。
据当时流传的代码分析(出自发现者在 ZuneBoards 上的帖子,Make 杂志转载,并非微软官方事后报告),闰年那一支写的是「剩余天数大于 366 才减」。到了 12 月 31 日,剩余天数恰好等于 366:条件不成立,没减;又因为是闰年,也走不到「年份加一」的出口。循环原地打转,设备便停在开机画面,看上去像死机,其实是在拼命空转。
这类 bug 很典型:边界条件差一位(「大于」本该是「大于等于」),平时完全不暴露,一年只有一天会触发,还偏偏发生在闰年。
官方方案:等
微软的说法是,问题出在时钟驱动,这个驱动来自 Freescale,只用于 Zune 30,所以其他型号不受影响。给用户的办法很朴素:让电池自然耗尽,然后在格林尼治时间 1 月 1 日中午之后充电开机。日期一过,天数不再是 366,循环自然跳出,机器恢复正常。
换句话说,这个 bug 的「补丁」是日历。微软同时表示会发布修复,避免下一个闰年(2012)重演。
值得记住的
- 时间相关代码的边界条件,要用「正好卡在边界」的值去测,比如闰年最后一天。
- 一年才触发一次的 bug,测试几乎一定抓不到,只能靠评审和对日期逻辑格外多疑。
- 硬件厂商的第三方驱动出问题,用户并不关心是谁写的;承担后果的是贴着品牌的那一方。
来源:
- Wikipedia:https://en.wikipedia.org/wiki/Zune
- Computerworld《Zune chokes on leap year bug》:https://www.computerworld.com/article/2529992/zune-chokes-on-leap-year-bug.html
- Make《Cause of ZUNE leapyear problem – Freescale date routine》:https://makezine.com/?p=60818