https://github.com/itsallcode/holiday-calculator
A Java library for calculating dates of public holidays
https://github.com/itsallcode/holiday-calculator
easter-sunday holiday-calculation holidays public-holidays
Last synced: 6 months ago
JSON representation
A Java library for calculating dates of public holidays
- Host: GitHub
- URL: https://github.com/itsallcode/holiday-calculator
- Owner: itsallcode
- License: gpl-3.0
- Created: 2021-05-27T15:18:55.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2024-07-07T05:54:10.000Z (12 months ago)
- Last Synced: 2024-07-07T19:15:26.416Z (12 months ago)
- Topics: easter-sunday, holiday-calculation, holidays, public-holidays
- Language: Java
- Homepage:
- Size: 444 KB
- Stars: 9
- Watchers: 1
- Forks: 3
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# holiday-calculator
Calculate holidays from flexible formulas in a configuration file.[](https://github.com/itsallcode/holiday-calculator/actions?query=workflow%3ABuild)
[](https://sonarcloud.io/dashboard?id=org.itsallcode%3Aholiday-calculator)
[](https://sonarcloud.io/dashboard?id=org.itsallcode%3Aholiday-calculator)
[](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.itsallcode%22%20a%3A%22holiday-calculator%22)Each holiday is meant to repeat every year and is defined by a formula in
order to compute a concrete holiday instance for a given year.
Holiday-calculator supports the following formula flavors:- a fixed date identical for every year
- a floating date defined by a pivot date, specified as month and day and an
offset restricted to a particular day of the week, e.g. fourth Sunday before
Christmas, i.e. December 24th
- a date defined relatively to Easter Sunday with a positive or negative offset of days
- a fixed date with a condition to make the holiday effective
- a fixed date with an alternative date as replacement under the given condition## Changelog
See [CHANGELOG.md](CHANGELOG.md).
## Usage
Include holiday-calculator as a dependency into your project:
### Gradle
```groovy
repositories {
mavenCentral()
}
dependencies {
compile 'org.itsallcode:holiday-calculator:0.2.1'
}
```### Maven
```xml
org.itsallcode
holiday-calculator
0.2.1```
### Calculating holidays
In order to calculate holidays you must create a holiday definition.
Holiday-calculator supports six different flavors of holiday definitions.
For each flavor there is a dedicated class in package `org.itsallcode.holidays.calculator.logic`:1. `FixedDateHoliday` defines a holiday with a fixed date.
2. `FloatingHoliday` defines a holiday with a floating date.
3. `EasterBasedHoliday` defines an Easter-based holiday.
4. `OrthodoxEasterBasedHoliday` defines an Orthodox-Easter-based holiday
5. `ConditionalHoliday` defines a conditional holiday, effective under the given condition
6. `HolidayWithAlternative` defines a holiday with an alternative date, effective under the given conditionSection [Configuration file](README.md#flavors) describes the details and parameters for each flavor.
#### Instantiating subclasses of Holiday
The following code sample instantiates one holiday for each of these flavors:
```java
import org.itsallcode.holidays.calculator.logic.variants.FixedDateHoliday;
import org.itsallcode.holidays.calculator.logic.variants.FloatingHoliday;
import org.itsallcode.holidays.calculator.logic.variants.EasterBasedHoliday;
import org.itsallcode.holidays.calculator.logic.variants.OrthodoxEasterBasedHoliday;
import org.itsallcode.holidays.calculator.logic.variants.ConditionalHoliday;
import org.itsallcode.holidays.calculator.logic.variants.HolidayWithAlternative;
import org.itsallcode.holidays.calculator.logic.conditions.DayOfWeekCondition;class MyClass {
public MyClass() {
Holiday h1 = new FixedDateHoliday("holiday", "Christmas Eve", MonthDay.of(12, 24));Holiday h2 = new FloatingHoliday(
"holiday", "Father's Day", 3, DayOfWeek.SUNDAY, Direction.AFTER, MonthDay.of(6, 1));Holiday h3 = new EasterBasedHoliday("holiday", "Good Friday", -2);
Holiday h4 = new OrthodoxEasterBasedHoliday(
"holiday", "Orthodox Good Friday", -2);ConditionBuilder dec25SatSun = new ConditionBuilder()
.withDaysOfWeek(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY)
.withPivotDate(MonthDay.of(12, 25));
Holiday h5_default = new FixedDateHoliday("holiday", "Bank Holiday 1", MonthDay.of(12, 27));
Holiday h5 = new ConditionalHoliday(dec25SatSun, h5_default);
Holiday h6_default = new FixedDateHoliday("holiday", "Koningsdag", MonthDay.of(4, 27));
ConditionBuilder isSunday = new ConditionBuilder().withDaysOfWeek(DayOfWeek.SUNDAY);
Holiday h6 = new HolidayWithAlternative(defaultKoningsdag, isSunday, MonthDay.of(4, 26));Holiday h7 = new FloatingHoliday("holiday", "Midsommarafton",
1, DayOfWeek.SATURDAY, Direction.BEFORE, MonthDay.of(6, 26))
.withOffsetInDays(-1);
}
}
```- Holiday `h5` is a conditional holiday with a negated condition, see [below](README.md#conditional-holidays).
- Holiday `h6` is a holiday with an alternative date, see [below](README.md#alternative-holidays).
- Holiday `h7` is a floating holiday with an additional offset in days, see [below](README.md#floating-holidays).#### Parsing a configuration File
Besides creating instances of the subclasses of `Holiday` you can also use a configuration file to define your personal selection of holidays. Class `HolidaysFileParser` then parses the file and returns a list of holidays:
```java
import org.itsallcode.holidays.calculator.logic.parser.HolidaysFileParserclass MyClass {
public MyClass() {
List holidays = new HolidaysFileParser.parse("/path/to/holidays.cfg")
}
}
```For the seven example holidays instantiated above, the content of the file could look like this:
```
# my holidaysholiday fixed 12 24 Christmas Eve
holiday float 3 SUN after 6 1 Father's Day
holiday easter -2 Good Friday
holiday orthodox-easter -2 Orthodox Good Friday
holiday if DEC 25 is Sat,Sun then fixed DEC 27 Bank Holiday
holiday either fixed 4 27 or if SUN then fixed 4 26 Koningsdag
holiday float 1 day before 1 Sat before JUN 26 Midsommarafton
```Section [Configuration file](README.md#flavors) describes the syntax in detail.
#### Evaluating a specific holiday for a specific year
In order to evaluate a holiday for the current or any other year and hence get an instance of this
holiday with a concrete date, you can just call method `Holiday.of()`, supplying the year as argument:```java
Holiday goodFriday = new EasterBasedHoliday("holiday", "Good Friday", -2);
LocalDate gf_2021 = goodFriday.of(2021); // 2021 April 4th
```### Configuration file
User can set up his or her individual personal list of favorite holidays using the supported formula flavors.
Holiday-calculator already provides configuration files for a growing list of countries in folder [holidays](./holidays/).#### Content of configuration file
The configuration file is organized in lines. Each line can contain one of 7 types of content:
1. Empty
2. Definition of a holiday with a fixed date
3. Definition of a holiday with a floating date
4. Definition of an Easter-based holiday
5. Definition of an Orthodox-Easter-based holiday
6. Definition of a conditional holiday
7. Definition of a holiday with an alternative dateAll other lines are rated as illegal and ignored by holiday-calculator, logging an error message.
Whitespace is allowed in most places without changing the nature of the
line. Hence, a line containing nothing but tabs and spaces is still rated to
be an empty line.##### Comments
Each line can contain an optional comment starting with hash mark `#`.
Holiday-calculator will ignore the rest of the line after and including the
hash mark.##### Holiday definitions
The definition of any holiday starts with a *category*. The category is an arbitrary
string of non-whitespace characters. The application evaluating your holidays
might support different categories of holidays, e.g. birthdays, anniversaries,
etc. and may display them in different colors. As a default we propose to use
category "holiday".The category is followed by a *tag* identifying the flavor of the holiday
definition and additional arguments depending on the flavor. The last argument
is always a string containing the name of the holiday.General rules
- All strings except the name of the holiday are case-insensitive.
- In all definitions including a month the month must be specified by
- either using its English name January, February, March, April, May, June, July, August, September, October, November, December
- or a unique abbreviation of the name
- or the number of a month
with 1 for January and 12 for December.
- Day of month is an integer from 1 to 31.
- Day of week is
- either one of the English names Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
- or a unique abbreviation of itIn the following cases holiday-calculator will log an error message and ignore
the holiday definition:
- if the tag does not match any of the three supported tags
"fixed", "float", "easter"
- if the holiday definition contains illegal numbers, such as month 0 or 13,
day 32, or day 30 for February
- if the day of week does not match the abbreviation of any of the English names
- if the day of week is abbreviated ambiguously, e.g. "T" or "S"
- if the name of a month does not match the abbreviation of any of the English names
- if the name of a month is abbreviated ambiguously, e.g. "Ma" or "Ju"##### Fixed date holiday definition
A fixed date holiday definition has the tag "fixed", followed by the month and day of month.
Syntax: `holiday fixed `
Samples:
- `holiday fixed 1 1 New Year`
- `holiday fixed Dec 12 Christmas Eve`##### Floating holiday definition
A floating holiday definition has the tag "float", followed by the offset, the
day of week, the direction "before" or "after", the month and day
of month. Month and day of month specify a *pivot date*.If the day of week of the pivot date is identical to the specified one then
the offset starts to count on the pivot date, otherwise on the next instance
of the specified weekday before or after the pivot date.Instead of a number the day of month can also be the special string "last-day".
Syntax: `holiday float `
Samples:
- `holiday float 1 W after 1 1 First Wednesday on or after New Year`
- `holiday float 2 MON before DEC last-day Second Monday before New Year's eve, December the 31st`
- `holiday float 4 SUNDAY before 12 24 First Advent`##### Floating holiday with additional offset in days
Optionally a floating holiday definition can contain an additional offset in days.
This is especially required for Swedish holiday "Midsommarafton", which is 1 day before midsummer day,
which in turn is the first Saturday on or before June, 26th.Syntax: `holiday float day[s] `
Samples:
- `holiday float 1 day before 1 Sat before JUN 26 Midsommarafton`##### Easter-based holiday definition
An Easter-based holiday definition has the tag "easter", followed by the
offset. The offset is the number of days from Easter Sunday. If offset is
negative then the holiday is before Easter Sunday, otherwise after.Syntax: `holiday easter `
Samples:
- `holiday easter 0 Easter Sunday`
- `holiday easter -2 Good Friday`
- `holiday easter +49 Pentecost Sunday`##### Orthodox-Easter-based holiday definition
An Orthodox-Easter-based holiday definition has the tag "orthodox-easter", followed by the
offset. The offset is the number of days from Easter Sunday. If offset is
negative then the holiday is before Orthodox-Easter Sunday, otherwise after.Syntax: `holiday orthodox-easter `
Samples:
- `holiday orthodox-easter 0 Orthodox Easter Sunday`
- `holiday orthodox-easter -2 Orthodox Good Friday`
- `holiday orthodox-easter +49 Orthodox Pentecost Monday`Some countries have a few holidays that do not occur every year but only if a specific condition is met.
Examples are the bank holidays in the UK.
Holiday-calculator calls such holidays *conditional holidays*.A conditional holiday definition has the tag "if", followed by
- pivot month
- pivot day of month
- "is"
- "not" (optional)
- comma-separated list of pivot days of week, e.g. "Wed,Fri", not containing any whitespace.
- "then"
- "fixed"
- month
- day of month
- nameThe conditional holiday is only effective, if the pivot-date of the conditions falls on one of the specified days of week.
If the definition contains the optional "not", then the pivot day must *not fall* on one of the specified days of week.Syntax: `holiday if is [not] then fixed `
Samples:
- `holiday if DEC 25 is Sat,Sun then fixed DEC 27 Bank Holiday`
- `holiday if DEC 25 is not Fri,Sat then fixed DEC 26 Boxing day is extra day off`##### Alternative date Holidays
Some countries have holidays that are moved to another date in case a specific condition is met.
An example is the "Koningsdag" in the Netherlands which occurs on April, 27th but is moved to April, 26 in case April, 27th is a Sunday.A holiday with an alternative has the tag "either", followed by
- month
- day
- "or"
- "if"
- "not" (optional)
- comma-separated list of pivot days of week, e.g. "Wed,Fri", not containing any whitespace.
- "then"
- "fixed"
- alternative month
- alternative day of month
- nameSyntax: `holiday either of if [not] then fixed `
Samples:
- `holiday either 4 27 or if SUN then fixed 4 26 Koningsdag`
- `holiday either 4 27 or if not Mon,Tue,We,Thu,Fri,Sat then fixed 4 26 Koningsdag`## Development
### Generate / update license header
```sh
./gradlew licenseFormat
```### Check if dependencies are up-to-date
```sh
./gradlew dependencyUpdates
```### Building
Install to local maven repository:
```sh
./gradlew clean publishToMavenLocal
```### Publish to Maven Central
#### Preparations
1. Checkout the `main` branch, create a new branch.
2. Update version number in `build.gradle` and `README.md`.
3. Add changes in new version to `CHANGELOG.md`.
4. Commit and push changes.
5. Create a new pull request, have it reviewed and merged to `main`.#### Perform the Release
1. Start the release workflow
* Run command `gh workflow run release.yml --repo itsallcode/holiday-calculator --ref main`
* or go to [GitHub Actions](https://github.com/itsallcode/holiday-calculator/actions/workflows/release.yml) and start the `release.yml` workflow on branch `main`.
2. Update title and description of the newly created [GitHub release](https://github.com/itsallcode/holiday-calculator/releases).
3. After some time the release will be available at [Maven Central](https://repo1.maven.org/maven2/org/itsallcode/holiday-calculator/).