如何到达下一个工作日,不包括周末和假日

CSh*_*ped 16 .net c# algorithm datetime date

我有一个要求,我需要在日期字段上工作,所以要求是这样的事情

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

  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
}
Run Code Online (Sandbox Code Playgroud)

fja*_*don 25

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

do {
  date = date.AddDays(1);
} while(IsHolliday(date) || IsWeekEnd(date));
Run Code Online (Sandbox Code Playgroud)

在前面的代码中IsHolliday是一个谓词,告诉日期是否为holliday.例如,无耻地重用您的代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果 GetNextWorkingDay() 返回与工作日相同的日期会稍微好一点 - 即删除通过的日期必须已经被计算为工作日的假设。可能在没有“do”的情况下用while循环来做 (2认同)

Tin*_*ger 7

根据@fjardon的回答,您可以使用我的项目Nager.Date它包含不同国家的Weekend逻辑,并且包含90多个国家的公共假期。

裸体

PM> install-package Nager.Date
Run Code Online (Sandbox Code Playgroud)

程式码片段

//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));
Run Code Online (Sandbox Code Playgroud)


Mat*_*int 5

固定的日期列表是表达假期的有限方式.

考虑到您的列表仅包含当前年份的日期,因此如果今天是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

通过这些定义,我们现在可以更加健壮地定义假期,例如:

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));
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

那个方法可以用在抽象Holiday类上,也可以去任何地方.

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

var weekendDays = new[] { DayOfWeek.Saturday, DayOfWeek.Sunday };

DateTime workDay = GetNextNonHolidayWeekDay(new DateTime(2015, 12, 31), holidays, weekendDays);
// returns 2016-01-04
Run Code Online (Sandbox Code Playgroud)

尽管如此,这仍然不是一个完整的解决方案.许多假期都有更复杂的计算规则.作为练习留给读者,尝试实施一个源自Holiday美国感恩节假期第二天的课程.第一天总是在11月的第四个星期四,但第二天总是"十一月的第四个星期四之后的星期五",而不仅仅是"十一月的第四个星期五"(参见2019年11月的一个例子事项).