package name.abuchen.portfolio.util; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.Month; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class TradeCalendar { private interface Holiday { boolean isOn(LocalDate date); } private static class Weekend implements Holiday { @Override public boolean isOn(LocalDate date) { DayOfWeek dayOfWeek = date.getDayOfWeek(); if (dayOfWeek == DayOfWeek.SATURDAY) return true; if (dayOfWeek == DayOfWeek.SUNDAY) return true; return false; } } private static class FixedHoliday implements Holiday { private final Month month; private final int dayOfMonth; public FixedHoliday(Month month, int dayOfMonth) { this.month = month; this.dayOfMonth = dayOfMonth; } @Override public boolean isOn(LocalDate date) { return date.getMonth() == month && date.getDayOfMonth() == dayOfMonth; } } private static class EasterHoliday implements Holiday { private Map<Integer, LocalDate> year2eastern = new HashMap<>(); @Override public boolean isOn(LocalDate date) { LocalDate easterSunday = year2eastern.computeIfAbsent(date.getYear(), year -> calculateEasterSunday(year)); if (easterSunday.plusDays(1).equals(date)) return true; if (easterSunday.plusDays(-2).equals(date)) return true; return false; } private LocalDate calculateEasterSunday(int nYear) { // @formatter:off // Taken from // http://www.java2s.com/Code/Java/Data-Type/CalculateHolidays.htm // and published under the // GNU General Public License version 2 /* Calculate Easter Sunday Written by Gregory N. Mirsky Source: 2nd Edition by Peter Duffett-Smith. It was originally from Butcher's Ecclesiastical Calendar, published in 1876. This algorithm has also been published in the 1922 book General Astronomy by Spencer Jones; in The Journal of the British Astronomical Association (Vol.88, page 91, December 1977); and in Astronomical Algorithms (1991) by Jean Meeus. This algorithm holds for any year in the Gregorian Calendar, which (of course) means years including and after 1583. a=year%19 b=year/100 c=year%100 d=b/4 e=b%4 f=(b+8)/25 g=(b-f+1)/3 h=(19*a+b-d-g+15)%30 i=c/4 k=c%4 l=(32+2*e+2*i-h-k)%7 m=(a+11*h+22*l)/451 Easter Month =(h+l-7*m+114)/31 [3=March, 4=April] p=(h+l-7*m+114)%31 Easter Date=p+1 (date in Easter Month) Note: Integer truncation is already factored into the calculations. Using higher percision variables will cause inaccurate calculations. */ // @formatter:on int nA = 0; int nB = 0; int nC = 0; int nD = 0; int nE = 0; int nF = 0; int nG = 0; int nH = 0; int nI = 0; int nK = 0; int nL = 0; int nM = 0; int nP = 0; int nEasterMonth = 0; int nEasterDay = 0; nA = nYear % 19; nB = nYear / 100; nC = nYear % 100; nD = nB / 4; nE = nB % 4; nF = (nB + 8) / 25; nG = (nB - nF + 1) / 3; nH = (19 * nA + nB - nD - nG + 15) % 30; nI = nC / 4; nK = nC % 4; nL = (32 + 2 * nE + 2 * nI - nH - nK) % 7; nM = (nA + 11 * nH + 22 * nL) / 451; // [3=March, 4=April] nEasterMonth = (nH + nL - 7 * nM + 114) / 31; nP = (nH + nL - 7 * nM + 114) % 31; // Date in Easter Month. nEasterDay = nP + 1; // Populate the date object... return LocalDate.of(nYear, nEasterMonth, nEasterDay); } } private static final List<Holiday> HOLIDAYS = Arrays.asList(new Weekend(), // new FixedHoliday(Month.JANUARY, 1), // new FixedHoliday(Month.MAY, 1), // new FixedHoliday(Month.DECEMBER, 24), // new FixedHoliday(Month.DECEMBER, 25), // new FixedHoliday(Month.DECEMBER, 26), // new EasterHoliday()); public boolean isHoliday(LocalDate date) { for (Holiday holiday : HOLIDAYS) { if (holiday.isOn(date)) return true; } return false; } }