parseInt の落とし穴 - 8月発生の時限式バグ

何気なくScrapBookのbackupフォルダを覗いてみたところ、なぜか一番新しいバックアップファイルが7月31日に生成されたもので、8月以降およそ2週間まったくバックアップファイルが生成されていなかった。

これは何か怪しいと思い、バックアップ処理を細かく調べたところ、バックアップファイル名の日付が何日前かをチェックする処理において、なぜか本日生成されたばかりのバックアップファイルが200日以上前のものだと判断され、即座に削除の対象にされていた。
ScrapBook では Firefox 起動時に本日付のバックアップファイルが存在するかを確認し、もしなければ本日付のバックアップファイルを生成し、なおかつ古いバックアップファイルの削除処理を行うという仕様になっている。したがって、 Firefox を起動する度に本日付のバックアップファイル生成→本日付のバックアップファイル削除、という処理が8月以降ずっと繰り返されていたというわけだ。

ではなぜ8月以降、バックアップファイルが何日前かを算出する処理がうまくいかなかったのだろうか。OSの設定が狂った、夏時間特有の問題、2つのDateオブジェクトの減算で型変換がうまくいっていない等の原因を考えたが、色々追求した結果、parseInt(“08”) が 0 になることが原因だと判明した。

alert( parseInt("08") );    // 「0」が表示される

今まで parseInt というJavaScript の組み込みグローバル関数は、string 型で表現された整数を、number 型に変換するだけの単純なものだと思っていたが、実は string 型で表現された小数や8進数や16進数や文字列も変換可能である。その際、引数として渡した string 型の値をどのような形式で変換するのかは自動で識別されるため、先頭に0をつけている場合は8進数とみなされ、”08″ が 0 に変換されるのである。また、第二引数に基数 10 を指定することで思い通りに10進数として変換させることが可能である。

alert( parseInt("08", 10) );    // 「8」が表示される

この parseInt(“08”) が原因となるバックアップ処理に関するバグは、8月になると突如発生する時限式バグといえよう。運良く2週間ほどでバグに気づけたことが不幸中の幸いである。このバグを修正したバージョン (1.1.0.2) はすでにリリース済みである。

ところで、ScrapBook を世に公開して間もない、非常に初期のバージョンにて、2004年10月になると突如取り込んだデータが上書きされ続けるという大変恐ろしい時限式バグが発生したが、このときの原因も今回のバグの原因と通ずるものがあり、年・月・日を加算するときに型の自動変換に頼っていたためにバグが引き起こされていた。

var y = 2004;    // number
var m = "09";    // string
var d = 30;    // number
var ymd = y + m + d;
alert(ymd);    // 「20040930」が表示される

9月30日までは、数値 9 を 文字列 “09” にして加算をしていたため、 2004 + “09” は “200409” になってうまくいっていた。

var y = 2004;    // number
var m = 10;    // number
var d = "01";    // string
var ymd = y + m + d;
alert(ymd);    // 「201401」が表示される

しかし、10月1日になると、数値 10 は文字列に変換せずに加算していたため、 2004 + 10 は 2014 になってしまった。

var y = 2004;    // number
var m = 10;    // number
var d = "01";    // string
var ymd = y.toString() + m.toString() + d.toString();
alert(ymd);    // 「20041001」が表示される

数値か文字列かわからないような値同士を加算するときには、必ず toString や parseInt を使って正しい型に変換してから加算をしなければならない。

parseInt のリファレンス:
Core JavaScript 1.5 Reference:Global Functions:parseInt – MDC

TOP

TOP