月の日数(月の末日)を求めるには
VBAを使って特定の月の日数を取得したい場面は多くあります。カレンダーアプリケーションの開発や、月次レポートの自動化など、月末日を正確に把握することは重要です。
VBAのDateSerial関数かDateAdd関数のどちらかを使えば月の末日を取得できます。
月の日数(月の末日、最終日)は以下の通り、年に関わらず決まっています。
1月:31日
2月:28日(うるう年の場合は29日)
3月:31日
4月:30日
5月:31日
6月:30日
7月:31日
8月:31日
9月:30日
10月:31日
11月:30日
12月:31日
問題なのは2月で、うるう年かどうかによって28日か29日に分かれることになります。なお、うるう年の判定方法については「VBAでうるう年の判定を行う」をご参照ください。
ただ、そういうことを考えなくても、VBAの機能を使えば簡単に求める方法が2つあります。
1つ目の方法は、DateSerial関数を使って翌月1日の前日を求める方法です。こちらは単純ですが関数の挙動を理解していないと何をやっているコードなのか分かりにくい点があります。
2つ目の方法は、DateAdd関数を使って翌月1日から1日引いた日付を求める方法です。
どちらを使っても構いません。結果は同じです。
方法1:DateSerial関数を使って月の日数を求める
DateSerial関数の特性を活用し、「翌月1日の前日」を求めることで月末日を取得します。
DateSerial関数の詳細については「年月日の数値をDate型に変換する(DateSerial)」をご参照ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Sub GetLastDay() Dim sDate '// 調べたい日付 Dim sLast '// 末日 Dim sLastDay '// 末日の日のみ '// 調べたい日付を設定 sDate = "2018/12/11" '// 翌月1日の前日を取得 sLast = DateSerial(Year(sDate), Month(sDate) + 1, 0) '// 末日の日のみを取得 sLastDay = Format(sLast, "d") '// 取得結果 Debug.Print "対象月: " & Format(sDate, "yyyy年mm月") Debug.Print "月末日: " & Format(sLast, "yyyy/mm/dd") Debug.Print "月の日数: " & sLastDay & "日" End Sub |
実行すると以下のように出力され、sLastDayに2018/12/31の”31″が設定されます。
対象月: 2018年12月
月末日: 2018/12/31
月の日数: 31日
コード9行目でのDateSerial関数の2番目の引数に+1しているのは翌月として扱うためです。
3番目の引数は-32,768から32,767の範囲を指定することが出来ます。
その際に日付の1日から31日の範囲を超えた分は年や月の増減に振り替えられます。
この性質を利用して、0を設定すると1日の前日となります。
方法2:DateAdd関数を使って月の日数を求める
DateAdd関数については「VBAで日付や時刻の加減算を行う(DateAdd)」をご参照ください。
DateAdd関数を使う方法はコードが少し長くなります。
調べたい日付(yyyy/mm/dd)の末日を調べたい場合に分かりやすく処理を分けて書くと以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Sub GetLastDay() Dim sDate '// 調べたい日付 Dim sLast '// 末日 Dim sFirst '// 初日 Dim sNext '// 翌月 Dim sLastDay '// 末日の日のみ sDate = "2018/12/11" '// 対象年月の1日を取得 sFirst = Format(sDate, "yyyy/mm/01") '// 対象年月1日の翌月を取得 sNext = DateAdd("m", 1, sFirst) '// 末日を取得 sLast = DateAdd("d", -1, sNext) '// 末日の日のみを取得 sLastDay = Format(sLast, "d") End Sub |
1行で書くことももちろん出来ます。
1 2 3 4 5 6 7 8 |
Sub GetLastDay2() Dim sDate '// 調べたい日付 Dim sLastDay '// 末日の日のみ sDate = "2018/12/11" sLastDay = Format(DateAdd("d", -1, DateAdd("m", 1, Format(sDate, "yyyy/mm/01"))), "d") End Sub |
2つの関数はいずれもsLastDayには2018/12/31の”31″が設定されます。
実用的な関数として作成
DateSerial関数とDateAdd関数の両方の方法を関数として実装し、再利用しやすい形にすると以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
'// DateSerial版 Function GetDaysInMonth_DateSerial(inputDate As Date) As Integer GetDaysInMonth_DateSerial = Day(DateSerial(Year(inputDate), Month(inputDate) + 1, 0)) End Function '// DateAdd版 Function GetDaysInMonth_DateAdd(inputDate As Date) As Integer Dim firstDay As Date firstDay = DateSerial(Year(inputDate), Month(inputDate), 1) GetDaysInMonth_DateAdd = Day(DateAdd("d", -1, DateAdd("m", 1, firstDay))) End Function '// 使用例 Sub TestFunctions() Dim testDate As Date '// 様々な月でテスト testDate = "2024/2/1" '// うるう年の2月 Debug.Print Format(testDate, "yyyy/mm") & ": " & GetDaysInMonth_DateSerial(testDate) & "日" testDate = "2023/2/1" '// 通常年の2月 Debug.Print Format(testDate, "yyyy/mm") & ": " & GetDaysInMonth_DateAdd(testDate) & "日" testDate = "2024/4/15" '// 4月 Debug.Print Format(testDate, "yyyy/mm") & ": " & GetDaysInMonth_DateSerial(testDate) & "日" End Sub |
どちらの方法を選ぶべきか
DateSerial関数の特徴
メリット:
- コードが簡潔
- 実行速度が若干速い
- メモリ使用量が少ない
デメリット:
- 関数の挙動を理解していないと分かりにくい
- 第3引数に0を指定する理由が直感的でない
DateAdd関数の特徴
メリット:
- 処理の流れが明確で理解しやすい
- デバッグしやすい
- コードの可読性が高い
デメリット:
- コードが長くなる
- 中間変数が必要
まとめ
DateSerial関数とDateAdd関数のどちらの方法も正確に月の日数を取得できます。
コードの簡潔性を重視するならDateSerial関数、可読性を重視するならDateAdd関数を選択することをお勧めします。