A Java library for defining and calculating holiday calendars. Provides an extensible foundation for generating the calendars used to determine when holidays occur and when they are observed worldwide.
Upgrading from v1.4.0 or earlier? v2.0.0 introduced the
EarlyCloseHolidayAPI, and v2.1.0 separates national holiday calendars from equities-exchange market calendars. Skip v2.0.0 and upgrade straight to v2.1.0 or later — see MIGRATION.md for the full breaking-changes guide.
Porting to another language? See docs/PORTING_GUIDE.md for the core abstractions, design patterns, and data formats needed to build a compatible implementation in JavaScript, Python, Go, or any other language.
Holiday Calendar answers common needs in financial, scheduling, and business applications: "Is this date a business day?" and "When is this holiday observed this year?"
Key design goals:
- Correct date rolling — when a holiday falls on a weekend, the library applies configurable rolling rules (e.g. following Monday, previous Friday) to compute the observed date.
- Extensible by design — new regional calendars are added via Java's
ServiceLoadermechanism; no changes to core code required. - Modern Java — built on Java 21, using sealed interfaces, records, and the module system (JPMS).
Each region may publish up to three distinct calendars: a National calendar (public holidays
only, no early closes — with one exception, noted below), a Central Bank/Settlement calendar
(currency/RTGS system holidays), and a Market/Exchange calendar (a specific exchange's trading
holidays, generally including half-day early closes). Not every country has all three — see the
tables below. factory.create("CODE") accepts any code from any column.
Exception:
TR(Turkey's national calendar) is the one national code that includes an early-close entry (Republic Day Eve), because it is a statutory closure rather than a market-only convention. The same entry is also carried byTRY.
| Country/Region | National | Central Bank/Settlement | Market/Exchange (MIC) | Early closes? |
|---|---|---|---|---|
| Australia | AU |
AUD RBA |
XASX ASX |
✅ (XASX) |
| Canada | CA |
CAD Bank of Canada (Lynx) |
XTSE TSX |
✅ (XTSE) |
| France | FR |
— | XPAR Euronext Paris |
✅ (XPAR) |
| Germany | DE |
— | XETR Xetra |
— |
| Switzerland | CH |
CHF SIC/SNB |
XSWX SIX |
— |
| United Kingdom | UK |
GBP CHAPS |
XLON LSE |
✅ (XLON) |
| United States | US |
USD Federal Reserve |
XNYS NYSE |
✅ (XNYS) |
| Eurozone (multi-country) | — | EUR TARGET2 |
— | — |
Germany and France have no dedicated currency-code row (no separate settlement system) — the
shared Eurozone/EUR row above serves that role instead of being duplicated per country.
| Country/Region | National | Central Bank/Settlement | Market/Exchange (MIC) | Early closes? |
|---|---|---|---|---|
| China | CN |
CNY PBOC |
— | — |
| Japan | JP |
JPY BOJ |
— | — |
| Singapore | SG |
SGD MAS/MEPS+ |
XSES SGX |
✅ (XSES) |
Japan has no dedicated exchange (MIC) calendar — JP is documented as national-only.
No Market/Exchange (MIC) calendars exist yet in this module — each country has only a National and
a Currency/Exchange code, with the currency code doubling as the de facto exchange calendar where
applicable (e.g. ILS for TASE, TRY for BIST).
| Country | National | Currency/Exchange | Early closes? |
|---|---|---|---|
| UAE | AE |
AED CBUAE/DFM/ADX |
— |
| Saudi Arabia | SA |
SAR Tadawul/SAMA |
— |
| Israel | IL |
ILS TASE/Bank of Israel |
✅ (ILS only, 6 entries) |
| Turkey | TR |
TRY BIST/TCMB |
✅ (both TR and TRY — shared Republic Day Eve entry) |
| Qatar | QA |
QAR QSE/QCB |
— |
| Egypt | EG |
EGP EGX/CBE |
— |
| Kuwait | KW |
KWD Boursa Kuwait/CBK |
— |
| Bahrain | BH |
BHD Boursa Bahrain/CBB |
— |
| Morocco | MA |
MAD CSE/BAM |
— |
| Jordan | JO |
JOD ASE/CBJ |
— |
For information on adding new calendars or maintaining existing ones, see the Contributing Guide. For per-exchange early-close times, time zones, weekend-roll behavior, and primary-source citations, see the calendar reference docs.
Holiday Calendar requires Java 21 and Maven 3.5.0 or higher.
The library is published to the GitHub Packages Maven registry. Add the repository and dependency to your pom.xml:
<repositories>
<repository>
<id>github</id>
<name>GitHub holiday-calendar Apache Maven Packages</name>
<url>https://maven.pkg.github.com/holiday-calendar/holiday-calendar-java</url>
</repository>
</repositories>Then add the modules you need:
<!-- Core API (required) -->
<dependency>
<groupId>org.holiday.calendar</groupId>
<artifactId>holiday-calendar-core</artifactId>
<version>2.2.0-SNAPSHOT</version>
</dependency>
<!-- Western calendars: US, USD, CA, CAD, UK, GBP, CH, CHF, DE, EUR, FR, AU, AUD -->
<dependency>
<groupId>org.holiday.calendar</groupId>
<artifactId>holiday-calendar-western</artifactId>
<version>2.2.0-SNAPSHOT</version>
</dependency>
<!-- APAC calendars: SG, SGD, JP, JPY, CN, CNY -->
<dependency>
<groupId>org.holiday.calendar</groupId>
<artifactId>holiday-calendar-apac</artifactId>
<version>2.2.0-SNAPSHOT</version>
</dependency>
<!-- MENA calendars: AE, AED, BH, BHD, EG, EGP, IL, ILS, JO, JOD, KW, KWD, MA, MAD, QA, QAR, SA, SAR, TR, TRY -->
<dependency>
<groupId>org.holiday.calendar</groupId>
<artifactId>holiday-calendar-mena</artifactId>
<version>2.2.0-SNAPSHOT</version>
</dependency>import org.holiday.calendar.HolidayCalendar;
import org.holiday.calendar.HolidayCalendarFactory;
import org.holiday.calendar.HolidayDate;
HolidayCalendarFactory factory = new HolidayCalendarFactory();
// Get the US holiday calendar
HolidayCalendar usCalendar = factory.create("US");
// Calculate observed holiday dates for 2025
List<HolidayDate> holidays = usCalendar.calculate(2025);
holidays.forEach(hd ->
System.out.printf("%s %s%n", hd.getDate(), hd.getHoliday().getName())
);boolean isWeekend = usCalendar.isWeekendUTC(Instant.now());HolidayCalendar ukCalendar = factory.create("UK");
HolidayCalendar combined = usCalendar.merge(ukCalendar);
List<HolidayDate> combined2025 = combined.calculate(2025);List<String> codes = factory.listAvailableCodes();
// ["AE", "AED", "AU", "AUD", "BH", "BHD", "CA", "CAD", "CH", "CHF", "CN", "CNY", "DE", "EUR", "FR", "GBP", "IL", "ILS", "JO", "JOD", "JP", "JPY", "KW", "KWD", "MA", "MAD", "QA", "QAR", "SA", "SAR", "SG", "SGD", "TR", "TRY", "UK", "US", "USD"]EarlyCloseHoliday entries represent partial trading days — e.g. NYSE's 1:00pm ET
close the day after Thanksgiving. They are excluded from calculate() and must be
retrieved separately via calculateEarlyCloses(int). Use an exchange MIC code such
as XNYS — national codes no longer carry early closes (with the sole exception of
TR; see Supported Calendars).
import org.holiday.calendar.EarlyCloseHoliday;
HolidayCalendar xnysCalendar = factory.create("XNYS");
for (HolidayDate hd : xnysCalendar.calculateEarlyCloses(2026)) {
EarlyCloseHoliday earlyClose = (EarlyCloseHoliday) hd.getHoliday();
System.out.printf("%s %s closes at %s %s%n",
hd.getDate(), earlyClose.getName(), earlyClose.getCloseTime(), earlyClose.getZoneId());
}hasEarlyCloses() is a cheap, year-independent check for whether a calendar has any
early-close entries at all, useful for branching without computing a specific year.
if (xnysCalendar.hasEarlyCloses()) {
System.out.println("This calendar publishes early-close trading days.");
}To get a single chronologically-sorted view of both full closures and early closes for
a year, merge the two result lists and re-sort — HolidayDate is not Comparable, so
sort with an explicit Comparator:
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
List<HolidayDate> allDates2026 = Stream.concat(
xnysCalendar.calculate(2026).stream(),
xnysCalendar.calculateEarlyCloses(2026).stream())
.sorted(Comparator.comparing(HolidayDate::getDate))
.toList();Implement HolidayCalendarService, register it via ServiceLoader, and it will be discovered automatically by HolidayCalendarFactory.
public class HolidayCalendarServiceJP implements HolidayCalendarService {
@Override
public boolean isProvided(String code) {
return "JP".equalsIgnoreCase(code);
}
@Override
public String getCode() { return "JP"; }
@Override
public String getRegion() { return "Japan National Holidays"; }
@Override
public HolidayCalendar getHolidayCalendar() {
return HolidayCalendar.builder()
.code("JP")
.name("Japan National Holidays")
.dateRoll(DateRolls.followingMonday())
.holiday(Holiday.builder()
.name("New Year's Day")
.monthDay(Month.JANUARY, 1)
.build())
// ... additional holidays
.build();
}
}Register the service in META-INF/services/org.holiday.calendar.HolidayCalendarService and add a provides directive to module-info.java.
Contributions are welcome! Please read the Contributing Guide before opening an issue or pull request. This project adheres to a Code of Conduct — all participants are expected to uphold it.
Holiday Calendar is released under the GNU Lesser General Public License, version 2.1.