以前に書いた【C#】「〇曜日の日付」を取得する方法という記事では、「YYYY年の〇曜日の日付を全て取得する」という処理のサンプルコードを載せていますが、実際には「〇曜日の日付が欲しい」というシーンだけではないと思います。
今回は、例えば「毎月第2月曜日は休業」といったシーンに対応できるように、「第N番目の〇曜日の日付」を取得する方法を紹介したいと思います。
コード
DateUtils.cs
public class DateUtils
{
/// <summary>
/// 対象年の指定された曜日に該当する日付を全て取得する。
/// </summary>
/// <param name="targetYear">対象年</param>
/// <param name="wantDayOfWeek">指定曜日。この曜日に合致する日付を取得する。</param>
/// <returns>指定曜日に合致する日付のリスト。</returns>
public static List<DateTime> FindWantDayOfWeek(int targetYear, DayOfWeek wantDayOfWeek)
{
List<DateTime> days = new List<DateTime>();
// 対象年の1月1日(元旦)の曜日を取得する。
DateTime gantan = new DateTime(targetYear, 1, 1);
// 対象年の1月において、引数で指定した曜日に該当する最初の日付を取得する
DateTime date;
if (gantan.DayOfWeek == wantDayOfWeek)
{
date = gantan;
}
else
{
int additionalDay = ((int)(DayOfWeek.Saturday - gantan.DayOfWeek + wantDayOfWeek) % 7) + 1;
date = gantan.AddDays(additionalDay);
}
// 7日ずつ日付をずらして、対象年における指定した曜日の日付を全て取得する。
while (date.Year == targetYear)
{
days.Add(date);
date = date.AddDays(7);
}
return days;
}
/// <summary>
/// 対象年の「第N〇曜日」に該当する日付を全て取得する。
/// </summary>
/// <param name="targetYear">対象年</param>
/// <param name="wantDayOfWeek">指定曜日。この曜日に合致する日付を取得する。</param>
/// <param name="nth">「各月のN番目の日」を表す数字。</param>
/// <returns>「第N〇曜日」のリスト。「第N〇曜日」が存在しない時は空のリストが返される。</returns>
public static List<DateTime> FindWantDayOfWeek(int targetYear, DayOfWeek wantDayOfWeek, int nth)
{
// 対象年の指定された曜日に該当する日付を全て取得する。
var days = FindWantDayOfWeek(targetYear, wantDayOfWeek);
// 日付を月単位でGroupByした後、Whereで「第N〇曜日が存在する月」に絞り込む。
// さらに、ElementAtで各月のN番目の〇曜日の日付を抽出する。
var newDays = days.Select(day => new { Day = day, Month = day.Month }).GroupBy(day => day.Month)
.Where(group => group.Count()>=nth).Select(group => group.ElementAt(nth - 1).Day).ToList();
return newDays;
}
/// <summary>
/// 期間内の「第N〇曜日」に該当する日付を全て取得する。
/// </summary>
/// <param name="targetYear">対象年</param>
/// <param name="wantDayOfWeek">指定曜日。この曜日に合致する日付を取得する。</param>
/// <param name="nth">「各月のN番目の日」を表す数字。</param>
/// <param name="start">期間開始日</param>
/// <param name="end">期間終了日</param>
/// <returns>期間内の「第N〇曜日」に該当する日付のリスト。「第N〇曜日」が存在しない時は空のリストが返される。</returns>
public static List<DateTime> FindWantDayOfWeek(int targetYear, DayOfWeek wantDayOfWeek, int nth, DateTime start, DateTime end)
{
var days = new List<DateTime>();
// 開始年~終了年の指定された曜日に該当する日付を全て取得する。
int elapsedYear = end.Year - start.Year + 1;
var years = Enumerable.Range(start.Year, elapsedYear);
foreach (int year in years)
{
days.AddRange(FindWantDayOfWeek(year, wantDayOfWeek, nth));
}
// 得られた日付リストを、開始日~終了日の間に絞り込む。
var newDays = days.Where(day => day >= start && day <= end).ToList();
return newDays;
}
}
コードの実行
テストコード
- 以下のテストコードでは、「2018/04/01から2019/03/31の間の第2火曜日」を取得・表示しています。
UnitTestDateUtils.cs
[TestClass]
public class UnitTestDateUtils
{
[TestMethod]
public void TestFindWantDayOfWeek()
{
// 2018年4月1日~2019年3月31日の第2火曜日の日付を取得する。
var days = DateUtils.FindWantDayOfWeek(2019, DayOfWeek.Tuesday, 2, new DateTime(2018, 4, 1), new DateTime(2019, 3, 31));
var secondTuesdays = string.Join(",", days.Select(day => day.ToString("yyyy/MM/dd")));
System.Console.WriteLine(secondTuesdays);
}
}
テストコードの実行結果
2018/04/10,2018/05/08,2018/06/12,2018/07/10,2018/08/14,2018/09/11,2018/10/09,2018/11/13,2018/12/11,2019/01/08,2019/02/12,2019/03/12