如何确定某个日期是否是列表中的第n个工作日?一个月后

本文关键字:工作日 一个月后 列表 何确定 日期 是否是 | 更新日期: 2023-09-27 18:08:34

我见过这个问题,它查找特定月份中的第n个工作日,但是我想要查找给定月份中的第n个[工作日列表]。有没有一种简单的方法可以做到这一点,而不需要在一个月的每一天都循环?

例如,如果我说我想要一个月的3rd [Mon, Wed, Fri],并且这个月从星期三开始,它应该返回第一个星期一(1 = Wed, 2 = Fri, 3 = Mon)。如果我说我想要2nd last [Mon, Tue, Wed, Thurs],并且这个月的最后一天是星期天,它应该返回前一个星期三。

如何确定某个日期是否是列表中的第n个工作日?一个月后

我的意思是你可能会想出一些丑陋和难以理解的基于公式并避免循环的东西,但它将是丑陋和难以理解的,因此容易出现bug和维护灾难,并且尝试想出一点也不有趣。

相反,只需想出易于使用循环编写的内容,然后通过使用LINQ:

隐藏循环
static class DateTimeExtensions {
    private static IEnumerable<DateTime> DaysOfMonth(int year, int month) {
        return Enumerable.Range(1, DateTime.DaysInMonth(year, month))
                         .Select(day => new DateTime(year, month, day));
    }
    public static DateTime NthDayOfMonthFrom(
        this HashSet<DayOfWeek> daysOfWeek,
        int year,
        int month,
        int nth
    ) {
        return
            DateTimeExtensions.DaysOfMonth(year, month)
                              .Where(
                                   date => daysOfWeek.Contains(date.DayOfWeek)
                              )
                              .Skip(nth - 1)
                              .First();
    }
}

用法:

var daysOfWeek = new HashSet<DayOfWeek> {
    DayOfWeek.Monday, DayOfWeek.Wednesday, DayOfWeek.Friday
};
DateTime date = daysOfWeek.NthDayOfMonthFrom(2011, 6, 3));
Assert.Equal(date, new DateTime(2011, 6, 6));

同样,使用Reverse,您可以轻松地编写NthLastDayOfWeekFrom。我把它留给你想一个更好的名字。

我想我接受Jason的答案,因为我喜欢他的解决方案,但这是我在看到他的帖子之前想到的。我想我应该发布它,因为我喜欢不循环遍历每个日期的想法,特别是如果N是一个更高的值,我想获得当前月份以外的日期。

我没有循环遍历所有的日子,而是快进(或者倒回)到最近的一周,然后循环遍历接下来的几天,找到第n个实例指定的工作日。

public static DateTime? GetNthWeekDayInMonth(DateTime month, int nth, List<DayOfWeek> weekdays)
{
    var multiplier = (nth < 0 ? -1 : 1);
    var day = new DateTime(month.Year, month.Month, 1);
    // If we're looking backwards, start at end of month
    if (multiplier == -1) day = day.AddMonths(1).AddDays(-1);
    var dayCount = weekdays.Count;
    if (dayCount == 0)
        return null;
    // Fast forward (or rewind) a few days to appropriate week
    var weeks = ((nth - (1 * multiplier)) / dayCount);
    day = day.AddDays(weeks * 7);
    // Current Nth value
    var currentNth = (1 * multiplier) + (weeks * dayCount);
    // Loop through next week looking for Nth value
    for (var x = 0; x <= 7; x++)
    {
        if (weekdays.Contains(day.DayOfWeek))
        {
            if (currentNth == nth)
            {
                // Verify the nth instance is still in the current month
                if (day.Month == month.Month)
                    return day;
                else
                    return null;
            }
            currentNth += multiplier;
        }
        day = day.AddDays(multiplier);
    }
    return null;
}