「月初取得」は“その月の1日を安全に取り出す”こと
業務システムでは、「今月の月初」「請求月の月初」「締め月の月初」をよく使います。
ここでやってはいけないのは、day = 1; のように自分で日付をいじることです。
正解は、LocalDate と TemporalAdjusters を使って、
「その月の1日」を“意味のある名前のメソッド”で取り出すことです。
これを覚えておくと、月次処理・集計・締め処理のコードが一気に読みやすくなります。
LocalDate から月初を取得する基本パターン
dayOfMonth(1) で「その月の1日」にする
一番シンプルな書き方はこれです。
import java.time.LocalDate;
public class MonthStartBasic {
public static void main(String[] args) {
LocalDate anyDay = LocalDate.of(2025, 3, 26);
LocalDate monthStart = anyDay.withDayOfMonth(1);
System.out.println("任意の日付 : " + anyDay); // 2025-03-26
System.out.println("その月の月初: " + monthStart); // 2025-03-01
}
}
Javaここで重要なポイントは二つあります。
一つ目は、「with〜系メソッドは“そのフィールドだけを差し替えた新しい値”を返す」ということです。withDayOfMonth(1) は「日だけ 1 に変えた LocalDate」を返します。
元の anyDay は変わりません(不変オブジェクト)。
二つ目は、「その月に 1 日が存在しない、ということはありえない」ので、withDayOfMonth(1) は常に安全に使える、ということです。
(withDayOfMonth(31) だと、2 月などで例外になりますが、1 は必ず存在します。)
TemporalAdjusters を使った「月初」の書き方
firstDayOfMonth() を使う
TemporalAdjusters には、「月初」「月末」「次の月初」などを
“名前付き”で表現するためのメソッドが用意されています。
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public class MonthStartAdjuster {
public static void main(String[] args) {
LocalDate anyDay = LocalDate.of(2025, 3, 26);
LocalDate monthStart =
anyDay.with(TemporalAdjusters.firstDayOfMonth());
System.out.println("任意の日付 : " + anyDay); // 2025-03-26
System.out.println("その月の月初: " + monthStart); // 2025-03-01
}
}
JavawithDayOfMonth(1) と結果は同じですが、
「firstDayOfMonth という名前で意図がはっきりする」のが大きなメリットです。
コードを読む人が、
「これは“月初を取っている”んだな」と一瞬で理解できます。
「今月の月初」を取得する
LocalDate.now() と組み合わせる
「今日が属する月の月初」を取りたい場合です。
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public class ThisMonthStart {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
LocalDate monthStart =
today.with(TemporalAdjusters.firstDayOfMonth());
System.out.println("今日 : " + today);
System.out.println("今月初: " + monthStart);
}
}
Java業務的には、
「今月の売上集計」「今月のログ抽出」「今月のバッチ対象期間」などで、
この「今月の月初」が起点になることが多いです。
「任意の年月の月初」を直接作る
LocalDate.of(年, 月, 1) で作る
「2025年3月の月初」のように、
年月がはっきりしている場合は、最初から 1 日を指定してしまうのもアリです。
import java.time.LocalDate;
public class SpecificMonthStart {
public static void main(String[] args) {
LocalDate monthStart = LocalDate.of(2025, 3, 1);
System.out.println("2025年3月の月初: " + monthStart); // 2025-03-01
}
}
Javaここでのポイントは、
「月初は“その月の1日”なので、LocalDate.of(年, 月, 1) で素直に表現できる」
ということです。
「ある日付から月初を求める」のではなく、
「そもそも月初を直接作る」方が自然な場面も多いです。
LocalDateTime / ZonedDateTime での月初取得
LocalDateTime の月初(時刻はそのまま or 0時にする)
LocalDateTime でも withDayOfMonth(1) や firstDayOfMonth() が使えます。
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
public class LocalDateTimeMonthStart {
public static void main(String[] args) {
LocalDateTime dt = LocalDateTime.of(2025, 3, 26, 15, 30);
LocalDateTime monthStartSameTime =
dt.with(TemporalAdjusters.firstDayOfMonth());
LocalDateTime monthStartAtMidnight =
dt.with(TemporalAdjusters.firstDayOfMonth())
.withHour(0).withMinute(0).withSecond(0).withNano(0);
System.out.println("元日時 : " + dt); // 2025-03-26T15:30
System.out.println("月初 同じ時刻 : " + monthStartSameTime); // 2025-03-01T15:30
System.out.println("月初 0時ちょうど: " + monthStartAtMidnight); // 2025-03-01T00:00
}
}
Java業務では、
「月初の 0:00 から月末の 23:59:59 までを対象にする」
といった要件が多いので、
「日付だけでなく時刻もどうするか」を意識して設計するとよいです。
ZonedDateTime の月初(タイムゾーン付き)
タイムゾーン付きの ZonedDateTime でも同様です。
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAdjusters;
public class ZonedDateTimeMonthStart {
public static void main(String[] args) {
ZonedDateTime zdt = ZonedDateTime.of(
2025, 3, 26,
15, 30, 0, 0,
ZoneId.of("Asia/Tokyo")
);
ZonedDateTime monthStart =
zdt.with(TemporalAdjusters.firstDayOfMonth())
.withHour(0).withMinute(0).withSecond(0).withNano(0);
System.out.println("元日時 : " + zdt);
System.out.println("月初0時: " + monthStart);
}
}
Java海外システムと連携する場合や、
「日本時間の月初」「UTC の月初」を区別したい場合は、ZonedDateTime で月初を扱う方が安全です。
実務での「月初取得」あるあるパターン
月次バッチの対象期間を求める
例えば「今月の 1 日 0:00 から、今月の月末 23:59:59 まで」を対象にしたい場合です。
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ここでのポイントは、
「月初取得」と「月末取得」をセットで考えると、月次処理の期間がきれいに書ける
ということです。
まとめ:月初取得で身につけてほしい感覚
月初取得は、「その月の1日」を安全かつ意図が伝わる形で取り出すことです。
LocalDate なら withDayOfMonth(1) かwith(TemporalAdjusters.firstDayOfMonth())。
「今月の月初」なら LocalDate.now().with(firstDayOfMonth())。LocalDateTime / ZonedDateTime では、日付に加えて「時刻をどうするか」も決める。
年月が分かっているなら LocalDate.of(年, 月, 1) で素直に作る。
あなたのコードのどこかに、day = 1; のように自前で月初を作っている箇所があれば、
そこを一度 withDayOfMonth(1) や firstDayOfMonth() に置き換えられないか眺めてみてください。
それだけで、
「カレンダー計算をきちんと API に任せられるエンジニア」に、また一歩近づけます。
