雑記 Excelの1900/02/29問題 [その他]
日付にまつわるコンピュータの話はいろいろとありますが、今回の話題はExcelの悩ましい仕様についてです。
「1900/02/29」という日付は、存在しない日付です。
1900年は4で割り切れるのですが、100で割り切れて、かつ400で割り切れないので、閏年ではありません。
ExcelのVBAのIsDateを使って試してみると、以下のようになります。
VBAでは、1900/02/29は正しい日付ではないと判断されます。
次に、Excelのワークシート上で試してみます。
見た目だけだとわかりにくいのですが、初期状態でA1のセルに「1900/02/29」と入力しています。
入力後、Excelが日付だと判断して、日付形式で表示を行っているので、セルの内容が右詰で表示されいます。
また、Excelで日付は、1900/1/1を起点とした連続した数値で扱われます。
A2のセルには、「A1+1」を入力しています。A1の内容は日付として認識されているので、日付計算が正しく行われ、A2には翌日の日付が表示されています。
正しくない日付が入力された場合は、下図のような表示になります。
以上のように、Excelでは「1900/02/29」が正しい日付として扱われてしまいます。
それだけなら問題無いのですが、特定の日付範囲において、シリアル値が示す日付が、ExcelとVBAで一致しなくなっています。
具体的な日付範囲は、1900/01/01から1900/02/29の間です。(厳密にはVBAに1900/02/29は存在しないので、1900/03/01より前の日付と表現した方が適切かも)
Excelでは、シリアル値「1」が示す日付は「1900/01/01」となっていますが、
VBAのシリアル値「1」が示す日付は「1899/12/31」です。
つまりVBAでは、シリアル値「2」が「1900/01/01」になっていて、Excelのシリアル値と一致していません。
しかし、前述のとおり、Excelには1900/02/29が存在するため、ここでずれが解消され、1900/03/01以降は、両者のシリアル値が一致するようになります。
確認の為に、下記のプログラムで試してみました。
Public Sub test()
Dim strXls As String
Dim strVBA As String
Dim lngI As Long
For lngI = 1 To 70
strXls = Application.WorksheetFunction.Text(lngI, "yyyy/mm/dd")
strVBA = Format(lngI, "yyyy/mm/dd")
Debug.Print lngI & "・" & strXls & "・" & strVBA
Next
End Sub
ExcelのVBAで作ったプログラムです。
WorksheetFunctionでExcelの処理を呼び出して、処理結果を得ています。
1から順に、値を日付に変換した場合、どのような日付になるか表示させています。
処理結果は下図のとおり。
途中省略しようかと思ったけど、そのまま貼ってみました。
左から順に、シリアル値、Excelの日付、VBAの日付、です。
ご覧の通り、シリアル値60までは、双方の日付が一致していません。
シリアル値61の1900/03/01以降一致しているのが確認できると思います。
VBAのシリアル値の起点が1899/12/31と中途半端なのは、この現象を加味したようにも思えます。
実際に問題になるケースですが、例えば、以下のような処理では、Excelに異なった日付が表示されてしまいます。
Public Sub test2()
Dim datA As Date
datA = DateSerial(1900, 2, 1)
ActiveSheet.Cells(1, 1).Value = datA
End Sub
結果は下図のとおり。
VBAでは1900/02/01の日付をセルにセットしているつもりなんですが、Excel上では1900/02/02と違った日付が表示されてしまいます。
単純にこの現象を回避したいだけなら、文字列で日付を代入することで回避できます。
Public Sub test3()
ActiveSheet.Cells(1, 1).Value = "1900/02/01"
End Sub
日付形式になっている文字列をExcelに渡した場合、キーボード操作で入力したときと同じように、入力内容が日付かどうか判定され、日付として認識されるようです。
売上システムなどの、今後発生するデータを処理するシステムでは、1900年と言った100年以上も前の日付に遭遇することはまず無いかと思います。
しかし、人の生年月日を管理することを主な目的としたシステムの場合、1900年以前の日付が出てくる可能性は十分にあると思われます。
シリアル値がずれる現象は、test3の方法で回避できます。
しかし、それ以外の問題として、VBAでは1900年以前の日付も扱えるのですか、Excelでは日付として扱うことができません。
ちょっと話が脱線しますが、この場合は、Excelでは文字列として扱うしか方法がありません。
表示だけならわりと簡単にできると思いますが、入力させるとなると、チェックをVBAでさせるとか工夫が必要になってくるかと思います。
1900/02/29問題に関係なくめんどくさいです。
Excelがなぜこんな風になってしまったのか、ネットでいろいろ調べてみたら、Louts1-2-3との互換性を保つためこうなったそうです。
つまり、もともとはLouts1-2-3バグだったと言うことになります。
こういう仕様になったことで、助かった人の数と、困っている人の数では、どちらが多いのか気になるところです。
(今回の検証は、Excel2010で行いました。)
コメント 0