はじめに 「日付一覧生成」は“カレンダー系ロジックの土台”
「指定期間の全日付を出したい」「1ヶ月分のカレンダーを作りたい」「営業日一覧を作りたい」
こういうときに必ず必要になるのが「日付一覧生成」です。
C# では、DateTime(または DateOnly)を1日ずつ足していくことで、
シンプルに日付のリストを作れます。
ただ、「開始・終了を含むか」「どの単位で増やすか」「条件付き(平日だけなど)にするか」を
きちんと意識しておくと、実務で使いやすいユーティリティになります。
ここでは、
基本の「日付を1日ずつ増やす」パターンから、
範囲指定・月カレンダー・条件付き生成まで、
初心者向けにかみ砕いて説明していきます。
基本:開始日から終了日までを1日ずつ列挙する
一番シンプルな日付一覧生成
まずは、「開始日から終了日まで、1日ずつ全部ほしい」という一番基本の形です。
using System;
using System.Collections.Generic;
public static class DateListUtil
{
public static IEnumerable<DateTime> GetDateList(DateTime from, DateTime to)
{
if (to < from)
{
throw new ArgumentException("終了日は開始日以上である必要があります。");
}
DateTime current = from.Date;
DateTime end = to.Date;
while (current <= end)
{
yield return current;
current = current.AddDays(1);
}
}
}
C#使い方の例です。
DateTime from = new DateTime(2026, 2, 10);
DateTime to = new DateTime(2026, 2, 13);
foreach (var d in DateListUtil.GetDateList(from, to))
{
Console.WriteLine(d.ToString("yyyy-MM-dd"));
}
// 出力:
// 2026-02-10
// 2026-02-11
// 2026-02-12
// 2026-02-13
C#ここでの重要ポイントは次の2つです。
from.Date/to.Dateで「日付だけ」にそろえていることcurrent <= endとして「開始・終了を含む」閉区間にしていること
この形を“基本形”として覚えておくと、
ほとんどの「日付一覧生成」はここから派生させて書けます。
.NET 6以降なら DateOnly 版も素直に書ける
日付だけ扱うなら DateOnly のほうが意図が明確
日付だけを扱いたい場合、DateOnly を使うとよりシンプルになります。
using System;
using System.Collections.Generic;
public static class DateOnlyListUtil
{
public static IEnumerable<DateOnly> GetDateList(DateOnly from, DateOnly to)
{
if (to < from)
{
throw new ArgumentException("終了日は開始日以上である必要があります。");
}
DateOnly current = from;
while (current <= to)
{
yield return current;
current = current.AddDays(1);
}
}
}
C#使い方の例です。
DateOnly from = new DateOnly(2026, 2, 10);
DateOnly to = new DateOnly(2026, 2, 13);
foreach (var d in DateOnlyListUtil.GetDateList(from, to))
{
Console.WriteLine(d);
}
// 出力:
// 2026/02/10
// 2026/02/11
// 2026/02/12
// 2026/02/13
C#DateOnly を使うと、「時刻をどうするか」を考えなくてよくなるので、
カレンダー系の処理ではかなり扱いやすくなります。
応用1:1ヶ月分のカレンダー日付一覧を作る
「その月の1日〜末日」を一気に出す
「月次レポート」「月カレンダー表示」などでは、
「指定年月の全日付」が欲しくなることが多いです。
public static IEnumerable<DateTime> GetDatesOfMonth(int year, int month)
{
DateTime first = new DateTime(year, month, 1);
int daysInMonth = DateTime.DaysInMonth(year, month);
for (int i = 0; i < daysInMonth; i++)
{
yield return first.AddDays(i);
}
}
C#使い方の例です。
foreach (var d in GetDatesOfMonth(2026, 2))
{
Console.WriteLine(d.ToString("yyyy-MM-dd"));
}
C#DateOnly 版も同じ考え方で書けます。
public static IEnumerable<DateOnly> GetDatesOfMonthDateOnly(int year, int month)
{
DateOnly first = new DateOnly(year, month, 1);
int daysInMonth = DateTime.DaysInMonth(year, month);
for (int i = 0; i < daysInMonth; i++)
{
yield return first.AddDays(i);
}
}
C#ここでの重要ポイントは、DateTime.DaysInMonth(year, month) を使って「その月の日数」を正しく取得していることです。
うるう年の2月なども自動で正しく扱ってくれます。
応用2:条件付きの日付一覧(例:平日だけ)
「一覧生成+フィルタ」の組み合わせで考える
実務では、「期間内の全日付」ではなく、
「平日だけ」「営業日だけ」「特定の曜日だけ」
といった条件付きの一覧が欲しくなることが多いです。
基本の考え方はシンプルで、
- まず「全日付一覧」を作る
- その中から条件に合うものだけを選ぶ
という2ステップで考えます。
例えば、「平日だけ」の一覧はこう書けます。
public static IEnumerable<DateTime> GetWeekdays(DateTime from, DateTime to)
{
foreach (var d in DateListUtil.GetDateList(from, to))
{
if (d.DayOfWeek != DayOfWeek.Saturday &&
d.DayOfWeek != DayOfWeek.Sunday)
{
yield return d;
}
}
}
C#使い方の例です。
DateTime from = new DateTime(2026, 2, 10);
DateTime to = new DateTime(2026, 2, 16);
foreach (var d in GetWeekdays(from, to))
{
Console.WriteLine($"{d:yyyy-MM-dd} ({d.DayOfWeek})");
}
C#「特定の曜日だけ」(例:毎週月曜日)なら、条件を変えるだけです。
public static IEnumerable<DateTime> GetSpecificWeekday(DateTime from, DateTime to, DayOfWeek dayOfWeek)
{
foreach (var d in DateListUtil.GetDateList(from, to))
{
if (d.DayOfWeek == dayOfWeek)
{
yield return d;
}
}
}
C#ここでの重要ポイントは、
「一覧生成のロジック」と「条件のロジック」を分けて考えることです。
これを分けておくと、「祝日を除く」「特定日を除外する」などの拡張がしやすくなります。
応用3:LINQ を使った書き方(考え方の整理用)
「日数分のシーケンス」から日付を作る
少し発展的な書き方として、
「0,1,2,… のようなインデックスから日付を作る」パターンもあります。
using System.Linq;
public static IEnumerable<DateTime> GetDateListLinq(DateTime from, DateTime to)
{
int days = (to.Date - from.Date).Days;
return Enumerable
.Range(0, days + 1)
.Select(i => from.Date.AddDays(i));
}
C#Enumerable.Range(0, days + 1) で 0〜days の整数列を作り、
それぞれを「開始日+i日」として日付に変換しています。
初心者のうちは while や for のほうが分かりやすいかもしれませんが、
「日数分のインデックスから日付を作る」という考え方は、
頭の中を整理するのに役立ちます。
実務での注意点1:開始・終了の順序チェック
「終了が開始より前」の不正な入力をどう扱うか
ユーザー入力や外部データから期間を受け取る場合、
「終了日が開始日より前」という不正な範囲が紛れ込むことがあります。
先ほどの GetDateList では、to < from のときに ArgumentException を投げるようにしました。
if (to < from)
{
throw new ArgumentException("終了日は開始日以上である必要があります。");
}
C#ここをどうするかは、システムの方針次第です。
- 例外にして「入力がおかしい」と早めに気づく
- 自動的に入れ替えてしまう(from/to を swap)
など、どちらにするかを最初に決めておきましょう。
個人的には、「入力ミスを早く見つけたい」場面では例外にするほうが安全です。
実務での注意点2:タイムゾーンと「どのカレンダーか」
UTC とローカルが混ざると日付がズレる
サーバー内部で UTC を使い、画面では日本時間を表示しているようなシステムでは、
「どのタイムゾーンの“日付一覧”か」を意識しないと、
「日本時間では 2/10〜2/13 のつもりが、UTC では 2/9〜2/12 になっている」
といったズレが起こります。
日付一覧生成は「カレンダーの世界」の話なので、
ユーザーのタイムゾーンに合わせてから .Date や DateOnly を使うのが基本です。
イメージとしては、こういう流れです。
public static IEnumerable<DateTime> GetLocalDateListFromUtc(
DateTime utcFrom,
DateTime utcTo,
TimeZoneInfo timeZone)
{
DateTime localFrom = TimeZoneInfo.ConvertTimeFromUtc(utcFrom, timeZone).Date;
DateTime localTo = TimeZoneInfo.ConvertTimeFromUtc(utcTo, timeZone).Date;
return DateListUtil.GetDateList(localFrom, localTo);
}
C#「どのタイムゾーンのカレンダーで日付一覧を作っているか」を
ユーティリティ名やコメントに刻んでおくと、後から読んだ人が迷いません。
まとめ 「日付一覧生成ユーティリティ」は“カレンダー処理の共通エンジン”
日付一覧生成は、一見ただのループですが、
期間の扱い方、日付か日時か、条件付きフィルタ、タイムゾーンなど、
カレンダー系ロジックのエッセンスが詰まっています。
押さえておきたいポイントを整理すると、こうなります。
- 基本形は「開始日から終了日まで、1日ずつ
AddDays(1)で進める」 - 日付だけを扱いたいときは
.DateやDateOnlyを使い、「時刻を無視する」ことをコードで明示する - 月単位の一覧は「1日+
DaysInMonth」で素直に作れる - 条件付き一覧(平日だけ、特定曜日だけ)は「全日付一覧+フィルタ」の組み合わせで考える
- 開始・終了の順序チェックと、タイムゾーン(どのカレンダーか)を最初に設計しておく
ここを押さえておけば、
「その場しのぎで for 文を書いている」状態から一歩進んで、
“いろいろなカレンダー処理の土台として再利用できる、実務で使える日付一覧生成ユーティリティ”を
自分の C# コードの中に気持ちよく組み込めるようになります。
