SSブログ

雑記 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で行いました。)


nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

※ブログオーナーが承認したコメントのみ表示されます。
※URL(リンク)は記述できません。

トラックバック 0

トラックバックの受付は締め切りました

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。