LoginSignup
0
2

More than 5 years have passed since last update.

【C#】「第N〇曜日の日付」を取得する方法

Posted at

以前に書いた【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
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2