Java Tips | 日付・時間:月末取得

Java Java
スポンサーリンク

「月末取得」は“その月の最後の日を安全につかまえる”こと

業務システムでは、「今月の月末」「請求月の月末」「締め月の月末」が頻出します。
ここでやってはいけないのは、
「31 日で固定」「30 日で固定」「2 月だけ特別扱い」みたいな“手作りカレンダー”です。

正解は、LocalDateTemporalAdjusters を使って、
「その月の最後の日」を API に任せること。

これを覚えると、月次処理・締め処理・集計のコードが一気に安全で読みやすくなります。


LocalDate から月末を取得する基本パターン

TemporalAdjusters.lastDayOfMonth() を使う

一番素直で“意図が伝わる”書き方です。

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class MonthEndBasic {

    public static void main(String[] args) {
        LocalDate anyDay = LocalDate.of(2025, 3, 26);

        LocalDate monthEnd =
                anyDay.with(TemporalAdjusters.lastDayOfMonth());

        System.out.println("任意の日付 : " + anyDay);      // 2025-03-26
        System.out.println("その月の月末: " + monthEnd);    // 2025-03-31
    }
}
Java

ここでの重要ポイントは二つです。

一つ目は、「lastDayOfMonth という名前で“月末を取っている”ことが一目で分かる」こと。
with(TemporalAdjusters.lastDayOfMonth()) というだけで、
「このコードは月末を求めている」とすぐ理解できます。

二つ目は、「月の長さを自分で意識しなくていい」こと。
31 日の月、30 日の月、28/29 日の 2 月——全部 Java が面倒を見てくれます。


「今月の月末」を取得する

LocalDate.now() と組み合わせる

「今日が属する月の月末」を取りたい場合です。

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class ThisMonthEnd {

    public static void main(String[] args) {
        LocalDate today = LocalDate.now();

        LocalDate monthEnd =
                today.with(TemporalAdjusters.lastDayOfMonth());

        System.out.println("今日   : " + today);
        System.out.println("今月末: " + monthEnd);
    }
}
Java

業務的には、
「今月の売上締め日」「今月の請求対象期間の終わり」「今月のバッチ処理の最終日」
などで、この「今月末」が起点・終点としてよく使われます。


「任意の年月の月末」を求める

いったん月初を作ってから lastDayOfMonth

「2025 年 3 月の月末」のように、年月が決まっている場合です。

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class SpecificMonthEnd {

    public static void main(String[] args) {
        LocalDate monthStart = LocalDate.of(2025, 3, 1);

        LocalDate monthEnd =
                monthStart.with(TemporalAdjusters.lastDayOfMonth());

        System.out.println("2025年3月の月初: " + monthStart); // 2025-03-01
        System.out.println("2025年3月の月末: " + monthEnd);   // 2025-03-31
    }
}
Java

ポイントは、
「月初は LocalDate.of(年, 月, 1) で素直に作り、月末は lastDayOfMonth で求める」
というパターンを覚えておくことです。

「月初+月末」のセットで考えると、月次処理の期間計算がとても書きやすくなります。


LocalDateTime / ZonedDateTime での月末取得

LocalDateTime の月末(時刻をどうするかが大事)

LocalDateTime でも lastDayOfMonth が使えます。

import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;

public class LocalDateTimeMonthEnd {

    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.of(2025, 3, 26, 15, 30);

        LocalDateTime monthEndSameTime =
                dt.with(TemporalAdjusters.lastDayOfMonth());

        LocalDateTime monthEndAt235959 =
                dt.with(TemporalAdjusters.lastDayOfMonth())
                  .withHour(23).withMinute(59).withSecond(59).withNano(0);

        System.out.println("元日時            : " + dt);                 // 2025-03-26T15:30
        System.out.println("月末 同じ時刻     : " + monthEndSameTime);   // 2025-03-31T15:30
        System.out.println("月末 23:59:59まで : " + monthEndAt235959);   // 2025-03-31T23:59:59
    }
}
Java

業務では、
「今月の 1 日 0:00 から、今月末 23:59:59 までを対象にする」
といった要件が多いので、
「日付だけでなく、時刻をどこに合わせるか」をきちんと決めることが重要です。

ZonedDateTime の月末(タイムゾーン付き)

タイムゾーン付きの ZonedDateTime でも同様です。

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAdjusters;

public class ZonedDateTimeMonthEnd {

    public static void main(String[] args) {
        ZonedDateTime zdt = ZonedDateTime.of(
                2025, 3, 26,
                15, 30, 0, 0,
                ZoneId.of("Asia/Tokyo")
        );

        ZonedDateTime monthEnd =
                zdt.with(TemporalAdjusters.lastDayOfMonth())
                   .withHour(23).withMinute(59).withSecond(59).withNano(0);

        System.out.println("元日時        : " + zdt);
        System.out.println("月末 23:59:59: " + monthEnd);
    }
}
Java

海外システムと連携する場合や、
「日本時間の月末」「UTC の月末」を区別したい場合は、
ZonedDateTime で月末を扱う方が安全です。


実務での「月末取得」あるあるパターン

月次バッチの対象期間を求める(今月の 1 日〜月末)

「今月のデータだけを集計したい」という典型パターンです。

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;

public class MonthlyRangeExample {

    public static void main(String[] args) {
        LocalDate today = LocalDate.now();

        LocalDateTime from = today.with(TemporalAdjusters.firstDayOfMonth())
                                  .atStartOfDay();

        LocalDateTime to = today.with(TemporalAdjusters.lastDayOfMonth())
                                .atTime(23, 59, 59);

        System.out.println("対象開始: " + from);
        System.out.println("対象終了: " + to);
    }
}
Java

ここでの重要ポイントは、
「月初取得」と「月末取得」をセットで使うと、
“今月の範囲”をきれいに表現できる
ことです。


まとめ:月末取得で絶対に覚えてほしいこと

月末取得は、「その月の最後の日」を安全かつ意図が伝わる形で取り出すことです。

LocalDate なら with(TemporalAdjusters.lastDayOfMonth()) を使う。
「今月末」なら LocalDate.now().with(lastDayOfMonth())
LocalDateTime / ZonedDateTime では、日付に加えて「時刻をどこに合わせるか」を決める。
年月が分かっているなら、LocalDate.of(年, 月, 1) から lastDayOfMonth で求める。

あなたのコードのどこかに、
「if 文で 31, 30, 28 を分岐して月末を計算している」ような箇所があれば、
そこを一度 lastDayOfMonth() に置き換えられないか眺めてみてください。

それだけで、
カレンダー計算のバグがぐっと減り、
“時間と日付に強いエンジニア”にまた一歩近づけます。

タイトルとURLをコピーしました