@date/holidays
![Coverage Status](https://coveralls.io/repos/github/elidoran/node-date-holidays/badge.svg?branch=master)
Test if dates are holidays and manage info related to the holidays.
Note, version 0.4.0 changed the API due to a complete rewrite. Use the old API by installing the latest patch version of 0.3.
Install
npm install --save @date/holidays
Usage: Add Simple Holiday
Some holidays are always on the same date so are easy to specify.
const Holidays = require('@date/holidays')
const holidays = Holidays()
const removerFn = holidays.add({
mainInfo: {
name: 'Valentine\'s Day',
public: true,
},
month: 1,
day : 14,
})
removerFn()
Usage: Add Complicated Holiday
Some holidays require calculating when they occur, and, whether they have an "observed" date as well.
Specify the range of dates the holiday can occur on and an is()
function to test whether a Date
is a holiday.
holidays.add({
month: 6,
firstDay: 3,
lastDay: 5,
mainInfo: {
name: 'Independence Day',
public: true,
bank: true,
},
observedInfo: {
name: 'Independence Day (observed)',
public: true,
bank: true,
},
mainInfoWhenObserved: {
name: 'Independence Day',
public: true,
bank: false,
},
is: function isIndependenceDay(date, day, month, year) {
const weekday = date.getDay()
switch(day) {
case 3: return (weekday === 5) ? 2 : 0
case 4: return (1 <= weekday && weekday <= 5) ? 1 : 3
case 5: return (weekday === 1) ? 2 : 0
default: return 0
}
},
})
holidays.add({
dateRange: [
[ 12, 31 ],
[ 0, 1, 2 ],
],
is: (date, day, month, year) => {
const weekday = date.getDay()
switch(day) {
case 31:
return (weekday === 5) ? 2 : 0
case 1:
return (1 <= weekday && weekday <= 5) ? 1 : 3
case 2:
return (weekday === 1) ? 2 : 0
default:
return 0
}
},
})
holidays.add({
mainInfo: {
name: 'Father\'s Day',
},
dateRange: [
[ 5, 15, 21 ]
],
is: function isFathersDay(date, day, month, year) {
return (date.getDay() === 0) ? 1 : 0
},
})
Usage: Helpers
When working with dates @date/generator and @date/business help.
The individual holidays I'm publishing will all have a gen(year)
function which returns a Date
instance for that holiday in the specified year. It may also have an observed
property set on it with the Date
that holiday is "observed" in that year. This is what was used in the previous version of @date/holidays
. The @date/generator
package is helpful for that.
The @date/business
package is helpful for changing a date to the next or previous business day. It accepts a Holidays
instance so it will skip over holiday dates.
API
API: isHoliday()
Returns false unless it is aware of a holiday on the specified date.
Extra criteria can be specified and the holiday's info
will be tested for those values. All specified values must exist in the info
for it to return true.
const date = getSomeDate()
holidays.isHoliday(date)
holidays.isHoliday(date, { some: 'thing' })
holidays.isHoliday(date, { some: 'thing', something: 'else' })
API: getHoliday()
Get an array of holiday "info" for all holidays on a specific date.
If there are no holidays on the date then an empty array is returned.
The "info" is an optional object provided when calling holidays.add()
.
See API: add()
infos = holidays.getHoliday(date)
if (infos.length > 0) {
}
infos = holidays.getHoliday(date, {
some: 'prop',
to: 'filter',
})
API: load() and loadMany()
Load a published holiday package into a Holidays
instance.
May override default options for the holiday by providing an object as the second argument. It may contain any properties used in add(). The default options and the specified options will be combined.
For convenience, loadMany()
accepts an array of arrays. Each sub-array should have the package name as the first element and an optional options override object as the second element.
Look for published holidays in @date organization.
const holidays = require('@date/holidays')()
holidays.load('the-package-name')
holidays.load('some-package-name', {
})
holidays.loadMany([
[ 'package-name' ],
[ 'some-name', { name: 'Changed Holiday Name', cache: true } ]
])
API: add(Object)
Adds a holiday to the holidays
instance.
Add accepts a single object argument containing the options for the holiday. The possible options are:
property | purpose |
---|
mainInfo | An optional object returned from getHoliday() and used when filtering results. May be a function which will then be called each time getHoliday() and isHoliday() is called and the holiday is a match on that date. This allows returning a fresh object each time. This is also the default "info" when either of the other 2 "info" properties is not specified. Defaults to an empty object. If "mainInfoWhenObserved" is specified as well then this one will only be used for a year when there is not an "observed date". |
observedInfo | Same as mainInfo except it's returned when the date is for the "observed date" of the holiday. Defaults to mainInfo if not specified. |
mainInfoWhenObserved | Same as mainInfo except, it's returned when the date is the "main date" of the holiday, but is on a weekend, so the holiday is "observed" on another date. Defaults to mainInfo if not specified. |
month | The month of the holiday. Uses 0-11 as JavaScript Date does. |
day | The day of the month of the holiday. Uses 1-31. Used for a "fixed date" holiday which is always on the same date. |
firstDay | The first day in a range of dates the holiday may be on. Used with lastDay . For holidays which may be on different dates and ones with possible "observed" dates (usually before or after the "main date"). |
lastDay | The last day in a range of dates the holiday may be on. Used with firstDay . |
dateRange | Provide multiple date ranges for a holiday which has a range spanning more than one month or year. For example, New Year's may be observed on December 31st which is both the previous month and previous year. An array of sub-arrays with each sub-array containing 2 elements: the month and the day, or 3 elements: the month, firstDay, and lastDay. |
is | The test function which specifies whether a specific date is: 1. the "main date" without an "observed date" that year; 2. the "observed date"; 3. the "main date" with an "observed date" that year. Its arguments are is(date, day, month, year) . The later args are there for convenience because Holidays has already called the functions on the date to get those values for itself. |
cache | Defaults to false . When set to true results of calling the is() will be cached in an object by year/month/day. Call holidays.purge() to replace all holiday cache objects with new empty objects. |
Read later sections about specific options. Below, let's look at them all together:
holidays.add({
month: 2,
day: 3,
})
holidays.isHoliday(new Date(2000, 2, 3))
result = holidays.getHoliday(new Date(2000, 2, 3))
if (result.length > 0) {
}
holidays.add({
mainInfo: {
name: 'Some Holiday',
some: 'property',
},
month: 2,
day: 3,
})
holidays.isHoliday(new Date(2000, 2, 3), { some: 'property' })
holidays.isHoliday(new Date(2000, 2, 3), {
name: 'Some Holiday',
some: 'property',
})
holidays.isHoliday(new Date(2000, 2, 3), { some: 'thing else' })
holidays.isHoliday(new Date(2000, 2, 3), { another: 'property' })
holidays.add({
mainInfo: {},
observedInfo: {},
mainInfoWhenObserved: {},
dateRange: [
[ 1, 2, 4 ],
],
month: 1,
firstDay: 2,
lastDay: 4,
is: (date, day, month, year) {
const weekday = date.getDay()
switch(day) {
case 2: return (weekday === 5) ? 2 : 0
case 3: return (1 <= weekday && weekday <= 5) ? 1 : 3
case 4: return (weekday === 1) ? 2 : 0
default: return 0
}
},
})
holidays.add({
mainInfo: () => { return { some: 'new object' } },
month: 4,
day: 5,
})
holidays.add({
cache: true,
})
holidays.purge()
API: add() - info trio
The info
is used in two places:
- as the return value of
getHoliday()
- for comparison of extra properties given to
isHoliday()
and getHoliday()
as "filters" (second argument).
All "info" objects are optional.
If mainInfo
isn't provided then it defaults to an empty object.
Both observedInfo
and mainInfoWhenObserved
default to the value of mainInfo
(which defaults to an empty object).
If the holiday is always on the same date then only mainInfo
will be used (I refer to those as "simple fixed date" holidays).
If the holiday may be "observed" on another date, usually one day before or one day after the "main date", then that "observed date" will return the observedInfo
object.
When there is an "observed date" in the year of the Date
provided to getHoliday()
or isHoliday()
then the "main date" is on the weekend and it will return the mainInfoWhenObserved
object.
For example, Valentine's Day is always February 14th and never has an observed date. So, only a mainInfo
is useful.
For example, Independence Day's "main date" is always July 4th. It may be observed on July 3rd or July 5th. When July 4th is a weekday (Monday - Friday) then the mainInfo
will be used for July 4th that year. If July 4th is on the weekend then mainInfoWhenObserved
will be used for July 4th that year. When a year observes the holiday on the 3rd or 5th then the observedInfo
will be used (otherwise, those days are not considered holidays).
How is this helpful? Consider the above example about Independence Day. It makes sense to have the mainInfo
contain bank: true
because it's a bank holiday. However, when the holiday is observed on the 3rd or 5th then the 4th is not a bank holiday. So, that's when mainInfoWhenObserved
is helpful because it can contain bank: false
. The observedInfo
would contain bank: true
because the "observed date" (in years where it occurs) is always a bank holiday.
API: add() - methods to specify dates
Holidays have varied date configurations. So, Holidays
allows specifying them in different ways.
- simple fixed date
Always on the same date without an "observed date". For example, Valentine's Day. Specify one like this:
holidays.add({
month: 1,
day: 14,
})
- fixed date with possible observed dates
The "main date" is always on the same date and it may have an "observed date" one day before, or after, the "main date". For example, Independence Day is always on July 4th but may be observed on July 3rd or July 4th. Specify one like this:
holidays.add({
month: 6,
firstDay: 3,
lastDay: 5,
})
holidays.add({
dateRange: [
[ 6, 3, 5 ],
],
})
holidays.add({
dateRange: [
[ 11, 31 ],
[ 0, 1, 2 ],
],
})
- variable date
The holiday date must be calculated for each year. One example is Father's Day which is always on the third Sunday in June. Another example is Easter which has a complicated calculation algorithm.
holidays.add({
month: 5,
firstDay: 15,
lastDay: 21,
})
holidays.add({
dateRange: [
[ 5, 15, 21 ],
],
})
API: add() - is()
The is()
function returns a value in the range of zero to three for the specified date. The values mean:
value | meaning |
---|
0 | not a match for the holiday. |
1 | is the "main date" of the holiday in a year without an "observed date". |
2 | is the "observed date" of the holiday for that year. |
3 | is the "main date" of the holiday in a year with an "observed date". |
The is()
function is optional for "simple fixed dates". The date info may be specified (see section above) and an is()
function will be created for it.
The arguments are (date, day, month, year)
. The first argument is the Date
object. The later arguments are for convenience so you don't have to call functions on the Date
to get them (the Holidays
already calls them for itself so it provides them to is()
).
For example:
holidays.add({
month: 1,
day: 2,
})
holidays.add({
month: 6,
firstDay: 3,
lastDay: 5,
is: (date, day, month, year) => {
const weekday = date.getDay()
switch(day) {
case 3: return (weekday === 5) ? 2 : 0
case 4: return (1 <= weekday && weekday <= 5) ? 1 : 3
case 5: return (weekday === 1) ? 2 : 0
default: return 0
}
},
})
holidays.add({
dateRange: [
[ 5, 15, 21 ],
],
is: (date, day, month, year) => {
const weekday = date.getDay()
return (weekday === 0) ? 1 : 0
},
})
API: remove a holiday
Removes a holiday from a Holidays
instance.
The Holidays.add()
function returns a "remover function" which, when called, will remove the added holiday.
const someHolidaySpecification = getIt()
const removerFn = holidays.add(someHolidaySpecification)
removerFn()
API: purge()
When cache:true
is specified in a holiday's options it will cache its results to avoid calling its is()
function repeatedly for the same date.
Each caching holiday creates its own cache object.
Calling purge()
on the Holidays
instance will replace all cache objects with new empty objects.
holidays.purge()