/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.basics.date;
import static java.time.DayOfWeek.MONDAY;
import static java.time.DayOfWeek.SATURDAY;
import static java.time.DayOfWeek.SUNDAY;
import static java.time.DayOfWeek.THURSDAY;
import static java.time.DayOfWeek.TUESDAY;
import static java.time.DayOfWeek.WEDNESDAY;
import static java.time.temporal.TemporalAdjusters.dayOfWeekInMonth;
import static java.time.temporal.TemporalAdjusters.firstInMonth;
import static java.time.temporal.TemporalAdjusters.lastInMonth;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
/**
* Implementation of some common global holiday calendars.
* <p>
* The data provided here has been identified through direct research and is not
* derived from a vendor of holiday calendar data.
* This data may or may not be sufficient for your production needs.
*/
final class GlobalHolidayCalendars {
/**
* The holiday calendar for London, United Kingdom, with code 'GBLO'.
* <p>
* This constant provides the calendar for London bank holidays.
* <p>
* The default implementation is based on original research and covers 1950 to 2099.
* Future dates are an extrapolations of the latest known rules.
*/
public static final HolidayCalendar GBLO = generateLondon();
/**
* The holiday calendar for Paris, France, with code 'FRPA'.
* <p>
* This constant provides the calendar for Paris public holidays.
* <p>
* The default implementation is based on original research and covers 1950 to 2099.
* Future and past dates are an extrapolations of the latest known rules.
*/
public static final HolidayCalendar FRPA = generateParis();
/**
* The holiday calendar for Zurich, Switzerland, with code 'EUTA'.
* <p>
* This constant provides the calendar for Zurich public holidays.
* <p>
* The default implementation is based on original research and covers 1950 to 2099.
* Future and past dates are an extrapolations of the latest known rules.
*/
public static final HolidayCalendar CHZU = generateZurich();
/**
* The holiday calendar for the European Union TARGET system, with code 'EUTA'.
* <p>
* This constant provides the calendar for the TARGET interbank payment system holidays.
* <p>
* The default implementation is based on original research and covers 1997 to 2099.
* Future dates are an extrapolations of the latest known rules.
* <p>
* Referenced by the 2006 ISDA definitions 1.8.
*/
public static final HolidayCalendar EUTA = generateEuropeanTarget();
/**
* The holiday calendar for United States Government Securities, with code 'USGS'.
* <p>
* This constant provides the calendar for United States Government Securities as per SIFMA.
* <p>
* The default implementation is based on original research and covers 1950 to 2099.
* Future and past dates are an extrapolations of the latest known rules.
* <p>
* Referenced by the 2006 ISDA definitions 1.11.
*/
public static final HolidayCalendar USGS = generateUsGovtSecurities();
/**
* The holiday calendar for New York, United States, with code 'USNY'.
* <p>
* This constant provides the calendar for New York holidays.
* <p>
* The default implementation is based on original research and covers 1950 to 2099.
* Future and past dates are an extrapolations of the latest known rules.
*/
public static final HolidayCalendar USNY = generateUsNewYork();
/**
* The holiday calendar for the Federal Reserve Bank of New York, with code 'NYFD'.
* <p>
* This constant provides the calendar for the Federal Reserve Bank of New York holidays.
* <p>
* The default implementation is based on original research and covers 1950 to 2099.
* Future and past dates are an extrapolations of the latest known rules.
* <p>
* Referenced by the 2006 ISDA definitions 1.9.
*/
public static final HolidayCalendar NYFD = generateNewYorkFed();
/**
* The holiday calendar for the New York Stock Exchange, with code 'NYSE'.
* <p>
* This constant provides the calendar for the New York Stock Exchange.
* <p>
* The default implementation is based on original research and covers 1950 to 2099.
* Future and past dates are an extrapolations of the latest known rules.
* <p>
* Referenced by the 2006 ISDA definitions 1.10.
*/
public static final HolidayCalendar NYSE = generateNewYorkStockExchange();
/**
* The holiday calendar for Tokyo, Japan, with code 'JPTO'.
* <p>
* This constant provides the calendar for Tokyo bank holidays.
* <p>
* The default implementation is based on original research and covers 1950 to 2099.
* Future and past dates are an extrapolations of the latest known rules.
*/
public static final HolidayCalendar JPTO = generateTokyo();
//-------------------------------------------------------------------------
/**
* Restricted constructor.
*/
private GlobalHolidayCalendars() {
}
//-------------------------------------------------------------------------
// generate GBLO
// common law (including before 1871) good friday and christmas day (unadjusted for weekends)
// from 1871 easter monday, whit monday, first Mon in Aug and boxing day
// from 1965 to 1970, first in Aug moved to Mon after last Sat in Aug
// from 1971, whitsun moved to last Mon in May, last Mon in Aug
// from 1974, added new year
// from 1978, added first Mon in May
// see Hansard for specific details
// 1965, Whitsun, Last Mon Aug - http://hansard.millbanksystems.com/commons/1964/mar/04/staggered-holidays
// 1966, Whitsun May - http://hansard.millbanksystems.com/commons/1964/mar/04/staggered-holidays
// 1966, 29th Aug - http://hansard.millbanksystems.com/written_answers/1965/nov/25/august-bank-holiday
// 1967, 29th May, 28th Aug - http://hansard.millbanksystems.com/written_answers/1965/jun/03/bank-holidays-1967-and-1968
// 1968, 3rd Jun, 2nd Sep - http://hansard.millbanksystems.com/written_answers/1965/jun/03/bank-holidays-1967-and-1968
// 1969, 26th May, 1st Sep - http://hansard.millbanksystems.com/written_answers/1967/mar/21/bank-holidays-1969-dates
// 1970, 25th May, 31st Aug - http://hansard.millbanksystems.com/written_answers/1967/jul/28/bank-holidays
static ImmutableHolidayCalendar generateLondon() {
List<LocalDate> holidays = new ArrayList<>(2000);
for (int year = 1950; year <= 2099; year++) {
// new year
if (year >= 1974) {
holidays.add(bumpToMon(first(year, 1)));
}
// easter
holidays.add(easter(year).minusDays(2));
holidays.add(easter(year).plusDays(1));
// early May
if (year == 1995) {
// ve day
holidays.add(date(1995, 5, 8));
} else if (year >= 1978) {
holidays.add(first(year, 5).with(firstInMonth(MONDAY)));
}
// spring
if (year == 2002) {
// golden jubilee
holidays.add(date(2002, 6, 3));
holidays.add(date(2002, 6, 4));
} else if (year == 2012) {
// diamond jubilee
holidays.add(date(2012, 6, 4));
holidays.add(date(2012, 6, 5));
} else if (year == 1967 || year == 1970) {
holidays.add(first(year, 5).with(lastInMonth(MONDAY)));
} else if (year < 1971) {
// whitsun
holidays.add(easter(year).plusDays(50));
} else {
holidays.add(first(year, 5).with(lastInMonth(MONDAY)));
}
// summer
if (year < 1965) {
holidays.add(first(year, 8).with(firstInMonth(MONDAY)));
} else if (year < 1971) {
holidays.add(first(year, 8).with(lastInMonth(SATURDAY)).plusDays(2));
} else {
holidays.add(first(year, 8).with(lastInMonth(MONDAY)));
}
// christmas
holidays.add(christmas(year));
holidays.add(boxingDay(year));
// royal wedding
if (year == 2011) {
holidays.add(date(2011, 4, 29));
}
// millenium
if (year == 1999) {
holidays.add(date(1999, 12, 31));
}
}
return ImmutableHolidayCalendar.of("GBLO", holidays, SATURDAY, SUNDAY);
}
//-------------------------------------------------------------------------
// generate FRPA
// data sources
// http://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000006902611&cidTexte=LEGITEXT000006072050
// http://jollyday.sourceforge.net/data/fr.html
static ImmutableHolidayCalendar generateParis() {
List<LocalDate> holidays = new ArrayList<>(2000);
for (int year = 1950; year <= 2099; year++) {
holidays.add(date(year, 1, 1)); // new year
holidays.add(easter(year).plusDays(1)); // easter monday
holidays.add(date(year, 5, 1)); // labour day
holidays.add(date(year, 5, 8)); // victory in europe
holidays.add(easter(year).plusDays(39)); // ascension day
if (year <= 2004 || year >= 2008) {
holidays.add(easter(year).plusDays(50)); // whit monday
}
holidays.add(date(year, 7, 14)); // bastille
holidays.add(date(year, 8, 15)); // assumption of mary
holidays.add(date(year, 11, 1)); // all saints
holidays.add(date(year, 11, 11)); // armistice day
holidays.add(date(year, 12, 25)); // christmas day
}
removeSatSun(holidays);
return ImmutableHolidayCalendar.of("FRPA", holidays, SATURDAY, SUNDAY);
}
//-------------------------------------------------------------------------
// generate CHZU
// data sources
// http://jollyday.sourceforge.net/data/ch.html
// https://github.com/lballabio/quantlib/blob/master/QuantLib/ql/time/calendars/switzerland.cpp
// http://www.six-swiss-exchange.com/funds/trading/trading_and_settlement_calendar_en.html
static ImmutableHolidayCalendar generateZurich() {
List<LocalDate> holidays = new ArrayList<>(2000);
for (int year = 1950; year <= 2099; year++) {
holidays.add(date(year, 1, 1)); // new year
holidays.add(date(year, 1, 2)); // saint berchtoldstag
holidays.add(easter(year).minusDays(2)); // good friday
holidays.add(easter(year).plusDays(1)); // easter monday
holidays.add(date(year, 5, 1)); // labour day
holidays.add(easter(year).plusDays(39)); // ascension day
holidays.add(easter(year).plusDays(50)); // whit monday
holidays.add(date(year, 8, 1)); // national day
holidays.add(date(year, 12, 25)); // christmas day
holidays.add(date(year, 12, 26)); // saint stephen
}
removeSatSun(holidays);
return ImmutableHolidayCalendar.of("CHZU", holidays, SATURDAY, SUNDAY);
}
//-------------------------------------------------------------------------
// generate EUTA
// 1997 - 1998 (testing phase), Jan 1, christmas day
// https://www.ecb.europa.eu/pub/pdf/other/tagien.pdf
// in 1999, Jan 1, christmas day, Dec 26, Dec 31
// http://www.ecb.europa.eu/press/pr/date/1999/html/pr990715_1.en.html
// http://www.ecb.europa.eu/press/pr/date/1999/html/pr990331.en.html
// in 2000, Jan 1, good friday, easter monday, May 1, christmas day, Dec 26
// http://www.ecb.europa.eu/press/pr/date/1999/html/pr990715_1.en.html
// in 2001, Jan 1, good friday, easter monday, May 1, christmas day, Dec 26, Dec 31
// http://www.ecb.europa.eu/press/pr/date/2000/html/pr000525_2.en.html
// from 2002, Jan 1, good friday, easter monday, May 1, christmas day, Dec 26
// http://www.ecb.europa.eu/press/pr/date/2000/html/pr001214_4.en.html
static ImmutableHolidayCalendar generateEuropeanTarget() {
List<LocalDate> holidays = new ArrayList<>(2000);
for (int year = 1997; year <= 2099; year++) {
if (year >= 2000) {
holidays.add(date(year, 1, 1));
holidays.add(easter(year).minusDays(2));
holidays.add(easter(year).plusDays(1));
holidays.add(date(year, 5, 1));
holidays.add(date(year, 12, 25));
holidays.add(date(year, 12, 26));
} else { // 1997 to 1999
holidays.add(date(year, 1, 1));
holidays.add(date(year, 12, 25));
}
if (year == 1999 || year == 2001) {
holidays.add(date(year, 12, 31));
}
}
removeSatSun(holidays);
return ImmutableHolidayCalendar.of("EUTA", holidays, SATURDAY, SUNDAY);
}
//-------------------------------------------------------------------------
// common US holidays
private static void usCommon(List<LocalDate> holidays, int year, boolean bumpBack, boolean columbusVeteran) {
// new year, adjusted if Sunday
holidays.add(bumpSunToMon(date(year, 1, 1)));
// martin luther king
if (year >= 1986) {
holidays.add(date(year, 1, 1).with(dayOfWeekInMonth(3, MONDAY)));
}
// washington
if (year < 1971) {
holidays.add(bumpSunToMon(date(year, 2, 22)));
} else {
holidays.add(date(year, 2, 1).with(dayOfWeekInMonth(3, MONDAY)));
}
// memorial
if (year < 1971) {
holidays.add(bumpSunToMon(date(year, 5, 30)));
} else {
holidays.add(date(year, 5, 1).with(lastInMonth(MONDAY)));
}
// labor day
holidays.add(date(year, 9, 1).with(firstInMonth(MONDAY)));
// columbus day
if (columbusVeteran) {
if (year < 1971) {
holidays.add(bumpSunToMon(date(year, 10, 12)));
} else {
holidays.add(date(year, 10, 1).with(dayOfWeekInMonth(2, MONDAY)));
}
}
// veterans day
if (columbusVeteran) {
if (year >= 1971 && year < 1978) {
holidays.add(date(year, 10, 1).with(dayOfWeekInMonth(4, MONDAY)));
} else {
holidays.add(bumpSunToMon(date(year, 11, 11)));
}
}
// thanksgiving
holidays.add(date(year, 11, 1).with(dayOfWeekInMonth(4, THURSDAY)));
// independence day & christmas day
if (bumpBack) {
holidays.add(bumpToFriOrMon(date(year, 7, 4)));
holidays.add(bumpToFriOrMon(date(year, 12, 25)));
} else {
holidays.add(bumpSunToMon(date(year, 7, 4)));
holidays.add(bumpSunToMon(date(year, 12, 25)));
}
}
// generate USGS
// http://www.sifma.org/services/holiday-schedule/
static ImmutableHolidayCalendar generateUsGovtSecurities() {
List<LocalDate> holidays = new ArrayList<>(2000);
for (int year = 1950; year <= 2099; year++) {
usCommon(holidays, year, true, true);
// good friday, in 1999/2007 only a partial holiday
holidays.add(easter(year).minusDays(2));
// hurricane sandy
if (year == 2012) {
holidays.add(date(year, 10, 30));
}
}
removeSatSun(holidays);
return ImmutableHolidayCalendar.of("USGS", holidays, SATURDAY, SUNDAY);
}
//-------------------------------------------------------------------------
// generate USNY
// http://www.cs.ny.gov/attendance_leave/2012_legal_holidays.cfm
// http://www.cs.ny.gov/attendance_leave/2013_legal_holidays.cfm
// etc
// ignore election day and lincoln day
static ImmutableHolidayCalendar generateUsNewYork() {
List<LocalDate> holidays = new ArrayList<>(2000);
for (int year = 1950; year <= 2099; year++) {
usCommon(holidays, year, false, true);
}
removeSatSun(holidays);
return ImmutableHolidayCalendar.of("USNY", holidays, SATURDAY, SUNDAY);
}
//-------------------------------------------------------------------------
// generate NYFD
// http://www.ny.frb.org/aboutthefed/holiday_schedule.html
static ImmutableHolidayCalendar generateNewYorkFed() {
List<LocalDate> holidays = new ArrayList<>(2000);
for (int year = 1950; year <= 2099; year++) {
usCommon(holidays, year, false, true);
}
removeSatSun(holidays);
return ImmutableHolidayCalendar.of("NYFD", holidays, SATURDAY, SUNDAY);
}
//-------------------------------------------------------------------------
// generate NYSE
// https://www.nyse.com/markets/hours-calendars
static ImmutableHolidayCalendar generateNewYorkStockExchange() {
List<LocalDate> holidays = new ArrayList<>(2000);
for (int year = 1950; year <= 2099; year++) {
usCommon(holidays, year, true, false);
// good friday
holidays.add(easter(year).minusDays(2));
}
removeSatSun(holidays);
return ImmutableHolidayCalendar.of("NYSE", holidays, SATURDAY, SUNDAY);
}
//-------------------------------------------------------------------------
// generate JPTO
// data sources
// https://www.boj.or.jp/en/about/outline/holi.htm/
// http://web.archive.org/web/20110513190217/http://www.boj.or.jp/en/about/outline/holi.htm/
// http://web.archive.org/web/20130502031733/http://www.boj.or.jp/en/about/outline/holi.htm
// http://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html (law)
// http://www.nao.ac.jp/faq/a0301.html (equinox)
// http://eco.mtk.nao.ac.jp/koyomi/faq/holiday.html.en
static ImmutableHolidayCalendar generateTokyo() {
List<LocalDate> holidays = new ArrayList<>(2000);
for (int year = 1950; year <= 2099; year++) {
// new year
holidays.add(date(year, 1, 1));
holidays.add(date(year, 1, 2));
holidays.add(date(year, 1, 3));
// coming of age
if (year >= 2000) {
holidays.add(date(year, 1, 1).with(dayOfWeekInMonth(2, MONDAY)));
} else {
holidays.add(bumpSunToMon(date(year, 1, 15)));
}
// national foundation
if (year >= 1967) {
holidays.add(bumpSunToMon(date(year, 2, 11)));
}
// vernal equinox (from 1948), 20th or 21st (predictions/facts 2000 to 2030)
if (year == 2000 || year == 2001 || year == 2004 || year == 2005 || year == 2008 || year == 2009 ||
year == 2012 || year == 2013 || year == 2016 || year == 2017 ||
year == 2020 || year == 2021 || year == 2024 || year == 2025 || year == 2026 || year == 2028 ||
year == 2029 || year == 2030) {
holidays.add(bumpSunToMon(date(year, 3, 20)));
} else {
holidays.add(bumpSunToMon(date(year, 3, 21)));
}
// showa (from 2007 onwards), greenery (from 1989 to 2006), emperor (before 1989)
// http://news.bbc.co.uk/1/hi/world/asia-pacific/4543461.stm
holidays.add(bumpSunToMon(date(year, 4, 29)));
// constitution (from 1948)
// greenery (from 2007 onwards), holiday between two other holidays before that (from 1985)
// children (from 1948)
if (year >= 1985) {
holidays.add(bumpSunToMon(date(year, 5, 3)));
holidays.add(bumpSunToMon(date(year, 5, 4)));
holidays.add(bumpSunToMon(date(year, 5, 5)));
if (year >= 2007 && (date(year, 5, 3).getDayOfWeek() == SUNDAY || date(year, 5, 4).getDayOfWeek() == SUNDAY)) {
holidays.add(date(year, 5, 6));
}
} else {
holidays.add(bumpSunToMon(date(year, 5, 3)));
holidays.add(bumpSunToMon(date(year, 5, 5)));
}
// marine
if (year >= 2003) {
holidays.add(date(year, 7, 1).with(dayOfWeekInMonth(3, MONDAY)));
} else if (year >= 1996) {
holidays.add(bumpSunToMon(date(year, 7, 20)));
}
// mountain
if (year >= 2016) {
holidays.add(bumpSunToMon(date(year, 8, 11)));
}
// aged
if (year >= 2003) {
holidays.add(date(year, 9, 1).with(dayOfWeekInMonth(3, MONDAY)));
} else if (year >= 1966) {
holidays.add(bumpSunToMon(date(year, 9, 15)));
}
// autumn equinox (from 1948), 22nd or 23rd (predictions/facts 2000 to 2030)
if (year == 2012 || year == 2016 || year == 2020 || year == 2024 || year == 2028) {
holidays.add(bumpSunToMon(date(year, 9, 22)));
} else {
holidays.add(bumpSunToMon(date(year, 9, 23)));
}
citizensDay(holidays, date(year, 9, 20), date(year, 9, 22));
citizensDay(holidays, date(year, 9, 21), date(year, 9, 23));
// health-sports
if (year >= 2000) {
holidays.add(date(year, 10, 1).with(dayOfWeekInMonth(2, MONDAY)));
} else if (year >= 1966) {
holidays.add(bumpSunToMon(date(year, 10, 10)));
}
// culture (from 1948)
holidays.add(bumpSunToMon(date(year, 11, 3)));
// labor (from 1948)
holidays.add(bumpSunToMon(date(year, 11, 23)));
// emperor (current emporer)
if (year >= 1990) {
holidays.add(bumpSunToMon(date(year, 12, 23)));
}
// new years eve - bank of Japan, but not national holiday
holidays.add(bumpSunToMon(date(year, 12, 31)));
}
holidays.add(date(1959, 4, 10)); // marriage akihito
holidays.add(date(1989, 2, 24)); // funeral showa
holidays.add(date(1990, 11, 12)); // enthrone akihito
holidays.add(date(1993, 6, 9)); // marriage naruhito
removeSatSun(holidays);
return ImmutableHolidayCalendar.of("JPTO", holidays, SATURDAY, SUNDAY);
}
// extra day between two other holidays, appears to exclude weekends
private static void citizensDay(List<LocalDate> holidays, LocalDate date1, LocalDate date2) {
if (holidays.contains(date1) && holidays.contains(date2)) {
if (date1.getDayOfWeek() == MONDAY || date1.getDayOfWeek() == TUESDAY || date1.getDayOfWeek() == WEDNESDAY) {
holidays.add(date1.plusDays(1));
}
}
}
//-------------------------------------------------------------------------
// date
private static LocalDate date(int year, int month, int day) {
return LocalDate.of(year, month, day);
}
// bump to following Monday
private static LocalDate bumpToMon(LocalDate date) {
if (date.getDayOfWeek() == SATURDAY) {
return date.plusDays(2);
} else if (date.getDayOfWeek() == SUNDAY) {
return date.plusDays(1);
}
return date;
}
// bump Sunday to following Monday
private static LocalDate bumpSunToMon(LocalDate date) {
if (date.getDayOfWeek() == SUNDAY) {
return date.plusDays(1);
}
return date;
}
// bump to Saturday to Friday and Sunday to Monday
private static LocalDate bumpToFriOrMon(LocalDate date) {
if (date.getDayOfWeek() == SATURDAY) {
return date.minusDays(1);
} else if (date.getDayOfWeek() == SUNDAY) {
return date.plusDays(1);
}
return date;
}
// christmas
private static LocalDate christmas(int year) {
LocalDate base = LocalDate.of(year, 12, 25);
if (base.getDayOfWeek() == SATURDAY || base.getDayOfWeek() == SUNDAY) {
return LocalDate.of(year, 12, 27);
}
return base;
}
// boxing day
private static LocalDate boxingDay(int year) {
LocalDate base = LocalDate.of(year, 12, 26);
if (base.getDayOfWeek() == SATURDAY || base.getDayOfWeek() == SUNDAY) {
return LocalDate.of(year, 12, 28);
}
return base;
}
// first of a month
private static LocalDate first(int year, int month) {
return LocalDate.of(year, month, 1);
}
// remove any holidays covered by Sat/Sun
private static void removeSatSun(List<LocalDate> holidays) {
holidays.removeIf(date -> date.getDayOfWeek() == SATURDAY || date.getDayOfWeek() == SUNDAY);
}
// calculate easter day by Delambre
static LocalDate easter(int year) {
int a = year % 19;
int b = year / 100;
int c = year % 100;
int d = b / 4;
int e = b % 4;
int f = (b + 8) / 25;
int g = (b - f + 1) / 3;
int h = (19 * a + b - d - g + 15) % 30;
int i = c / 4;
int k = c % 4;
int l = (32 + 2 * e + 2 * i - h - k) % 7;
int m = (a + 11 * h + 22 * l) / 451;
int month = (h + l - 7 * m + 114) / 31;
int day = ((h + l - 7 * m + 114) % 31) + 1;
return LocalDate.of(year, month, day);
}
}