「月末取得」は“その月の最後の日を安全につかまえる”こと
業務システムでは、「今月の月末」「請求月の月末」「締め月の月末」が頻出します。
ここでやってはいけないのは、
「31 日で固定」「30 日で固定」「2 月だけ特別扱い」みたいな“手作りカレンダー”です。
正解は、LocalDate と TemporalAdjusters を使って、
「その月の最後の日」を 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() に置き換えられないか眺めてみてください。
それだけで、
カレンダー計算のバグがぐっと減り、
“時間と日付に強いエンジニア”にまた一歩近づけます。
