こんにちは!マクロの記録から一歩抜け出して、本格的な業務自動化に挑むあなたの姿、とても素敵ですね。
Excel VBAを書いていると、避けて通れないのが「日付と時刻」の扱いです。
「昨日の売上を集計したい」「納期まであと何日か計算したい」――そんなごく自然な願いをコードに落とし込むとき、多くの人がシリアル値という見えない罠に足を取られてしまいます。
今回は、VBAにおける日付データの正体と、現場で絶対にやってはいけない「日付比較・計算のミス」を回避するためのベストプラクティスを、優しく、そして本質的なところまで徹底的に解説していきますね。
ここをクリアすれば、あなたのVBAスキルは一段と洗練されますよ!一緒にマスターしていきましょう。
—
1. そもそも「日付」の正体とは?(シリアル値の正体)
私たちがExcelのセルに `2023/10/01` と入力すると、画面には綺麗に日付が表示されますよね。しかし、VBAの裏側やExcelの心臓部では、これは日付文字としてではなく、ただの「数値」として扱われています。
この数値のことを「シリアル値」と呼びます。
- 基準日(1): `1900年1月1日` を 「1」 とします。
- 経過日数: そこから何日経ったかという「整数」が日付の正体です。
- `1900年1月2日` = `2`
- `2023年10月1日` = `45199` (1900年1月1日から45,199日後)
- 時刻の表現: 小数点以下で時間を表します。
- 1日は「24時間 = 1.0」なので、正午(12:00)は `0.5` となります。
つまり、VBAで日付を扱う `Date` 型の本質は、「整数部に日付、小数部に時間を保持する倍精度浮動小数点数(Double)」なのです。
—
2. 【実例】みんながハマる「日付・時刻の罠」
シリアル値の性質を理解していないと、現場で頭を抱えるような不可解なバグに遭遇します。よくある「3つの罠」を見てみましょう。
罠その1:「文字列」と「日付」をそのまま比較してしまう
初心者の方が最もやりがちなのが、フォームから取得した文字列や、セルからそのまま読み込んだ値と、日付を直接比較してしまうミスです。
‘ 【やってはいけない例】
Dim targetDate As Date
targetDate = “2023/10/01”
‘ セルの値(文字列の “2023/10/01″)と Date型をそのまま比較
If Range(“A1”).Value = targetDate Then
‘ 一致するはずが、セルの書式や型によっては一致判定にならないことがある!
End If
Excelのセルに入っている値は、見た目が日付であっても、セルの設定やデータのインポート方法によっては「文字列型(String)」として保持されていることがあります。VBAは型に厳密なため、型が違うと「同じ日付(見た目)」でも「別物」とみなされてしまうのです。
罠その2:時刻を含む日付の「~以上・~以下」比較
「10月1日のデータをすべて抽出したい」と思って、以下のようなコードを書いたとします。
‘ 【危険な例】
If Range(“A1”).Value >= #10/1/2023# Then
もしセル `A1` の値が `2023/10/01 15:30:00` だった場合、これはシリアル値で言うと `45199.646…` です。
純粋な `#10/1/2023#` は `45199.0` なので「以上」の条件に引っかかりますが、もし「10月1日 ちょうど」を判定したい場合に、時刻の小数部分がついていると、意図した一致判定になりません。
—
3. バグを回避するベストプラクティス
では、こうした罠を華麗に回避し、実務で安心して使えるコードを書くにはどうすればよいでしょうか?
プロの現場で使われている3つの鉄則を伝授します。
鉄則1:変数は必ず `Date` 型で明示的に宣言する
Variant型やString型で日付を曖昧に保持せず、必ず `Dim 〇〇 As Date` と宣言しましょう。VBAが自動的にシリアル値への変換・管理を行ってくれます。
鉄則2:日付の定数は `#`(シャープ)で囲む
VBAで日付リテラル(直接値を入れること)を記述するときは、ダブルクォーテーション(`”`)ではなく、シャープ(`#`)で囲むのが作法です。これにより、VBAはそれが「日付データである」と明確に認識します。
Dim dueMth As Date
dueMth = #10/31/2023# ※米国式(月/日/年)の順序に注意!
鉄則3:日付の比較は「整数部(日付)」と「時間」を意識する
日付だけで比較したい(時刻を無視したい)場合は、VBAの `Int` 関数(または `Fix` 関数)を使って時刻の小数部分を切り捨てるのが、極めて堅牢なテクニックです。
—
4. 実践!安全な期間計算・比較のサンプルコード
それでは、ここまでの知見を詰め込んだ「実務でそのまま使える安全な日付処理マクロ」を見てみましょう。
指定した日付範囲にセル内のデータが含まれているかを正確に判定し、集計するサンプルです。
Sub SafeDateCalculationSample()
Dim ws As Worksheet
Set ws = ActiveSheet
‘ 検索条件の定義(Date型として明確に宣言し、#で囲む)
Dim startDate As Date
Dim endDate As Date
startDate = #10/1/2023#
endDate = #10/31/2023#
Dim targetCellVal As Variant
Dim targetDate As Date
Dim matchCount As Long
matchCount = 0
Dim i As Long
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, “A”.End(xlUp).Row
For i = 2 To lastRow
targetCellVal = ws.Cells(i, 1).Value
‘ 1. セルが空でないか、かつ日付として解釈できるかをチェック
If IsDate(targetCellVal) Then
‘ 2. Date型に安全に変換
targetDate = CDate(targetCellVal)
‘ 3. 時刻の要素を排除し、純粋な「日付(整数部)」だけで比較する
‘ Int関数を使うことで、何時何分であってもその日の「0時0分」に正規化されます
If Int(targetDate) >= Int(startDate) And Int(targetDate) <= Int(endDate) then
matchCount = matchCount + 1
' ここに処理を記述(例:背景色を変えるなど)
ws.Cells(i, 1.Interior.Color = RGB(200, 255, 200)
End If
End If
Next i
MsgBox "10月の対象データは " & matchCount & " 件です。", vbInformation, "集計完了"
End Sub
このコードの優れたポイント
1. `IsDate` 関数による事前チェック:セルにゴミデータ(文字など)が入っていても、エラーでマクロが停止するのを防ぎます。
2. `CDate` による型強制:文字列として入力されていたセル値も、確実に `Date` 型へと昇華させます。
3. `Int` 関数による時刻のマスク:時間のズレによる判定漏れを完全にシャットアウトします。
—
まとめ
いかがでしたでしょうか?
日付・時刻データをただの「文字」や「数字」として扱うのではなく、「シリアル値という基盤の上に成り立つ、整数と小数の組み合わせ」として捉えることで、VBAのエラーやバグは劇的に減らすことができます。
ここをクリアしたあなたなら、もう「マクロが勝手に日付を勘違いして動かなくなる」という恐怖とはお別れです。ぜひ次の自動化開発でこのテクニックを取り入れてみてくださいね。
あなたのVBAライフが、より快適で知的で楽しいものになりますように。
それでは、また次の極限の知見でお会いしましょう!
