2015-10-27 175 views
10

我有,我需要一个日期字段工作的要求,所以要求是这样的如何获得下一个工作日,不包括周末和节假日

一些事情,我会打电话给外地的最小可能日期

  1. 添加+1的日期

  2. 如果最小可能发生的日期加1天之后落在一个周末(周六或周日),显示即下一个工作日周一

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

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

我试图通过具有多如果和其他案件,只是不知道是否有这样做的任何通用的和优雅的方式对上述问题的解决方案?

我已经试过

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 
} 

回答

17

基本上你想要得到下一个工作日。所以,你可以在此条件下增加1天到当前日期

do { 
    date = date.AddDays(1); 
} while(IsHolliday(date) || IsWeekEnd(date)); 

在上面的代码IsHolliday环是谓词讲,如果日期是周末。例如,无耻地重用代码:

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(); 
    } 
} 
+0

+1并通过使'IsHoliday'可以避开节假日建设的静态列表和动态的,而不是计算功能日期/年份通过了 - 这对复活节这样的日期很重要。 –

+0

什么是“IsHolliday(日期)”?这种方法存在吗? – Fabjan

+0

@Fabjan你必须自己实现这个方法。例如,您可以插入OP编写的用于检查日期是否为hollidays的代码。 – fjardon

0
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))); 
+1

您应该从'targetDate'开始'datesAhead'而不是当前日期。那么不需要'a> targetDate'。 – juharr

+0

你说得对。我甚至不需要100天。 –

+0

好吧,因为它只会迭代直到获得一个匹配,传递给'Enumerable.Range'的计数只需要超过下一个工作日可能的最大天数。我的猜测是5或6,但将它放大不会产生负面影响。 – juharr

4

日期的固定列表是为了表达假期比较有限的方式。

考虑到你的列表只包含当年的日期,所以如果今天是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月的例子)事项)。

+0

模拟假期的方式似乎非常合适。这是一个很好的答案。 –

1

根据@fjardon的回答,您可以使用Nager.Date它包含不同国家的周末逻辑,包含70多个国家的公共假期。

的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)); 
相关问题