Microsoft Excel の 1900 年 2 月 29 日問題 まとめ

Microsoft Excel を使ってブック間で日付形式のデータを含むデータをやりとりする場合、表示される日付がおかしくなる事があります。具体的には日付が 4 年と 1 日 (1,462 日) 分ずれて表示される、この現象自体は割と知られているようですが、発生する理由やその歴史的経緯、参考情報などについてちょっとまとめてみました。
そもそもこの現象が起きるのは、Excel に二つの日付基準が存在しているからです。Excel ではすべての日付データはある基準日からの日数として記憶されており、表示形式に応じてその日数から年月日を計算して表示しています。こうする事で暦法 (例えば西暦と和暦、イスラム暦など) に依存しない情報として日付データを扱う事ができ、また多様な表示形式に対応させる事も可能になっています。この基準日からの日数のことを「シリアル値」と呼びますが、シリアル値の基準日が二つ存在しているのです。
一つ目の日付基準は 1904 年 1 月 1 日をシリアル値 0 として計算するもので、もう一つの日付基準は 1900 年 1 月 1 日をシリアル値 1 として計算するものです。単に基準となる年が違うだけでなく、シリアル値の開始が 1 と 0 で異なっているのです。
これで理解できるように、日付が意図に反してずれてしまう現象は 1900 年 を基準としていたデータが 1904 年を基準として表示されてしまう、あるいはその逆によって発生するのですが、ではどうして Excel はこのような日付基準が二つあるという紛らわしい仕様になっているのでしょうか。
この事情については元 Microsoftプログラマー Joel Spolsky 氏の Blog に詳しいので概略を記してみましょう。MicrosoftWindows 版の Excel を開発する際、当時シェアが大きかった Lotus 1-2-3 からの移行を容易にするためこれとの互換性を持たせるようにしたいと考えました。ところが Lotus 1-2-3 の日付データの仕様を調べたところ、閏年の扱いについておかしな仕様になっている事がわかったのです。閏年は一般的に西暦年数が 4 で割り切れる 4 年に一度ですが、100 で割り切れて 400 で割り切れない年は閏年にはなりません。ですから 1900 年は閏年では無いのですが、Lotus 1-2-3 は 1900 年を閏年として扱っていたのです。そして Lotus 1-2-3 の日付のシリアル値は 1900 年 1 月 1 日を 1 としていました。
これは MS-DOS 版の 1-2-3 からの仕様で、西暦年数を二進数で表現した際のビット列の下 2 ビットが共に 0 かどうかという単純なロジックで閏年を判定するために、100 で割り切れて 400 で割り切れない年は閏年にならないという条件は無視されているのです。確かに次にこの条件で閏年にならないのは (2000 年は 400 で割り切れるから閏年だったので) 2100 年ですから、CPU 能力にもメモリにも制約の多い当時 (1980 年代) の状況を考えれば無理のない判断だったとも言えましょう。
Microsoft はこうした 1-2-3 の (悪) 仕様を Windows 版の Excel にも引き継ぐ事にしたのですが、ここでもう一つの問題が生じます。Excel は元々 Macintosh 用のアプリケーションとして開発され、Windows 版開発の時点で MacExcel は好評を博して広く使われていました。ところが初期の Mac OS ではこれと同じ閏年のトラブルを未然に防ぐため (というより面倒な処理を減らすため、だったのでしょうが) に 1904 年 1 月 1 日より前の日付はサポートされていませんでした。そのため MacExcel は 1904 年 1 月 1 日をシリアル値 0 として計算していました。つまり WindowsExcelLotus 1-2-3 との互換性を取ったために MacExcel との互換性に問題が生じてしまったのです。
そこで [1904 年から計算する] というオプションが作られました。このオプションを有効にすると、WindowsExcel でも MacExcel と同じ日付基準が使われるようになります。このオプションの設定はブック (ファイル) 単位で保持されるので、オプションが有効 (1904 年基準) のブックと無効 (1900 年基準) のブックを同時に開いて、その間でデータをコピー&ペーストする事もできます。また日付データが入力済みのブックでオプションを変更する事もできます。しかしコピー&ペーストや設定の変更で基準日付が変更されても、シリアル値を自動的に調整されるような機能は無くそのまま保持されるので、そのタイミングで日付が 1,462 日分変わってしまいます。
さて、この仕様にはもう一つの副作用があります。Excel では WEEKDAY 関数で日付に対する曜日を計算できますが、1900 年 1 月 1 日を基準日付にしていると 1900 年 3 月 1 日より以前の (1900 年 1 月と 2 月の) 日付については曜日が一つずれてしまいます。例えば WEEKDAY 関数は 1900 年 2 月 16 日 に対して木曜日に相当する 5 を返しますが、実際にはこの日は金曜日で、正しい結果は 6 であるはずです。これは前述の理由で Excel が 1900 年を閏年として扱うため、実際には存在しない 1900 年 2 月 29 日を含めて曜日を計算してしまうためです。
ソフトウェアはその時点でのベストな仕様が採用されるべきですが、「ベスト」とは必ずしも技術的に最も適切であるとは限らず、マーケティング的な要素や後方互換性なども考慮して決められる事になります。しかしそうして決められた仕様が将来的にも適切とは限らず、思わぬトラブルや意味不明な動作の元になる事もあります。この Excel の動作はそうした現象の良い事例でしょう。
なお現行の Excel では閏年の判定は適切なロジックが使われており、2100 年はちゃんと閏年では無いと判断されています。

参考情報