using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
void assert(int expected, DateTime first, DateTime second)
=> Assert.AreEqual(expected, WeekdaysBetweenTwoDates(first, second));
void assertWithHolidays(int expected, DateTime first, DateTime second)
=> Assert.AreEqual(expected, BusinessDaysBetweenTwoDates(first, second, new List<DateTime> {
new DateTime(2013, 12, 25),
new DateTime(2013, 12, 26),
new DateTime(2014, 1, 1)}));
HolidayRule anzacDay = new AnnualSameDateHolidayRule(month: 4, day: 25);
HolidayRule newYearsDay = new AnnualSameDayPostponedIfOnWeekendHolidayRule(month: 1, day: 1);
HolidayRule queensBirthday = new AnnualSameNthDayOfWeekInMonthHolidayRule(DayOfWeek.Monday, occurence: 2, month: 6);
void assertWithHolidayRules(int expected, DateTime first, DateTime second)
=> Assert.AreEqual(expected, BusinessDaysBetweenTwoDates(first, second, new List<DateTime>(), new List<HolidayRule> { anzacDay, newYearsDay, queensBirthday }));
assert(0, new DateTime(2013, 10, 7), new DateTime(2013, 10, 8));
assert(1, new DateTime(2013, 10, 7), new DateTime(2013, 10, 9));
assert(5, new DateTime(2013, 10, 5), new DateTime(2013, 10, 14));
assert(61, new DateTime(2013, 10, 7), new DateTime(2014, 1, 1));
assert(0, new DateTime(2013, 10, 7), new DateTime(2013, 10, 5));
assertWithHolidays(1, new DateTime(2013, 10, 7), new DateTime(2013, 10, 9));
assertWithHolidays(0, new DateTime(2013, 12, 24), new DateTime(2013, 12, 27));
assertWithHolidays(59, new DateTime(2013, 10, 7), new DateTime(2014, 1, 1));
assertWithHolidayRules(3, new DateTime(2019, 4, 23), new DateTime(2019, 4, 30));
assertWithHolidayRules(0, new DateTime(2017, 1, 1), new DateTime(2017, 1, 3));
assertWithHolidayRules(1, new DateTime(2019, 6, 9), new DateTime(2019, 6, 12));
Assert.AreEqual(1287,
BusinessDaysBetweenTwoDates(
new DateTime(2010, 1, 1), new DateTime(2015, 1, 1),
new List<DateTime> { new DateTime(2012, 4, 4), new DateTime(2014, 2, 13), new DateTime(2011, 3, 2)},
new List<HolidayRule> { anzacDay, newYearsDay, queensBirthday }));
}
private static int WeekdaysBetweenTwoDates(DateTime firstDate, DateTime secondDate)
{
return BusinessDaysBetweenTwoDates(firstDate, secondDate, new List<DateTime>());
}
private static int BusinessDaysBetweenTwoDates(DateTime firstDate, DateTime secondDate, IList<DateTime> publicHolidays)
{
return BusinessDaysBetweenTwoDates(firstDate, secondDate, publicHolidays, new List<HolidayRule>());
}
private static int BusinessDaysBetweenTwoDates(DateTime firstDate, DateTime secondDate, IList<DateTime> publicHolidays, IList<HolidayRule> publicHolidayRules)
{
return firstDate >= secondDate ? 0 :
Enumerable.Range(1, (secondDate.AddDays(-1) - firstDate).Days)
.Select(offset => firstDate.AddDays(offset))
.Count(day =>
day.DayOfWeek != DayOfWeek.Saturday
&& day.DayOfWeek != DayOfWeek.Sunday
&& !publicHolidays.Contains(day)
&& !publicHolidayRules.Any(rule => rule.IsHoliday(day)));
}
protected abstract class HolidayRule
{
internal abstract bool IsHoliday(DateTime datetime);
}
private class AnnualSameDateHolidayRule : HolidayRule
{
protected readonly int \_month;
protected readonly int \_day;
internal AnnualSameDateHolidayRule(int month, int day)
{
_month = month;
_day = day;
}
internal override bool IsHoliday(DateTime dateTime) => dateTime.Month == \_month && [dateTime.Day](https://dateTime.Day) == \_day;
}
private class AnnualSameDayPostponedIfOnWeekendHolidayRule : AnnualSameDateHolidayRule
{
internal AnnualSameDayPostponedIfOnWeekendHolidayRule(int month, int day) : base(month, day) { }
internal override bool IsHoliday(DateTime dateTime) {
var holiday = new DateTime(dateTime.Year, _month, _day);
if (holiday.DayOfWeek == DayOfWeek.Saturday) return holiday.AddDays(2) == dateTime;
if (holiday.DayOfWeek == DayOfWeek.Sunday) return holiday.AddDays(1) == dateTime;
return holiday == dateTime;
}
}
private class AnnualSameNthDayOfWeekInMonthHolidayRule : HolidayRule
{
protected readonly DayOfWeek \_dayOfWeek;
protected readonly int \_occurrence;
protected readonly int \_month;
internal AnnualSameNthDayOfWeekInMonthHolidayRule(DayOfWeek dayOfWeek, int occurence, int month)
{
_dayOfWeek = dayOfWeek;
_occurrence = occurence;
_month = month;
}
internal override bool IsHoliday(DateTime dateTime)
{
if (dateTime.Month != _month || dateTime.DayOfWeek != _dayOfWeek) return false;
return Enumerable.Range(1, dateTime.Day)
.Select(dayOfMonth => new DateTime(dateTime.Year, dateTime.Month, dayOfMonth))
.Count(day => day.DayOfWeek == _dayOfWeek) == _occurrence;
}
}
}
}