如何找到给定月份的第n个DayOfWeek?

时间:2019-04-11 20:22:02

标签: c#

我试图找到给定月份(给定年份)的第n个DayOfWeek。

例如:我正在寻找5月的第三个星期六(2019年)。

我无法使用DayOfWeek扩展方法提出可行的解决方案。我是否需要遍历整个月才能找到第三个星期六?

4 个答案:

答案 0 :(得分:3)

您当然可以遍历整个月,但我认为这是一种更为优雅的方法(摘自here):

private static DateTime FindTheNthDayOfWeek(int year, int month, int nthOccurrence, DayOfWeek dayOfWeek)
    {
        if (month < 1 || month > 12)
        {
            throw new ArgumentOutOfRangeException("Invalid month");
        }

        if (nthOccurrence < 0 || nthOccurrence > 5)
        {
            throw new ArgumentOutOfRangeException("Invalid nth occurrence");
        }

        var dt = new DateTime(year, month, 1);

        while (dt.DayOfWeek != dayOfWeek)
        {
            dt = dt.AddDays(1);
        }

        dt = dt.AddDays((nthOccurrence - 1) * 7);

        if (dt.Month != month)
        {
            throw new ArgumentOutOfRangeException(string.Format("The given month has less than {0} {1}s", nthOccurrence, dayOfWeek));
        }

        return dt;
    }

此私有方法不会在整个月中循环,但是一旦找到第一个DayOfWeek,它便已经停止。然后,您只需为每个第n次出现添加一个星期(减去已经添加的星期;-))。

答案 1 :(得分:1)

如果您谈论日期,则日期应与某些日历相关,例如我的公历。

public static class DataTimeExt
{
    public static IEnumerable<DateTime> TakeWhileInclusive(this DateTime value,
        Func<DateTime, bool> func)
    {
        DateTime dt = value;

        yield return dt; //[first            
        while (func(dt = dt.AddDays(1))) yield return dt; //in between            
        yield return dt; //last]
    }
}

然后您可以迭代日期直到周日,然后再添加14天。

var calendar = new GregorianCalendar();

var dates = new DateTime(2019, 5, 1, calendar)
    .TakeWhileInclusive(dt => calendar.GetDayOfWeek(dt) != DayOfWeek.Sunday);

Console.WriteLine(dates.Last().AddDays(14));

答案 2 :(得分:1)

我创建了两个扩展方法,其中一个从日期获取下一个DayOfWeek(可选地包括日期本身),另一个从前一个DayOfWeek获取,具有相同的功能。

public static DateTime Next(
  this      DateTime source,
  DayOfWeek dayOfWeek,
  bool      considerSameDate
) => ( dayOfWeek - source.DayOfWeek) is var difference
  && difference < (considerSameDate ? 0 : 1)
  ? source.AddDays(difference + 7)
  : source.AddDays(difference)
  ;

public static DateTime Previous(
  this DateTime source,
  DayOfWeek     dayOfWeek,
  bool          considerSameDate
) => dayOfWeek == source.DayOfWeek
     ? ( considerSameDate ? source : source.AddDays(-7) )
     : source.AddDays(
         ( dayOfWeek - source.DayOfWeek ) is var difference
         && difference > 0
         ? difference - 7
         : difference
     );

有了这些,人们可以问你提出的问题:

var x = new System.DateTime(2019, 5, 1).Next(System.DayOfWeek.Saturday, true).AddDays(14);

我创建了一个新的DateTime(2019-05-01),在星期六致电Next,并将5/1作为候选者,然后加上14天,这使其成为2019年5月的第三个星期六。 >

答案 3 :(得分:1)

这很简单而且干净,没有循环。只是一点算术。

static DateTime? NthWeekDayOfMonth( int n, DayOfWeek dow, int year , int month)
{
  DateTime startOfMonth      = new DateTime( year, month, 1 ) ;
  int      offset            = ( 7 + dow - startOfMonth.DayOfWeek ) % 7 ;
  DateTime nthWeekDayOfMonth = startOfMonth
                             .AddDays( offset    )
                             .AddDays( 7 * (n-1) )
                             ;
  bool isSameMonth           =  startOfMonth.Year  == nthWeekDayOfMonth.Year
                             && startOfMonth.Month == nthWeekDayOfMonth.Month
                             ;
  return isSameMonth
    ? nthWeekDayOfMonth
    : (DateTime?) null
    ;
}