はじめに
ExcelVBAの初心者が理解しにくいのが日付型の扱いかもしれない。ここでは少々厄介なExcelでの日付の扱いについて学んでみよう。
なぜわかりずらいのか? 理由は以下の2つの要因から来ている。
・勝手に日付型に変換されるExcelの仕様
・VBAでは日付型をシリアル値と書式設定の2つで管理している。
では1つずつ見ていこう。
勝手に日付型に変換されるExcelの仕様
まずは何も考えずに、F1セルに 12/1 と入力してみよう。

12/1 が日付に自動変換されて、ご丁寧に月と日まで追加されている。Excelに使い慣れている人にとっては当たり前ともいえる仕様だ。上の数式バーに表示されているは、2024/12/1 で入力していない西暦年まで追加されている。
今度はF1セルの値をDeleteキーで削除して、1 を入力してみよう。すると不思議な事に以下のような日付が勝手に入力されている。

慣れていない人にとっては「何だこりゃ」となるのだが、これがExcelの仕様なので受け入れるしかない。
郷に入らば郷に従え
Excelの立場から言えば多分こうだろう。
「一旦日付を入力したセルは次からもどうせ日付を入力するだろう。そうに決まってる!」
お節介な話だが、仕様としてそうなっていているのだからユーザーとしては文句を言っても始まらない。
さて、なぜ 1 を入力したら、 1900/1/1 になったかという理由は次で明らかにしよう。
日付型をシリアル値と書式設定の2つで管理
ExcelVBAで日付型を扱う場合、シリアル値と書式設定を意識する必要がある。
シリアル値とは、1900年1月1日を 1 として開始した序数(順番に並ぶ数)の事だ。これに日付表示となるように書式設定を施したものがExcelでの日付の扱いとなる。 だから上の例では、 1900/1/1 と表示されたのだ。
書式設定に関しては、Excelに慣れている人であれば、何度も目にしていると思う。日付だけでなく、数値の桁切りや小数点、貨幣など色々な設定ができるようになっている。

さてここでExcelVBAのセルの値には2種類あることに触れておこう。
1つは Valueプロパティ、もう一つはTextプロパティだ。
(もちろんセルのプロパティは他にもたくさんあるのだが、よく使う2つを取り上げている)
前の例で言えば、「1」 はValue。 「1月1日」はText という事になる。
Debug.Print Range(“F1”).Value ‘ 1
Debug.Print Range(“F1”).Text ‘ 1月1日
ちなみにプロパティを省略した場合は、Valueを指定したのと同じなる。
なので他のセルの値と同じようにValueを省略して比較したりすると日付の所で上手くいかない場合がある。
日付けを比較したい時
VBAで2つの日付値を比較したい場合、値(シリアル値)を比較すれば同じ日ならば、書式が違ってもHitする。もし、見た目で書式も含めて同じでないと困る場合は、Textを付けて表示文字列で比較すればよいだろう。

Sub testDay()
If Range("A2") = Range("B2") Then 'シリアル値で比較
Range("C2") = "〇"
Else
Range("C2") = "×"
End If
If Range("A3").Text = Range("B3").Text Then '文字列で比較
Range("C3") = "〇"
Else
Range("C3") = "×"
End If
End Sub
初心者であれば、見た目の違いで判断した方がエラーに気付きやすいかもしれない。
「日」などの要素で比較したい場合
日付型では、年、月、日を抜き出す専用の関数が用意されている。これらを使えば、日付を数値として扱えるようになるので、日だけを比較したいような場合に便利だ。
日付関数は以下のようなものが用意されている。
・Day関数(日付型から日を取り出す)
・Month関数(日付型から月を取り出す)
・Year関数(日付型から年を取り出す)
上記の例で以下を実行すると日が取り出される。
Debug.Print Day(Range("A2")) ' 1
尚、Day関数の引数に日付型以外を渡すと「型が一致しません」というエラーが出るので気を付けよう。
書式設定したい時
セルにある日付の書式を変えたい時、「セルの書式設定」の「表示形式」から「ユーザー定義」にして、日付のフォーマットを変更しますが、VBAでは、NumberFormatLocalを使って同じように変更できます。

NumberFormatLocalプロパティは、そのセルの書式設定を取得・設定する事ができます。
前回のシートで、B列の書式設定をA列と同じにしたければ以下のようにすれば良いでしょう。
Sub testNumberFormatLocal()
Range("B2").NumberFormatLocal = Range("A2").NumberFormatLocal
Range("B3").NumberFormatLocal = Range("A3").NumberFormatLocal
End Sub
ここでは、A列の書式設定をB列に代入しています。
この状態で、先程の testDay() を実行すると以下のようになります。

見た目(書式)もシリアル値も同じなので、2行目も3行目も同じものとして認識されています。
ちなみに、Range(“A2”).NumberFormatLocal の値は、m”月”d”日” となっています。
もし、これをVBAプログラムでセットするには以下のようにしなければいけません。
Range("B2").NumberFormatLocal = "m""月""d""日"""
何やらダブルクォーテーションがやたらと多い気がしますが、ダブルクォーテーションでくくる文字列内部でダブルクォーテーションを使う場合は、ダブルクォーテーションでエスケープシーケンスするという決まりがあります。
初心者にはわかりずらいと思いますが、以下のように、外側の青のダブルクォーテーションは、文字列としての括り。中にある赤いダブルクォーテーションは、次の黒いダブルクォーテーションを文字列内で有効にするためのダブルクォーテーションという事になります。
“m““月““d““日“““
かなりややこしい話ですね。
Format関数で日付の書式が変更されない罠
Format関数を使えば日付の書式を指定した文字列をセルに代入することができます。
ただ、以下のようなプログラムでは思うような結果になってくれません。
Sub tetFormat() '今日の日付が 2024.12.21 だとすると、
Range("B2") = Format(Date, "mm月dd日") '12月21日 と表示される
Range("B2") = Format(Date + 1, "yyyy/m/d") '2024/12/22 と表示させたいが、12月22日と表示される
End Sub

二行目では、B2セルに書式設定付きで日付をセットしたいのですが、前の書式のままです。これはどういう事でしょうか?
実は、Format関数は、文字列を出力するという所がミソです。一行目では文字列を日付と判断して日付型に自動変換しています。その際、シリアル値と書式設定もセットされます。
次の2行目では、同じように文字列の日付を入力しますが、日付と判断されて自動変換される際に、シリアル値しか変更しないのです。
このような時は、2回目の代入を行う際、一旦セルをクリアしてから代入すればうまく行きます。
Sub tetFormat()
Range("B2") = Format(Date, "mm月dd日")
Range("B2").Clear
Range("B2") = Format(Date + 1, "yyyy/m/d")
End Sub
もちろん、NumberFormatLocalプロパティを変更してもOKです。
Format関数は文字列を出力するものだと覚えておいてください。
まとめ
Excelでの日付の扱いには少々コツがいる。慣れてしまえば大したことはないのだが、最初のうちはつまずく事も多いだろう。大事なのは間違いに気付いたり、修正できる事だ。
日付が自動変換される事や、シリアル値と書式設定で構成されている事を頭に置いておけば間違いに気づきやすくなるだろう。
これまでいくつか日付に関するトピックを書いているので、気になる人は以下を参照していただきたい。
コメント