VBAで自動化 VBA一覧 日付・時刻 配列・データ操作

【VBA】月の最終日を簡単に求める方法|DateSerialで月末処理を安全に自動化

月末締め、請求書の発行日、勤怠の締日、売上の集計期間など、実務では「その月の最終日」を正しく扱う場面が頻繁にあります。
ところがVBAで日付を作るとき、月末だけは意外と落とし穴が多いです。たとえば「2月は28日(うるう年は29日)」「4月は30日」など、月ごとに日数が違うため、固定で「31日」を入れると簡単に壊れます。

この記事では、Excel VBAで月の最終日を“安全に・簡単に”求める方法を、実務での使い方まで含めて整理します。単にコードを出すだけではなく、なぜその書き方が堅牢なのか、運用でどこに注意すべきかも一緒に解説します。

✅ VBAで月の最終日を求める前に知っておくべき落とし穴

月末は「分かっているつもり」で実装すると、後から静かに事故ります。特に怖いのは、テストが通っても“特定の月だけ”壊れることです。2月やうるう年、30日までの月が混ざった瞬間に処理が止まると、締め処理や請求処理のような重要業務ほど影響が大きくなります。さらに、日付が文字列として扱われていると、見た目は正しくても比較や加算でズレが出ることがあります。ここを理解せずに「とりあえず動いた」実装をすると、翌月・来年・別PCで再現するタイプの不具合になりやすいです。月末を扱うコードは、最初から“壊れにくい形”にしておくのが結局いちばん安いです。

・「31日固定」「月ごとの日数テーブル」は壊れやすい

よくあるNG例は、次のようなものです。

  • 「月末=31日」と決め打ちして DateSerial(2026, 4, 31) のように作る
  • 月ごとの日数を配列やSelect Caseで管理する
  • うるう年だけ別分岐を足す

これらは、追加条件が増えるほど保守がつらくなり、最終的に「どこが正なのか」分からなくなります。
月末はVBAの仕様をうまく使う方が、短く・安全で・読みやすくなります。


✅ DateSerialで月末を求める基本|「翌月の0日」が最終日

月末取得で最もおすすめなのが DateSerial を使う方法です。理由は明確で、月ごとの日数差・うるう年を全部VBA側に任せられるからです。自前で分岐を持たないので壊れにくく、読み手にも意図が伝わりやすいです。ここを押さえずに他の方法に進むと、月末処理が“テクニック”の寄せ集めになりやすいので、まずは王道から固めるのが安全です。月末が必要な処理ほど、後で仕様変更(締日変更、期間変更、複数拠点対応)が入りやすいので、最初に堅牢な型を作っておく価値が大きいです。

・月の最終日を返す関数の作り方(おすすめ)

以下は、指定した日付が属する「月の最終日」を返す関数です。実務ではこの形にしておくと再利用しやすく、別マクロでもすぐ使えます。

Option Explicit

'指定した日付が属する月の最終日を返す
Public Function GetEndOfMonth(ByVal targetDate As Date) As Date
    Dim nextMonthFirstDay As Date
    
    '翌月の1日を作ってから「0日」を指定すると当月末になる(DateSerialの仕様)
    nextMonthFirstDay = DateSerial(Year(targetDate), Month(targetDate) + 1, 1)
    GetEndOfMonth = DateSerial(Year(nextMonthFirstDay), Month(nextMonthFirstDay), 0)
End Function

なぜこの書き方が良いのか(設計意図)

  • 日数計算やうるう年判定を自前で持たないため、例外が増えても壊れにくい
  • DateSerial(年, 月+1, 0) のパターンは「月末を返す」意図が読み手に伝わりやすい
  • 関数化しておけば、締め処理・請求処理・勤怠処理などに横展開しやすい

別案との違い(メリット差)

  • Select Caseで月ごとの日数を管理する案は、仕様追加や修正に弱い
  • DateAdd("m", 1, ...) で月を進める案も良いが、起点が月末だとズレることがある(後述)
  • 「最後の日を求める」用途では、DateSerialが最も直球で事故が少ない

実務で気をつけるポイント

  • targetDateDate型で受け取る(文字列のまま渡すと環境依存で解釈がズレることがある)
  • シート上の値が文字列になっている場合は、先にDateへ変換してから渡す

✅ 指定した「年・月」から月末を求める|締め処理で使いやすい形

日付ではなく、年と月(例:2026年2月)だけが入力として与えられるケースも多いです。たとえば「対象年月」をセルに持っておき、月末を基準に集計するような締め処理です。この場合、日付からYear/Monthを抜くより、最初から「年・月」を引数にしておく方が設計が素直になります。後から「対象年月が複数」「締日が月末固定ではない」などの仕様変更が入っても、変更点を局所化できます。月末処理は“業務ルールの中心”になりやすいので、入力の形に合わせた関数を用意しておくのが実務的です。

・年・月を指定して月末を返す関数

Option Explicit

'年・月を指定してその月の最終日を返す
Public Function GetEndOfMonthByYearMonth(ByVal targetYear As Long, ByVal targetMonth As Long) As Date
    '翌月の0日 = 当月末
    GetEndOfMonthByYearMonth = DateSerial(targetYear, targetMonth + 1, 0)
End Function

なぜこの書き方が良いのか(設計意図)

  • 年月指定のため、入力ルールが明確(締め処理で扱いやすい)
  • シートの「対象年月」セルと相性が良い(Year/Monthで分解しやすい)

実務で気をつけるポイント

  • targetMonth に 1〜12 以外が入る可能性があるなら、入力チェックを入れる(後述)

✅ 月末を基準に「当月の期間」を作る|集計・抽出の実務パターン

月末が取れると、次に必要になるのは「当月の開始日〜終了日」を作ることです。ここでよくある失敗が、開始日と終了日のどちらかだけを月末基準で作って、期間がズレることです。期間がズレると、集計の数字が合わないのにエラーにならないという最悪の状態になります。月末は“目に見える境界”なので安心しがちですが、開始日(1日)もセットで設計しておくことが重要です。特に、売上や勤怠のように「1件でも漏れたらNG」なデータは、期間設計を関数として固定化するのが安全です。

・当月の開始日と終了日を返す(例:配列で返す)

Option Explicit

'指定日が属する月の開始日と終了日を返す
Public Function GetMonthRange(ByVal targetDate As Date) As Variant
    Dim startOfMonth As Date
    Dim endOfMonth As Date
    
    startOfMonth = DateSerial(Year(targetDate), Month(targetDate), 1)
    endOfMonth = DateSerial(Year(targetDate), Month(targetDate) + 1, 0)
    
    GetMonthRange = Array(startOfMonth, endOfMonth)
End Function

なぜこの書き方が良いのか

  • 月の開始日と終了日を同じルールで作るため、期間ズレが起きにくい
  • 集計処理(AutoFilterやSQL、PowerQuery取り込み等)に使い回しやすい

実務で気をつけるポイント

  • 呼び出し側で Array(0)Array(1) の意味が曖昧になりがちなので、業務規模が大きいならユーザー定義型やクラス化も検討(ただし過剰設計は避ける)

✅ DateAddで月末を求める方法と注意点|ズレるケースを理解する

DateAddを使って「翌月に進めてから引く」という発想もあります。ただし、DateAddは起点の日付によって結果がズレるケースがあり、月末取得だけを目的にするならDateSerialの方が事故が少ないです。ここを知らないと、月末から1か月進めたつもりが「翌月の同日」にならず、日付が丸められる挙動に引っ張られます。もちろんDateAddが悪いわけではなく、用途に応じた使い分けが重要です。月末を確実に取るという目的では、DateSerialが“意図どおりに読める”点でも優位です。

・DateAddを使う場合の考え方(参考)

  • 当月1日を作る
  • 1か月進めて翌月1日を作る
  • 1日引いて月末にする
Option Explicit

Public Function GetEndOfMonth_DateAdd(ByVal targetDate As Date) As Date
    Dim firstDay As Date
    Dim nextMonthFirstDay As Date
    
    firstDay = DateSerial(Year(targetDate), Month(targetDate), 1)
    nextMonthFirstDay = DateAdd("m", 1, firstDay)
    
    GetEndOfMonth_DateAdd = DateAdd("d", -1, nextMonthFirstDay)
End Function

こう書く理由

  • DateAddは月末起点だとズレやすいので、必ず当月1日を起点にする
  • 月末取得の計算手順が明示され、意図が伝わる

DateAddを使った月末取得は、
考え方を理解していないと日付がズレるケースがあります。
実務で月末処理を安全に行うには、
日付計算の仕様を前提から整理できる方法を選ぶことが重要です。
DateSerial関数を軸に、
日付計算を壊れにくく設計する考え方については、
【VBA】DateSerial関数の完全ガイド|日付計算を安全に行う実務設計 で詳しく解説しています。


✅ 入力チェック込みで“壊れない”月末関数にする|実務の保守性を上げる

実務では、想定外の入力が必ず混ざります。月末関数も例外ではありません。たとえば、対象月が空白、0、13、文字列、エラー値…など、現場のExcelでは普通に起きます。ここで「とりあえずDateSerialに投げる」だと、エラーが出るか、意図しない日付が生成される可能性があります。月末が狂うと締め処理全体が狂うので、入口で弾く設計を入れておくと、後工程の安心感が段違いです。保守性を重視するなら、入力の正当性を関数側で担保し、呼び出し側をシンプルに保つのが実務的です。

・年・月入力をチェックして月末を返す(堅牢版)

Option Explicit

'年・月の妥当性をチェックして月末を返す(不正ならエラーを返す)
Public Function GetEndOfMonthSafe(ByVal targetYear As Long, ByVal targetMonth As Long) As Date
    If targetMonth < 1 Or targetMonth > 12 Then
        Err.Raise vbObjectError + 1000, "GetEndOfMonthSafe", "月は1~12で指定してください。"
    End If
    
    GetEndOfMonthSafe = DateSerial(targetYear, targetMonth + 1, 0)
End Function

なぜこの書き方が良いのか

  • 不正入力を早期に検出できる(誤った月末で処理が進む方が危険)
  • エラー原因が明確になり、運用・保守が楽になる

実務で気をつけるポイント

  • エラーを“止める”のが困る運用なら、エラーではなく「0日」や空白を返す設計にするなど、運用方針に合わせて調整する

✅ 応用:月末を使った実務シナリオ例(締め処理・フォルダ名・抽出条件)

月末が取れるようになると、単に日付を表示するだけでなく、業務の自動化に直結します。たとえば締め処理では、期間の抽出条件に使えますし、請求書や帳票のファイル名にも使えます。さらに、フォルダ名を「2026-02」のように年月単位で切る設計とも相性が良いです。こういう“周辺の設計”まで含めておくと、月末関数は単発の小技ではなく、再利用できる部品になります。逆に、月末をその場で都度計算する作り方にすると、仕様変更時に修正漏れが出やすくなります。

・ファイル名に月末日を入れる例

  1. 対象日付から月末を取得する
  2. Format"yyyymmdd" に整形する
  3. ファイル名に結合する
Option Explicit

Public Sub CreateMonthlyFileNameExample()
    Dim targetDate As Date
    Dim endOfMonth As Date
    Dim fileName As String
    
    targetDate = Date
    endOfMonth = GetEndOfMonth(targetDate)
    
    fileName = "請求書_" & Format(endOfMonth, "yyyymmdd") & ".xlsx"
    Debug.Print fileName
End Sub

 

✅ まとめ:VBAで月の最終日を安全に求めるならDateSerial

  • 月末は月ごとの日数差・うるう年が絡むため、決め打ち実装は壊れやすい
  • DateSerial(年, 月+1, 0) を使うと、当月末をシンプルに取得できる
  • 関数化しておくと、締め処理・集計・保存設計に横展開しやすい
  • 実務では不正入力が混ざるため、必要に応じて入力チェックも入れる
  • 月末は単発の小技ではなく、業務自動化の“基礎部品”として扱うと保守性が上がる

月末処理は、地味ですが実務では致命傷になりやすい部分です。
最初から壊れにくい形で関数化しておくと、締め処理や帳票作成が一段ラクになります。

今回は、
VBAで月の最終日を安全に求める方法を解説しました。

一方で、実務では
「ここまで作り込むべきか」
「もっとシンプルで十分ではないか」

と迷う場面も少なくありません。

Excel業務改善を進めるうえで、
コスパが悪くなりやすい判断ラインを整理した記事として、
Excel業務改善はどこまでやるべきか?コスパが悪くなる判断ライン
もあわせて参考にしてみてください。

    -VBAで自動化, VBA一覧, 日付・時刻, 配列・データ操作