Java Tips | 日付・時間:年一覧生成

Java Java
スポンサーリンク

年一覧生成とは何をするユーティリティか

「年度選択のプルダウンに 2015〜2030 年を出したい」「レポート画面で対象年を選ばせたい」「ログ集計で“どの年のデータか”を一覧にしたい」。
こういうときに使うのが「年一覧生成」のユーティリティです。

やっていることはシンプルで、
開始年と終了年を渡すと、その間の年を順番に並べてくれるだけです。
でも、業務システムではほぼ必ず出てくる処理なので、きちんとユーティリティ化しておくと、コードがかなりスッキリします。


一番基本的な「年一覧生成」ユーティリティ

開始年から終了年までを1年ずつ並べる

まずは、最も基本的な形からいきます。

import java.util.ArrayList;
import java.util.List;

public class YearRange {

    public static List<Integer> yearsBetween(int startInclusive, int endInclusive) {
        if (endInclusive < startInclusive) {
            throw new IllegalArgumentException("終了年は開始年以上である必要があります");
        }

        List<Integer> result = new ArrayList<>();
        int year = startInclusive;
        while (year <= endInclusive) {
            result.add(year);
            year++;
        }
        return result;
    }
}
Java

使い方の例です。

public class YearRangeExample {

    public static void main(String[] args) {
        List<Integer> years = YearRange.yearsBetween(2020, 2025);
        for (Integer y : years) {
            System.out.println(y);
        }
    }
}
Java

出力はこうなります。

2020
2021
2022
2023
2024
2025

ここで押さえておきたいポイントは三つです。
開始年も終了年も「含む」こと。
終了年が開始年より小さい場合は例外にして、バグや誤設定を早期に気づけるようにすること。
1年ずつインクリメントしているだけなので、ロジックが非常に読みやすいこと。


Stream を使った年一覧生成

IntStream.rangeClosed でスッキリ書く

Java 8 以降なら、IntStream を使ってもっとコンパクトに書けます。

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class YearRangeStream {

    public static List<Integer> yearsBetween(int startInclusive, int endInclusive) {
        if (endInclusive < startInclusive) {
            throw new IllegalArgumentException("終了年は開始年以上である必要があります");
        }

        return IntStream.rangeClosed(startInclusive, endInclusive)
                        .boxed()
                        .collect(Collectors.toList());
    }
}
Java

やっていることは同じですが、
「start〜end の整数をそのまま年として扱う」という意図が、よりはっきり見えます。


よくある業務パターン別の年一覧生成

直近◯年分の一覧を作る

「直近5年の売上」「直近10年のログ」「直近3年の契約」など、よくある要件です。

import java.time.Year;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RecentYears {

    public static List<Integer> lastYears(int count) {
        if (count <= 0) {
            throw new IllegalArgumentException("count は 1以上で指定してください");
        }

        int currentYear = Year.now().getValue();
        List<Integer> result = new ArrayList<>();
        int year = currentYear;
        for (int i = 0; i < count; i++) {
            result.add(year);
            year--;
        }
        Collections.reverse(result);
        return result;
    }
}
Java

使い方の例です。

public class RecentYearsExample {

    public static void main(String[] args) {
        List<Integer> years = RecentYears.lastYears(5);
        years.forEach(System.out::println);
    }
}
Java

ここで深掘りしたいのは、「過去に向かって減らし、最後に reverse して古い順に並べる」というパターンです。
グラフや一覧表示では「古い年 → 新しい年」の順で並べることが多いので、この形にしておくとそのまま使えます。

会計年度・事業年度の一覧を作る

日本のシステムだと、「年度(4月〜翌年3月)」で扱うことが非常に多いです。
この場合、「年度のラベル」として年を持つことがよくあります。

例えば「2024年度」は「2024-04-01〜2025-03-31」ですが、
一覧としては「2022年度」「2023年度」「2024年度」といった形で年だけ並べたい、というケースです。

import java.util.ArrayList;
import java.util.List;

public class FiscalYears {

    public static List<Integer> fiscalYearsBetween(int startYearInclusive, int endYearInclusive) {
        if (endYearInclusive < startYearInclusive) {
            throw new IllegalArgumentException("終了年度は開始年度以上である必要があります");
        }

        List<Integer> result = new ArrayList<>();
        int year = startYearInclusive;
        while (year <= endYearInclusive) {
            result.add(year);
            year++;
        }
        return result;
    }
}
Java

ここでは「年度の開始年」を整数で持っているイメージです。
例えば「2024年度」であれば 2024 を持ち、実際の期間は別のユーティリティで計算する、という分担にすると設計がきれいになります。


年一覧と他のユーティリティのつなぎ方

年から YearMonth や LocalDate に橋渡しする

年一覧だけではなく、「その年の月一覧」「その年の期間」を扱いたいことも多いです。
そのときは、年一覧生成ユーティリティと、月一覧・日付範囲のユーティリティを組み合わせます。

例えば「2023〜2025年の全月一覧」を作りたい場合は、次のようにできます。

import java.time.YearMonth;
import java.util.ArrayList;
import java.util.List;

public class YearToMonths {

    public static List<YearMonth> monthsOfYears(int startYear, int endYear) {
        List<Integer> years = YearRange.yearsBetween(startYear, endYear);
        List<YearMonth> result = new ArrayList<>();
        for (Integer y : years) {
            for (int m = 1; m <= 12; m++) {
                result.add(YearMonth.of(y, m));
            }
        }
        return result;
    }
}
Java

こうしておくと、「年単位で選ばせる」「月単位で集計する」「日単位で処理する」といった階層的な設計がしやすくなります。


セキュリティ・運用の観点から見た年一覧生成

範囲が“広すぎないか”を必ず意識する

年一覧生成は軽そうに見えますが、ユーザー入力から自由に開始年・終了年を受け取る場合は注意が必要です。
例えば「1900〜9999」などをそのまま受け入れると、8000年以上分のリストができます。

年単位なので日単位ほど極端ではありませんが、それでも無制限に許すのは危険です。
実務では、例えば次のような制限をユーティリティ側に組み込むことが多いです。

現在年から ±100年まで
システムの運用開始年〜現在年+数年まで
それ以外はエラー、あるいは警告ログを出す

こうした制限は、誤設定や悪意ある入力による負荷増大を防ぐうえで重要です。

表示用フォーマットを統一する

年一覧は、画面やログで「2024」「2024年」などの文字列として表示されます。
フォーマットがバラバラだと、人間にも機械にも扱いづらくなります。

例えば「画面では必ず YYYY年 形式で表示する」「ログでは YYYY のみ」といったルールを決め、
小さなフォーマットユーティリティを用意しておくと、システム全体の一貫性が上がります。


まとめ:年一覧生成で身につけてほしい感覚

年一覧生成は、「開始年から終了年までを、年単位で並べる」だけのシンプルな処理です。
でも、その中に大事な考え方が詰まっています。

開始・終了の関係を必ずチェックし、終了が開始より前なら例外にする。
ループでも Stream でもよいが、「年の列を生成している」という意図が読み取れる書き方にする。
直近◯年、特定の年度範囲など、業務でよく使うパターンをユーティリティ化しておく。
範囲が広すぎないように上限を決め、誤設定や負荷増大を防ぐ。

もしあなたのプロジェクトで、画面やバッチのあちこちに「for (int y = 2020; y <= 2030; y++)」のようなコードが散らばっているなら、
それを一度「年一覧生成ユーティリティ」にまとめてみてください。

それだけで、コードの意図がはっきりし、
“業務で長く運用できる”日付・時間まわりの設計に一歩近づきます。

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