Java Tips | 日付・時間:年初取得

Java Java
スポンサーリンク

「年初取得」は“その年の1月1日を意味で取り出す”こと

業務システムだと、「今年の年初」「会計年度の開始日」「前年の年初」みたいな話がよく出てきます。
ここでやってはいけないのは、month = 1; day = 1; のように、自分でフィールドを書き換えることです。

Java には、「その年の1月1日」を“名前付き”で安全に取り出すための API が用意されています。
それを素直に使うと、年次処理・年間集計・年度管理のコードが一気に読みやすくなります。


LocalDate から年初を取得する基本パターン

withMonth(1).withDayOfMonth(1) で「その年の1月1日」

一番ストレートな書き方はこれです。

import java.time.LocalDate;

public class YearStartBasic {

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

        LocalDate yearStart = anyDay.withMonth(1)
                                    .withDayOfMonth(1);

        System.out.println("任意の日付 : " + anyDay);     // 2025-03-26
        System.out.println("その年の年初: " + yearStart); // 2025-01-01
    }
}
Java

ここで押さえてほしいポイントは二つあります。

一つ目は、withXxx「そのフィールドだけを差し替えた新しい LocalDate を返す」ということです。
withMonth(1) で「同じ年・同じ日だが月だけ1月」、
さらに withDayOfMonth(1) で「その年の1月1日」になります。
元の anyDay は変わりません(不変オブジェクト)。

二つ目は、「1月1日は必ず存在する」ので、
withMonth(1).withDayOfMonth(1) は常に安全に使える、ということです。


TemporalAdjusters を使った「年初」の書き方

firstDayOfYear() を使うと“意図が名前になる”

TemporalAdjusters には、「年初」「年末」などを
“意味のある名前”で表現するためのメソッドが用意されています。

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

public class YearStartAdjuster {

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

        LocalDate yearStart =
                anyDay.with(TemporalAdjusters.firstDayOfYear());

        System.out.println("任意の日付 : " + anyDay);     // 2025-03-26
        System.out.println("その年の年初: " + yearStart); // 2025-01-01
    }
}
Java

with(TemporalAdjusters.firstDayOfYear()) と書いてあるだけで、
「このコードは“年初を取っている”んだな」と一瞬で分かります。

withMonth(1).withDayOfMonth(1) よりも、
「意図がコードにそのまま出ている」という意味で、実務ではこちらを強くおすすめします。


「今年の年初」を取得する

LocalDate.now() と firstDayOfYear の組み合わせ

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

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

public class ThisYearStart {

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

        LocalDate yearStart =
                today.with(TemporalAdjusters.firstDayOfYear());

        System.out.println("今日   : " + today);
        System.out.println("今年の年初: " + yearStart);
    }
}
Java

業務的には、
「今年の売上集計」「今年のログ抽出」「今年のバッチ対象期間」などで、
この「今年の年初」が起点になることが多いです。


「任意の年の年初」を直接作る

LocalDate.of(年, 1, 1) で素直に表現する

「2025 年の年初」のように、年がはっきりしている場合は、
最初から 1 月 1 日を指定してしまうのも自然です。

import java.time.LocalDate;

public class SpecificYearStart {

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

        System.out.println("2025年の年初: " + yearStart); // 2025-01-01
    }
}
Java

「ある日付から年初を求める」のではなく、
「そもそも年初を直接作る」方が分かりやすい場面も多いです。

例えば「前年の年初」を求めたいなら、

LocalDate thisYearStart = LocalDate.of(2025, 1, 1);
LocalDate lastYearStart = thisYearStart.minusYears(1); // 2024-01-01
Java

のように、「年初を基準に minusYears(1)」とするのもきれいです。


LocalDateTime / ZonedDateTime での年初取得

LocalDateTime の年初(時刻をどうするかを決める)

LocalDateTime でも firstDayOfYear が使えます。

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

public class LocalDateTimeYearStart {

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

        LocalDateTime yearStartSameTime =
                dt.with(TemporalAdjusters.firstDayOfYear());

        LocalDateTime yearStartAtMidnight =
                dt.with(TemporalAdjusters.firstDayOfYear())
                  .withHour(0).withMinute(0).withSecond(0).withNano(0);

        System.out.println("元日時            : " + dt);                 // 2025-03-26T15:30
        System.out.println("年初 同じ時刻     : " + yearStartSameTime);  // 2025-01-01T15:30
        System.out.println("年初 0時ちょうど : " + yearStartAtMidnight);// 2025-01-01T00:00
    }
}
Java

業務では、
「今年の 1 月 1 日 0:00 から、今年の 12 月 31 日 23:59:59 まで」
といった“年間の対象期間”を扱うことが多いので、
「日付だけでなく、時刻をどこに合わせるか」をきちんと決めることが大事です。

ZonedDateTime の年初(タイムゾーン付き)

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

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

public class ZonedDateTimeYearStart {

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

        ZonedDateTime yearStart =
                zdt.with(TemporalAdjusters.firstDayOfYear())
                   .withHour(0).withMinute(0).withSecond(0).withNano(0);

        System.out.println("元日時      : " + zdt);
        System.out.println("年初 0時    : " + yearStart);
    }
}
Java

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


実務での「年初取得」あるあるパターン

今年の対象期間(年初〜年末)を求める

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

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

public class YearlyRangeExample {

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

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

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

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

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


まとめ:年初取得で身につけてほしい感覚

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

LocalDate なら with(TemporalAdjusters.firstDayOfYear())
あるいは withMonth(1).withDayOfMonth(1)
「今年の年初」なら LocalDate.now().with(firstDayOfYear())
LocalDateTime / ZonedDateTime では、日付に加えて「時刻をどこに合わせるか」を決める。
年が分かっているなら LocalDate.of(年, 1, 1) で素直に作る。

あなたのコードのどこかに、
month = 1; day = 1; のように自前で年初を作っている箇所があれば、
そこを一度 firstDayOfYear()LocalDate.of(年, 1, 1) に置き換えられないか眺めてみてください。

その小さな置き換えが、
「カレンダー計算を API に任せられるエンジニア」への、確かな一歩になります。

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