はじめに 「年一覧生成」は“長期スパンの集計・分析の土台”
売上の年次推移、年度別の件数集計、ログの年別アーカイブ。
こういう「年単位」で物事を見る処理の裏側には、たいてい「年一覧」がいます。
C# では、DateTime や単なる int(年だけ)を使って、
「開始年から終了年までを順番に列挙する」ユーティリティを作るだけで、
年次処理の多くがぐっと書きやすくなります。
ここでは、
最も基本的な「年のリスト生成」から、DateTime 版・年度対応・直近N年・
そして実務での設計ポイントまで、初心者向けにかみ砕いて説明します。
基本の考え方:年は「int」で扱うか「DateTime」で扱うか
年だけ欲しいなら int、日付も絡むなら DateTime
「2020, 2021, 2022, 2023…」のように、
単純に“年の数字だけ”が欲しいなら int の列で十分です。
一方で、「その年の1月1日」や「その年の開始日・終了日」も一緒に扱いたいなら、DateTime(あるいは DateOnly)で持っておくと便利です。
まずは一番シンプルな int 版から見ていき、そのあと DateTime 版・年度版へと広げていきます。
年一覧生成の基本形(int 版)
開始年から終了年までを順番に列挙する
最も基本的な「年一覧生成」は、次のように書けます。
using System;
using System.Collections.Generic;
public static class YearListUtil
{
public static IEnumerable<int> GetYearList(int fromYear, int toYear)
{
if (toYear < fromYear)
{
throw new ArgumentException("終了年は開始年以上である必要があります。");
}
int current = fromYear;
while (current <= toYear)
{
yield return current;
current++;
}
}
}
C#使い方の例です。
foreach (var year in YearListUtil.GetYearList(2020, 2024))
{
Console.WriteLine(year);
}
// 出力:
// 2020
// 2021
// 2022
// 2023
// 2024
C#ここでの重要ポイントは、
「開始年・終了年を含む閉区間(fromYear 以上 toYear 以下)」としていることです。
年次グラフや年別集計では、この形が一番自然なことが多いです。
DateTime 版:その年の代表日(1月1日)を持たせる
「年+日付」をセットで扱いたい場合
年次処理の中で、「その年の開始日」「その年の範囲」をすぐに使いたいことがあります。
その場合、DateTime で「その年の1月1日」を持たせておくと便利です。
using System;
using System.Collections.Generic;
public static class YearDateTimeListUtil
{
public static IEnumerable<DateTime> GetYearStartDates(int fromYear, int toYear)
{
if (toYear < fromYear)
{
throw new ArgumentException("終了年は開始年以上である必要があります。");
}
int current = fromYear;
while (current <= toYear)
{
yield return new DateTime(current, 1, 1);
current++;
}
}
}
C#使い方の例です。
foreach (var d in YearDateTimeListUtil.GetYearStartDates(2020, 2023))
{
Console.WriteLine(d.ToString("yyyy-MM-dd"));
}
// 出力:
// 2020-01-01
// 2021-01-01
// 2022-01-01
// 2023-01-01
C#この「その年の1月1日」を起点に、
「その年の開始日」「その年の終了日」「その年の全日付一覧」などを
派生させて作ることができます。
年の範囲情報をまとめた小さな型を作る
「年+開始日+終了日」をひとまとめにする
実務では、「その年の開始日と終了日」をセットで扱うことが多いです。
例えば、「2024年のデータだけ抽出したい」といったケースです。
そこで、小さな struct を作って、
「年」「開始日」「終了日」をひとまとめにしてしまうと扱いやすくなります。
public readonly struct YearRange
{
public int Year { get; }
public DateTime StartDate { get; }
public DateTime EndDate { get; }
public YearRange(int year)
{
Year = year;
StartDate = new DateTime(year, 1, 1);
EndDate = new DateTime(year, 12, 31);
}
public override string ToString() => Year.ToString();
}
C#この YearRange を一覧で返すユーティリティです。
public static class YearRangeUtil
{
public static IEnumerable<YearRange> GetYearRanges(int fromYear, int toYear)
{
if (toYear < fromYear)
{
throw new ArgumentException("終了年は開始年以上である必要があります。");
}
int current = fromYear;
while (current <= toYear)
{
yield return new YearRange(current);
current++;
}
}
}
C#使い方の例です。
foreach (var yr in YearRangeUtil.GetYearRanges(2022, 2024))
{
Console.WriteLine($"{yr}: {yr.StartDate:yyyy-MM-dd} ~ {yr.EndDate:yyyy-MM-dd}");
}
// 出力:
// 2022: 2022-01-01 ~ 2022-12-31
// 2023: 2023-01-01 ~ 2023-12-31
// 2024: 2024-01-01 ~ 2024-12-31
C#ここでの重要ポイントは、
「年の範囲(開始・終了)を YearRange の中に閉じ込めている」ことです。
呼び出し側は「どの年を回すか」だけを考えればよくなり、
開始・終了日の計算ロジックをあちこちに書かずに済みます。
応用1:直近N年の一覧を生成する
「今年を含めた直近5年」などのよくある要件
年次グラフやレポートでよくあるのが、
「今年を含めた直近N年分を出したい」という要件です。
using System;
using System.Collections.Generic;
public static class RecentYearUtil
{
public static IEnumerable<int> GetRecentYears(int count, int? baseYear = null)
{
if (count <= 0)
{
throw new ArgumentOutOfRangeException(nameof(count));
}
int year = baseYear ?? DateTime.Today.Year;
int start = year - (count - 1);
for (int y = start; y <= year; y++)
{
yield return y;
}
}
}
C#使い方の例です。
foreach (var y in RecentYearUtil.GetRecentYears(5))
{
Console.WriteLine(y);
}
C#このようにしておくと、
「直近N年」のロジックを一箇所にまとめられ、
画面やレポートごとにバラバラの計算をしなくて済みます。
応用2:年度一覧(会計年度・学年など)を生成する
「4月始まり」「10月始まり」の世界
日本の業務システムでは、「年度」がよく出てきます。
例えば「2024年度」は、
2024/04/01 ~ 2025/03/31 のような期間を指します。
この場合、「年度の開始月」をパラメータにしたユーティリティを作ると便利です。
public readonly struct FiscalYearRange
{
public int FiscalYear { get; }
public DateTime StartDate { get; }
public DateTime EndDate { get; }
public FiscalYearRange(int fiscalYear, int startMonth)
{
FiscalYear = fiscalYear;
StartDate = new DateTime(fiscalYear, startMonth, 1);
DateTime endStart = StartDate.AddYears(1).AddDays(-1);
EndDate = endStart;
}
public override string ToString() => $"{FiscalYear}年度";
}
C#この FiscalYearRange を一覧で返すユーティリティです。
public static class FiscalYearUtil
{
public static IEnumerable<FiscalYearRange> GetFiscalYears(
int fromFiscalYear,
int toFiscalYear,
int startMonth)
{
if (toFiscalYear < fromFiscalYear)
{
throw new ArgumentException("終了年度は開始年度以上である必要があります。");
}
int current = fromFiscalYear;
while (current <= toFiscalYear)
{
yield return new FiscalYearRange(current, startMonth);
current++;
}
}
}
C#使い方の例です(4月始まりの年度)。
foreach (var fy in FiscalYearUtil.GetFiscalYears(2022, 2024, 4))
{
Console.WriteLine($"{fy}: {fy.StartDate:yyyy-MM-dd} ~ {fy.EndDate:yyyy-MM-dd}");
}
// 出力イメージ:
// 2022年度: 2022-04-01 ~ 2023-03-31
// 2023年度: 2023-04-01 ~ 2024-03-31
// 2024年度: 2024-04-01 ~ 2025-03-31
C#ここでの重要ポイントは、
「年度の開始月を引数で受け取り、そこから1年引き伸ばして終了日を決めている」ことです。
会計年度・学年・事業年度など、開始月が違うパターンにも柔軟に対応できます。
実務での注意点1:開始・終了の順序チェックを必ず入れる
「終了年が開始年より小さい」入力をどう扱うか
ユーザー入力や外部データから年範囲を受け取る場合、
「終了年が開始年より前」という不正な範囲が紛れ込むことがあります。
これをそのまま使うと、
「一覧が空になる」「ループが無限になる」などの問題が起こり得ます。
そのため、ユーティリティの入り口で必ずチェックを入れ、
おかしければ例外を投げるか、値を入れ替えるか、
方針を決めておくことが大事です。
個人的には、「入力ミスを早く見つけたい」場面では例外にするほうが安全です。
実務での注意点2:年と日付の混在に気をつける
「2024年」と「2024/01/01~2024/12/31」を混同しない
年一覧生成はシンプルですが、
「年だけのつもりが、どこかで日付として扱ってしまう」
「年度と暦年をごちゃ混ぜにする」
といったミスが起こりやすいところでもあります。
設計としては、
- 暦年(カレンダーの年)は
YearRange - 年度(会計年度など)は
FiscalYearRange
のように型を分けておくと、
「これはどの“年の概念”なのか」がコードから読み取りやすくなります。
まとめ 「年一覧生成ユーティリティ」は“長期スパンの見通しを良くする道具”
年一覧生成は、一見ただの for ループですが、
年次グラフ、年別集計、年度処理など、
長期スパンのビジネスロジックの土台になります。
押さえておきたいポイントをまとめると、次のようになります。
年だけ欲しいなら int の一覧、開始年から終了年までを閉区間で列挙する。
日付も絡めたいなら「その年の1月1日」を DateTime(または DateOnly)で持たせる。
「年+開始日+終了日」をまとめた YearRange や、開始月を持つ FiscalYearRange を用意すると、年次・年度処理が書きやすくなる。
直近N年などのよくある要件は専用ユーティリティにして、ロジックを一箇所に集約する。
開始・終了の順序チェックと、「暦年」と「年度」を型レベルで分ける設計を意識すると、実務でのバグを大きく減らせる。
ここまで押さえておけば、
「その場しのぎで for 文を書いている」状態から一歩進んで、
“年次・年度処理の背骨として安心して使える、実務で使える年一覧生成ユーティリティ”を
自分の C# コードの中に自然に組み込めるようになります。
