In case anyone finds it useful, this is the code I use to automatically detect US holidays. I don't claim credit for it - added bits and pieces over the years from various sources until it was complete enough that I didn't have to worry about it anymore.
public bool isHoliday(DateTime date)
{
return GetHolidays(date.Year).Contains(date.Date);
}
public bool isHalfDay(DateTime date)
{
if (date.Month == 7 && date.Day == 3) return true;
if (date.Month == 12 && date.Day == 24) return true;
//if (date.Date == CalcGoodFri(date.Year).AddDays(-1)) return true;
// Day after Thanksgiving
var thanksgivingFriday = (from day in Enumerable.Range(1, 30)
where new DateTime(date.Year, 11, day).DayOfWeek == DayOfWeek.Friday
select day).ElementAt(3);
DateTime thanksgivingFridayDate = new DateTime(date.Year, 11, thanksgivingFriday);
if (date.Date == thanksgivingFridayDate) return true;
return false;
}
private HashSet<DateTime> GetHolidays(int year)
{
HashSet<DateTime> holidays = new HashSet<DateTime>();
// George W Bush
if (year == 2018) holidays.Add(new DateTime(2018, 12, 5));
// New Years
DateTime newYearsDate = AdjustForWeekendHoliday(new DateTime(year, 1, 1));
holidays.Add(newYearsDate);
// MLK Day -- 3rd Monday in January
var mlk = (from day in Enumerable.Range(1, 30)
where new DateTime(year, 1, day).DayOfWeek == DayOfWeek.Monday
select day).ElementAt(2);
DateTime mlkDay = new DateTime(year, 1, mlk);
holidays.Add(mlkDay);
// President's Day -- 3rd Monday in January
var pres = (from day in Enumerable.Range(1, 30)
where new DateTime(year, 2, day).DayOfWeek == DayOfWeek.Monday
select day).ElementAt(2);
DateTime presDay = new DateTime(year, 2, pres);
holidays.Add(presDay);
// Good Friday
holidays.Add(CalcGoodFri(year));
// Memorial Day -- last monday in May
DateTime memorialDay = new DateTime(year, 5, 31);
DayOfWeek dayOfWeek = memorialDay.DayOfWeek;
while (dayOfWeek != DayOfWeek.Monday)
{
memorialDay = memorialDay.AddDays(-1);
dayOfWeek = memorialDay.DayOfWeek;
}
holidays.Add(memorialDay);
// Independence Day
DateTime independenceDay = AdjustForWeekendHoliday(new DateTime(year, 7, 4));
holidays.Add(independenceDay);
// Labor Day -- 1st Monday in September
DateTime laborDay = new DateTime(year, 9, 1);
dayOfWeek = laborDay.DayOfWeek;
while(dayOfWeek != DayOfWeek.Monday)
{
laborDay = laborDay.AddDays(1);
dayOfWeek = laborDay.DayOfWeek;
}
holidays.Add(laborDay);
// Thanksgiving Day -- 4th Thursday in November
var thanksgiving = (from day in Enumerable.Range(1, 30)
where new DateTime(year, 11, day).DayOfWeek == DayOfWeek.Thursday
select day).ElementAt(3);
DateTime thanksgivingDay = new DateTime(year, 11, thanksgiving);
holidays.Add(thanksgivingDay);
// Christmas Day
DateTime christmasDay = AdjustForWeekendHoliday(new DateTime(year, 12, 25));
holidays.Add(christmasDay);
// Next year's new years check
DateTime nextYearNewYearsDate = AdjustForWeekendHoliday(new DateTime(year + 1, 1, 1));
if (nextYearNewYearsDate.Year == year)
holidays.Add(nextYearNewYearsDate);
return holidays;
}
private DateTime AdjustForWeekendHoliday(DateTime holiday)
{
if (holiday.DayOfWeek == DayOfWeek.Saturday)
{
return holiday.AddDays(-1);
}
else if (holiday.DayOfWeek == DayOfWeek.Sunday)
{
return holiday.AddDays(1);
}
else
{
return holiday;
}
}
// Credits to Mike Cook cited here: https://stackoverflow.com/questions/2510383/how-can-i-calculate-what-date-good-friday-falls-on-given-a-year
private DateTime CalcGoodFri(int yr)
{
int a = yr % 19;
int b = yr / 100;
int c = yr % 100;
int d = b / 4;
int e = b % 4;
int i = c / 4;
int k = c % 4;
int g = (8 * b + 13) / 25;
int h = ((19 * a) + b - d - g + 15) % 30;
int l = ((2 * e) + (2 * i) - k + 32 - h) % 7;
int m = (a + (11 * h) + (19 * l)) / 433;
int days_to_good_friday = h + l - (7 * m) - 2;
int mo = (days_to_good_friday + 90) / 25;
int da = (days_to_good_friday + (33 * mo) + 19) % 32;
return new DateTime(yr, mo, da);
}