如何获得下一个工作日,不包括周末和节假日

本文关键字:周末 节假日 不包括 何获得 下一个 工作日 | 更新日期: 2023-09-27 18:19:38

我有一个需要处理日期字段的需求,所以这个需求类似于

我将该字段称为最短可能日期

  1. 将+1添加到日期

  2. 如果添加1天后,最短可能日期恰好在周末(周六或周日),则显示下一个工作日,即周一

  3. 如果可能的最短日期恰好在假日,则显示下一个工作日。(假期1.1、1.5、3.10、25.12、26.12)

  4. 如果添加1天后,最短可能的日期恰好在周末(周六或周日),并且第二天是假期,则显示下一个工作日。例如:+1天后,如果最短可能的一天是星期六,我们将不得不在星期一展出。但是如果星期一正好是假日,那么我们就必须展示星期二。

我已经尝试了通过多个if和else案例来解决上述问题,但只是想知道是否有任何通用且优雅的方法可以做到这一点?

我试过

var holidays = new List<DateTime>();
holidays.Add(new DateTime(DateTime.Now.Year,1,1));
holidays.Add(new DateTime(DateTime.Now.Year,1,5));
holidays.Add(new DateTime(DateTime.Now.Year,3,10));
holidays.Add(new DateTime(DateTime.Now.Year,12,25));
if (date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday)
{
     //Logic to add +1 and again some logic to check for weekends and weekdays
}
else if (holidays.Contain(date))
{
   //Logic to add +1 and again some logic to check for weekends and weekdays
}

如何获得下一个工作日,不包括周末和节假日

基本上你想要下一个工作日。所以你可以在这个条件下循环,在当前日期上加1天

do {
  date = date.AddDays(1);
} while(IsHoliday(date) || IsWeekend(date));

在前面的代码中,IsHoliday是一个谓词,用于判断日期是否为假日。例如,无耻地重复使用您的代码:

class Program
{
    private static readonly HashSet<DateTime> Holidays = new HashSet<DateTime>();
    private static bool IsHoliday(DateTime date)
    {
        return Holidays.Contains(date);
    }
    private static bool IsWeekend(DateTime date)
    {
        return date.DayOfWeek == DayOfWeek.Saturday
            || date.DayOfWeek == DayOfWeek.Sunday;
    }

    private static DateTime GetNextWorkingDay(DateTime date)
    {
        do
        {
            date = date.AddDays(1);
        } while (IsHoliday(date) || IsWeekend(date));
        return date;
    }
    static void Main(string[] args)
    {
        Holidays.Add(new DateTime(DateTime.Now.Year, 1, 1));
        Holidays.Add(new DateTime(DateTime.Now.Year, 1, 5));
        Holidays.Add(new DateTime(DateTime.Now.Year, 3, 10));
        Holidays.Add(new DateTime(DateTime.Now.Year, 12, 25));
        var dt = GetNextWorkingDay(DateTime.Parse(@"2015-10-31"));
        Console.WriteLine(dt);
        Console.ReadKey();
    }
}

根据@fjardon的回答,您可以使用我的项目Nager.Date它包含100多个国家的公共假日和周末。

nuget

PM> install-package Nager.Date

代码片段

//usings
using Nager.Date;
using Nager.Date.Extensions;
//logic
var date = DateTime.Today; //Set start date
var countryCode = CountryCode.US; //Set country
do
{
    date = date.AddDays(1);
} while (DateSystem.IsPublicHoliday(date, countryCode) || date.IsWeekend(countryCode));

固定日期列表是表达节日的一种有限方式。

考虑到你的列表只包含当年的日期,所以如果今天是2015年12月30日,那么下一个假期将是2016年1月1日——你在列表中找不到。

还要考虑到,每年的许多假期都不在同一天。它们通常与一周中的哪一天联系在一起,有时由宗教日历或任意决定。

一个更强大的系统应该能处理各种不同类型的假期。这里有一个可能的实现:

public abstract class Holiday
{
    public abstract DateTime? GetDate(int year);
}
public class MonthDayBasedHoliday : Holiday
{
    private readonly int _month;
    private readonly int _day;
    public MonthDayBasedHoliday(int month, int day)
    {
        _month = month;
        _day = day;
    }
    public override DateTime? GetDate(int year)
    {
        return new DateTime(year, _month, _day);
    }
}
public class DayOfWeekBasedHoliday : Holiday
{
    private readonly int _occurrence;
    private readonly DayOfWeek _dayOfWeek;
    private readonly int _month;
    public DayOfWeekBasedHoliday(int occurrence, DayOfWeek dayOfWeek, int month)
    {
        _occurrence = occurrence;
        _dayOfWeek = dayOfWeek;
        _month = month;
    }
    public override DateTime? GetDate(int year)
    {
        if (_occurrence <= 4)
        {
            DateTime dt = new DateTime(year, _month, 1);
            int delta = (_dayOfWeek - dt.DayOfWeek + 7) % 7;
            delta += 7 * (_occurrence - 1);
            return dt.AddDays(delta);
        }
        else  // last occurrence in month
        {
            int daysInMonth = DateTime.DaysInMonth(year, _month);
            DateTime dt = new DateTime(year, _month, daysInMonth);
            int delta = (dt.DayOfWeek - _dayOfWeek + 7) % 7;
            return dt.AddDays(-delta);
        }
    }
}
public class FixedDateBasedHoliday : Holiday
{
    private readonly IDictionary<int, DateTime> _dates;
    public FixedDateBasedHoliday(params DateTime[] dates)
    {
        _dates = dates.ToDictionary(x => x.Year, x => x);
    }
    public override DateTime? GetDate(int year)
    {
        if (_dates.ContainsKey(year))
            return _dates[year];
        // fixed date not established for year
        return null;
    }
}

有了这些定义,我们现在可以更有力地定义假期,例如:

var holidays = new List<Holiday>();
// New Year's Day
holidays.Add(new MonthDayBasedHoliday(1, 1));
// President's Day (US)
holidays.Add(new DayOfWeekBasedHoliday(3, DayOfWeek.Monday, 2));
// Easter (Western Observance)
holidays.Add(new FixedDateBasedHoliday(new DateTime(2015, 4, 5), new DateTime(2016, 3, 27)));
// Memorial Day (US)
holidays.Add(new DayOfWeekBasedHoliday(5, DayOfWeek.Monday, 5));
// Christmas Day
holidays.Add(new MonthDayBasedHoliday(12, 25));

现在,我们可以根据您的要求创建一个检查下一个工作日的方法:

public static DateTime GetNextNonHolidayWeekDay(DateTime date, IList<Holiday> holidays, IList<DayOfWeek> weekendDays)
{
    // always start with tomorrow, and truncate time to be safe
    date = date.Date.AddDays(1);
    // calculate holidays for both this year and next year
    var holidayDates = holidays.Select(x => x.GetDate(date.Year))
        .Union(holidays.Select(x => x.GetDate(date.Year + 1)))
        .Where(x=> x != null)
        .Select(x=> x.Value)
        .OrderBy(x => x).ToArray();
    // increment until we get a non-weekend and non-holiday date
    while (true)
    {
        if (weekendDays.Contains(date.DayOfWeek) || holidayDates.Contains(date))
            date = date.AddDays(1);
        else
            return date;
    }
}

该方法可以放在抽象的Holiday类上,也可以放在任何地方。

示例用法(具有holidays的上述定义):

var weekendDays = new[] { DayOfWeek.Saturday, DayOfWeek.Sunday };
DateTime workDay = GetNextNonHolidayWeekDay(new DateTime(2015, 12, 31), holidays, weekendDays);
// returns 2016-01-04

不过,这仍然不是一个完全的解决方案。许多节假日有更复杂的计算规则。作为留给读者的练习,试着在美国感恩节假期的第二天实现一个源自Holiday的类。第一天总是在11月的第四个星期四,但第二天总是"11月第四个周四之后的星期五",而不仅仅是"11月的第一个星期五"(有关这一点的示例,请参阅2019年11月)。

var Holidays = new List<DateTime>();
Holidays.Add(new DateTime(DateTime.Now.Year, 1, 1));
Holidays.Add(new DateTime(DateTime.Now.Year, 1, 5));
Holidays.Add(new DateTime(DateTime.Now.Year, 3, 10));
Holidays.Add(new DateTime(DateTime.Now.Year, 12, 25));
var exclude = new List<DayOfWeek> {DayOfWeek.Saturday, DayOfWeek.Sunday};
var targetDate = new DateTime(2015, 12, 24);

var myDate = Enumerable.Range(1, 30)
  .Select( i => targetDate.AddDays(i) )
  .First(a =>  
  !( exclude.Contains( a.DayOfWeek ) || 
     Holidays.Contains(a)) );

我在项目中有类似的需求,我使用SQl Server进行检索。如果您可以在SQL server中这样做,我们有一个非常简单的查询,它将在下一个工作日返回。

DECLARE @dt datetime='20150113' 
SELECT DATEADD(dd,CASE WHEN DATEDIFF(dd,0,@dt)%7 > 3 THEN 7-DATEDIFF(dd,0,@dt)%7 ELSE 1 END,@dt)

说明:
根据数据库,基准日期始终为"1月1日1900"
19000101-->星期一-->--所以结果是THEN VALUE=1天
19000102->星期二-->--所以结果是THEN VALUE=1天
19000103-->周三-->--所以结果是THEN VALUE=1天
19000104->星期四-->--所以结果是THEN VALUE=1天
19000105-->星期五-->--所以结果是7-@number=3天
19000106-->周六-->--所以结果是7-@number=2天
19000107->周日-->--所以结果是7-@number=1天