日期的固定列表是为了表达假期比较有限的方式。
考虑到你的列表只包含当年的日期,所以如果今天是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月的例子)事项)。
+1并通过使'IsHoliday'可以避开节假日建设的静态列表和动态的,而不是计算功能日期/年份通过了 - 这对复活节这样的日期很重要。 –
什么是“IsHolliday(日期)”?这种方法存在吗? – Fabjan
@Fabjan你必须自己实现这个方法。例如,您可以插入OP编写的用于检查日期是否为hollidays的代码。 – fjardon