C#如何计算不包括特定日期(节假日)的工作日

Posted

技术标签:

【中文标题】C#如何计算不包括特定日期(节假日)的工作日【英文标题】:C# How to calculate working days excluding specific dates (Holidays) 【发布时间】:2017-09-14 18:57:12 【问题描述】:

我阅读了一些与 C# 中的函数相关的问题/答案来计算工作日(从星期一到星期五的天数);有些人使用扩展代码来实现这一点。

我有一个超过 50,000 行的数据表,我需要一种有效的方法来计算这些信息。

【问题讨论】:

要明确 - 您是要查找某一年的所有假期(然后您可以计算这些天损失的小时数),还是要查找工作时间年? 【参考方案1】:

根据@MiBols 的回答,更有效的解决方案是使用HashSet<DateTime> 而不是List<DateTime>,其中搜索然后在O(1)

/// <summary> Get working days between two dates (Excluding a list of dates - Holidays) </summary>
/// <param name="dtmCurrent">Current date time</param>
/// <param name="dtmFinishDate">Finish date time</param>
/// <param name="excludedDates">Collection of dates to exclude (Holidays)</param>
public static int fwGetWorkingDays(this DateTime dtmCurrent, DateTime dtmFinishDate, 
                                   HashSet<DateTime> excludedDates)

    Func<DateTime, bool> workDay = currentDate => (
        currentDate.DayOfWeek == DayOfWeek.Saturday ||
        currentDate.DayOfWeek == DayOfWeek.Sunday ||
        excludedDates.Contains(currentDate.Date));

    return Enumerable.Range(0, 1 + (dtmFinishDate - dtmCurrent).Days)
        .Count(intDay => workDay(dtmCurrent.AddDays(intDay)));

【讨论】:

【参考方案2】:
/// <summary> Get working days between two dates (Excluding a list of dates - Holidays) </summary>
/// <param name="dtmCurrent">Current date time</param>
/// <param name="dtmFinishDate">Finish date time</param>
/// <param name="lstExcludedDates">List of dates to exclude (Holidays)</param>
public static int fwGetWorkingDays(this DateTime dtmCurrent, DateTime dtmFinishDate, List<DateTime> lstExcludedDates)

    Func<DateTime, bool> workDay = currentDate =>
            (
                currentDate.DayOfWeek == DayOfWeek.Saturday ||
                currentDate.DayOfWeek == DayOfWeek.Sunday ||
                lstExcludedDates.Exists(evalDate => evalDate.Date.Equals(currentDate.Date))
            );

    return Enumerable.Range(0, 1 + (dtmFinishDate - dtmCurrent).Days).Count(intDay => workDay(dtmCurrent.AddDays(intDay)));

我创建了这个扩展,它基本上需要两个日期和一个要排除的日期列表(假期)。 令人惊讶的是,这种方法非常快,并且在与排除列表进行比较时,可以验证从 DateTime 中排除时间。

【讨论】:

取决于“工作日”的定义。公共假期不被视为工作日。我做你所做的,另外我为该软件所服务的特定类型的组织在接下来的几年中保留了每年的公共假期列表。如果您将公共假期支票添加到您的代码中,您将获得一个真正的工作日计算器。花 10 分钟在 db 表中输入未来几年的 10-12 行并不是很大的负担。 我很惊讶,workDay 的定义被颠倒了......我已经重写了它(排除日期的第二个答案语法): Func workDay = currentDate => ( currentDate. DayOfWeek != DayOfWeek.Saturday && currentDate.DayOfWeek != DayOfWeek.Sunday && !excludedDates.Contains(currentDate.Date));此外,我没有在“return Enumerable.Range(0, 1 + (”) 中添加“+1”,因为我只考虑过去的几天,并且正在考虑在早上计算差异,在营业日开始时知道我是否迟到了。【参考方案3】:
public static int ToBusinessWorkingDays(this DateTime start, DateTime due, DateTime[] holidays)
        
            return Enumerable.Range(0, (due - start).Days)
                            .Select(a => start.AddDays(a))
                            .Where(a => a.DayOfWeek != DayOfWeek.Sunday)
                            .Where(a => a.DayOfWeek != DayOfWeek.Saturday)
                            .Count(a => !holidays.Any(x => x == a));

        

【讨论】:

【参考方案4】:

我混合了上面的两个解决方案,反转表达式以验证 workDay 并检查差异是否为负。

public static int GetWorkingDaysUntil(this DateTime dtmCurrent, DateTime dtmFinishDate, HashSet<DateTime> excludedDates)

    Func<DateTime, bool> workDay = currentDate => !(currentDate.DayOfWeek == DayOfWeek.Saturday ||
                                                    currentDate.DayOfWeek == DayOfWeek.Sunday ||
                                                    excludedDates.Contains(currentDate.Date));

    var daysDifference = (dtmFinishDate - dtmCurrent).Days;

    return daysDifference > 0 ?
                Enumerable.Range(0, 1 + daysDifference).Count(intDay => workDay(dtmCurrent.AddDays(intDay))) :
                0;

【讨论】:

以上是关于C#如何计算不包括特定日期(节假日)的工作日的主要内容,如果未能解决你的问题,请参考以下文章

如何在 oracle 中计算工作日,不包括一周中的任何特定日期?

在相邻单元格中找到的日期之后的十个工作日(不包括节假日)突出显示相邻单元格

选择日期范围内的特定日期

选择日期+3天,不包括周末和节假日

moment.js能判断是不是为节假日吗

C#跳过工作日,计算几个工作日之后到期的方法