@18f/us-federal-holidays
Version:
All about US federal holidays
317 lines (282 loc) • 10.3 kB
JavaScript
const customParseFormat = require("dayjs/plugin/customParseFormat");
const dayjs = require("dayjs");
const tap = require("tap");
// The customParseFormat plugin changes the way the dayjs() utility method
// handles format strings. Specifically, MM and DD format tokens will REQUIRE
// two-digit months and days, whereas the default constructor will happily take
// single-digit months and days. Add the plugin to our tests to make sure we
// still work in environments where the plugin is being used.
dayjs.extend(customParseFormat);
const federalHolidays = require("./index");
const getDate = dateString => new Date(`${dateString} 00:00:00`);
const getDateUTC = dateString => new Date(`${dateString}T00:00:00Z`);
tap.test("handles standard federal holidays", async tests => {
tests.test(
"gets observed holidays, accounting for actual holidays on weekends",
async test => {
[
"2010-12-31", // New Year's Day falls on a Saturday, observed before
"2012-01-02", // New Year's Day falls on a Sunday, observed after
"2014-01-01",
"2014-01-20",
"2014-02-17",
"2014-05-26",
"2014-07-04",
"2014-09-01",
"2014-10-13",
"2014-11-11",
"2014-11-27",
"2014-12-25",
"2015-07-03", // Independence Day falls on a Saturday, observed before
"2016-12-26", // Christmas Day falls on a Sunday, observed after
"2017-12-25",
"2021-05-31" // https://github.com/18F/us-federal-holidays/issues/28
].forEach(dateString => {
const date = getDate(dateString);
const utcDate = getDateUTC(dateString);
test.ok(
federalHolidays.isAHoliday(date),
`${dateString} is a holiday (observed)`
);
test.ok(
federalHolidays.isAHoliday(utcDate, { utc: true }),
`${dateString} UTC is a holiday (observed)`
);
});
}
);
tests.test(
"actual holidays on weekends are not listed as observed holidays",
async test => {
[
"2011-01-01", // New Year's Day falls on a Saturday, so is not a holiday
"2012-01-01", // New Year's Day falls on a Sunday, so is not a holiday
"2015-07-04", // Independence Day falls on a Saturday, so is not a holiday
"2016-12-25", // Christmas Day falls on a Sunday, so is not a holiday
"2021-05-24" // https://github.com/18F/us-federal-holidays/issues/28
].forEach(dateString => {
const date = getDate(dateString);
const utcDate = getDateUTC(dateString);
test.notOk(
federalHolidays.isAHoliday(date),
`${dateString} is not a holiday (observed)`
);
test.notOk(
federalHolidays.isAHoliday(utcDate, { utc: true }),
`${dateString} UTC is not a holiday (observed)`
);
});
}
);
tests.test("Juneteenth is only included from 2021 onwards", async test => {
test.notOk(
federalHolidays.isAHoliday(getDate("2020-06-19")),
"Juneteenth is not a holiday in 2020"
);
// In 2021, Juneteenth fell on a Saturday, so the observed holiday was the
// 18th instead of the 19th.
test.ok(
federalHolidays.isAHoliday(getDate("2021-06-18")),
"Juneteenth is a holiday in 2021"
);
// 2023 is the first year that the observation of Juneteenth falls on the
// actual holiday.
test.ok(
federalHolidays.isAHoliday(getDate("2023-06-19")),
"Juneteenth is a holiday in 2023"
);
test.notOk(
federalHolidays
.allForYear(2020)
.some(({ name }) => name === "Juneteenth National Independence Day"),
"Juneteenth is not included in the list of holidays for 2020"
);
test.ok(
federalHolidays
.allForYear(2021)
.some(({ name }) => name === "Juneteenth National Independence Day"),
"Juneteenth is included in the list of holidays for 2021"
);
});
tests.test(
"honors requests not to shift holidays on weekends",
async test => {
test.ok(
federalHolidays.isAHoliday(getDate("2011-01-01"), {
shiftSaturdayHolidays: false
}),
`2011-01-01 is a holiday if Sundays are not shifted`
);
test.ok(
federalHolidays.isAHoliday(getDate("2012-01-01"), {
shiftSundayHolidays: false
}),
`2012-01-01 is a holiday if Sundays are not shifted`
);
test.ok(
federalHolidays.isAHoliday(getDate("2015-07-04"), {
shiftSaturdayHolidays: false
}),
`2015-07-04 is a holiday if Saturdays are not shifted`
);
test.ok(
federalHolidays.isAHoliday(getDate("2016-12-25"), {
shiftSundayHolidays: false
}),
`2016-12-25 is a holiday if Saturdays are not shifted`
);
}
);
tests.test(
"handles federal holidays within a range (Saturday and Sunday holidays shifted)",
async test => {
const holidays = federalHolidays.inRange(
new Date("2015-07-03"),
new Date("2016-12-26")
);
holidays.forEach(holiday => {
test.ok(
federalHolidays.isAHoliday(getDate(holiday.dateString)),
`${holiday.dateString} is a holiday (observed)`
);
// Make sure the alsoObservedAs property gets pulled through
if (
holiday.name === "Washington's Birthday" ||
holiday.name === "Columbus Day"
) {
test.ok(holiday.alsoObservedAs);
}
});
}
);
tests.test(
"handles federal holidays within a range (Saturday shifted only)",
async test => {
const shiftSaturdayHolidays = true;
const shiftSundayHolidays = false;
const holidays = federalHolidays.inRange(
new Date("2015-07-03"),
new Date("2016-12-26"),
{ shiftSaturdayHolidays, shiftSundayHolidays }
);
holidays.forEach(holiday => {
test.ok(
federalHolidays.isAHoliday(getDate(holiday.dateString), {
shiftSaturdayHolidays,
shiftSundayHolidays
}),
`${holiday.dateString} is a holiday (observed)`
);
});
}
);
tests.test(
"handles federal holidays within a range (Sunday shifted only)",
async test => {
const shiftSaturdayHolidays = false;
const shiftSundayHolidays = true;
const holidays = federalHolidays.inRange(
new Date("2015-07-03"),
new Date("2016-12-26"),
{ shiftSaturdayHolidays, shiftSundayHolidays }
);
holidays.forEach(holiday => {
test.ok(
federalHolidays.isAHoliday(getDate(holiday.dateString), {
shiftSaturdayHolidays,
shiftSundayHolidays
}),
`${holiday.dateString} is a holiday (observed)`
);
});
}
);
tests.test(
"handles federal holidays within a range (none shifted)",
async test => {
const shiftSaturdayHolidays = false;
const shiftSundayHolidays = false;
const holidays = federalHolidays.inRange(
new Date("2015-07-03"),
new Date("2016-12-26"),
{ shiftSaturdayHolidays, shiftSundayHolidays }
);
holidays.forEach(holiday => {
test.ok(
federalHolidays.isAHoliday(getDate(holiday.dateString), {
shiftSaturdayHolidays,
shiftSundayHolidays
}),
`${holiday.dateString} is a holiday (observed)`
);
});
}
);
tests.test("handles default dates and ranges", async defaultTests => {
const testYear = 2000;
let GlobalDate;
// Create a proxy for the global Date object. This way we can control the
// date that is created for default arguments.
defaultTests.beforeEach(() => {
GlobalDate = global.Date;
const ProxiedDate = new Proxy(Date, {
construct: (_, args) => {
// We only want to override the constructor if there aren't any
// arguments. In that case, use our magic date of July 1. Otherwise,
// pass the arguments on to the real Date constructor.
if (args.length === 0) {
return new GlobalDate(`${testYear}-07-01T00:00:00.000Z`);
}
return new GlobalDate(...args);
}
});
global.Date = ProxiedDate;
});
defaultTests.afterEach(() => {
// Put the real Date object back.
global.Date = GlobalDate;
});
// We've already tested isAHoliday and allForYear with args, so let's assume
// they're correct. If not, our earlier tests should have caught that. If
// they didn't... uhoh.
defaultTests.test("indicates whether today is a holiday", async test => {
// July 1 should not be a holiday in any year.
test.same(federalHolidays.isAHoliday(), false, "is not a holiday");
});
defaultTests.test(
"fetches all holidays for the current year",
async test => {
const expected = federalHolidays.allForYear(testYear);
const holidays = federalHolidays.allForYear();
test.match(holidays, expected, "gets the expected holidays");
test.same(
holidays.length,
expected.length,
"gets exactly the expected holidays"
);
}
);
defaultTests.test(
"defaults to a range from now to one year from now",
async test => {
const holidays = federalHolidays.inRange();
// Safe-ify these tests against changing test years. In 2021, Juneteenth
// was added to the holiday calendar. If the test year is before 2020,
// there are 4 holidays preceding July 1, so we start our slice at
// holiday #5 (index 4). From 2021 onwards, there are 5 holidays before
// July 1, so we start our slice at holiday #6 (index 5).
const slice = testYear > 2020 ? 5 : 4;
const expected = [
...federalHolidays.allForYear(testYear).slice(slice),
...federalHolidays.allForYear(testYear + 1).slice(0, slice)
];
test.match(holidays, expected, "get the expected holidays");
test.same(
holidays.length,
expected.length,
"gets exactly the expected holidays"
);
}
);
});
});