Zune 30の閏年バグ
http://d.hatena.ne.jp/taizod/20090103/p2なんだけど、あんまりおかしいので再現してみた。
(defun hoge (days &optional (year 1980)) (while (> days 365) (if (is-leap-year year) (if (> days 366) (progn (decf days 366) (incf year))) (progn (decf days 365) (incf year)))) (values days year)) (defun is-leap-year (year) (or (zerop (mod year 400)) (and (not (zerop (mod year 100))) (zerop (mod year 4)))))
閏年の判定関数は適当に作った。これにformatをくっつけて回すとこんな感じ。問題になるのは閏年の12月31日だけだ。
(hoge 10593) 1980-10593 うるう年 1981-10227 1982-9862 1983-9497 1984-9132 うるう年 1985-8766 ... (中略) 2006-1096 2007-731 2008-366 うるう年 2008-366 うるう年 2008-366 うるう年 ... Quit
マイクロソフト Zune 30GBに閏年バグ、12月31日に動作停止 (追記:gigabeatも) - Engadget 日本版で書かれているとおりの症状。なんか記述がごちゃごちゃして判り辛かったけど、バグの原因はwhile条件が「365日より多い」なのに、閏年の処理条件が「366日より多い」だから。閏年で366日だとループ内でなにもしないことになる。もちろん、日数チェックを一回だけにすればこんなことは起こらない。余計な重複はバグの温床という典型的な例だね。
(defun fuga (days &optional (year 1980)) (let ((d (if (is-leap-year year) 366 365))) (if (> days d) (fuga (- days d) (1+ year)) (values days year))))
仕様に沿って書くとこんな感じ。それにしても何で1を基底にしてるんだろう? 0からの方が見通しが良いと思うけど。