/** * Copyright 2010 Sven Diedrichsen * <p> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package de.jollyday.util; import de.jollyday.Holiday; import de.jollyday.HolidayType; import de.jollyday.config.Fixed; import org.threeten.extra.chrono.CopticChronology; import org.threeten.extra.chrono.JulianChronology; import java.time.LocalDate; import java.time.chrono.ChronoLocalDate; import java.time.chrono.Chronology; import java.time.chrono.HijrahChronology; import java.time.temporal.ChronoField; import java.util.Calendar; import java.util.HashSet; import java.util.Set; import static java.time.DayOfWeek.SATURDAY; import static java.time.DayOfWeek.SUNDAY; import static java.time.Month.*; /** * Utility class for date operations. * * @author Sven Diedrichsen * @version $Id: $ */ public class CalendarUtil { private final XMLUtil xmlUtil = new XMLUtil(); /** * Creates the current date within the gregorian calendar. * * @return today */ public LocalDate create() { return LocalDate.now(); } /** * Creates the date within the ISO chronology. * * @param year a int. * @param month a int. * @param day a int. * @return date */ public LocalDate create(int year, int month, int day) { return LocalDate.of(year, month, day); } /** * Creates the date within the provided chronology. * * @param year * a int. * @param month * a int. * @param day * a int. * @param chronology * the chronology to use * @return date the {@link LocalDate} */ public ChronoLocalDate create(int year, int month, int day, Chronology chronology) { return chronology.date(year, month, day); } /** * Creates the date from the month/day within the specified year. * * @param year * a int. * @param fixed * a {@link de.jollyday.config.Fixed} object. * @return A local date instance. */ public LocalDate create(int year, Fixed fixed) { return create(year, xmlUtil.getMonth(fixed.getMonth()), fixed.getDay()); } /** * Creates a LocalDate. Does not use the Chronology of the Calendar. * * @param c * a {@link java.util.Calendar} object. * @return The local date representing the provided date. */ public LocalDate create(final Calendar c) { //TODO: javadoc needs updating return LocalDate.of(c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c .get(Calendar.DAY_OF_MONTH)); } /** * Returns the easter sunday for a given year. * * @param year * a int. * @return Easter sunday. */ public LocalDate getEasterSunday(int year) { if (year <= 1583) { return getJulianEasterSunday(year); } else { return getGregorianEasterSunday(year); } } /** * Returns the easter sunday within the julian chronology. * * @param year * a int. * @return julian easter sunday */ public LocalDate getJulianEasterSunday(int year) { int a, b, c, d, e; int x, month, day; a = year % 4; b = year % 7; c = year % 19; d = (19 * c + 15) % 30; e = (2 * a + 4 * b - d + 34) % 7; x = d + e + 114; month = x / 31; day = (x % 31) + 1; return LocalDate.from(JulianChronology.INSTANCE.date(year, (month == 3 ? 3 : 4), day)); } /** * Returns the easter sunday within the gregorian chronology. * * @param year * a int. * @return gregorian easter sunday. */ public LocalDate getGregorianEasterSunday(int year) { int a, b, c, d, e, f, g, h, i, j, k, l; int x, month, day; 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; j = c % 4; k = (32 + 2 * e + 2 * i - h - j) % 7; l = (a + 11 * h + 22 * k) / 451; x = h + k - 7 * l + 114; month = x / 31; day = (x % 31) + 1; return LocalDate.of(year, (month == 3 ? MARCH : APRIL), day); } /** * Returns if this date is on a wekkend. * * @param date * a {@link LocalDate} object. * @return is weekend */ public boolean isWeekend(final LocalDate date) { return date.getDayOfWeek() == SATURDAY || date.getDayOfWeek() == SUNDAY; } /** * Returns a set of gregorian dates within a gregorian year which equal the * islamic month and day. Because the islamic year is about 11 days shorter * than the gregorian there may be more than one occurrence of an islamic * date in an gregorian year. i.e.: In the gregorian year 2008 there were * two 1/1. They occurred on 1/10 and 12/29. * * @param gregorianYear * a int. * @param islamicMonth * a int. * @param islamicDay * a int. * @return List of gregorian dates for the islamic month/day. */ public Set<LocalDate> getIslamicHolidaysInGregorianYear(int gregorianYear, int islamicMonth, int islamicDay) { return getDatesFromChronologyWithinGregorianYear(islamicMonth, islamicDay, gregorianYear, HijrahChronology.INSTANCE); } /** * Returns a set of gregorian dates within a gregorian year which equal the * ethiopian orthodox month and day. Because the ethiopian orthodox year * different from the gregorian there may be more than one occurrence of an * ethiopian orthodox date in an gregorian year. * * @param gregorianYear * a int. * @return List of gregorian dates for the ethiopian orthodox month/day. * @param eoMonth * a int. * @param eoDay * a int. */ public Set<LocalDate> getEthiopianOrthodoxHolidaysInGregorianYear(int gregorianYear, int eoMonth, int eoDay) { return getDatesFromChronologyWithinGregorianYear(eoMonth, eoDay, gregorianYear, CopticChronology.INSTANCE); } /** * Searches for the occurrences of a month/day in one chronology within one * gregorian year. * * @param targetMonth * @param targetDay * @param gregorianYear * @param targetChrono * @return the list of gregorian dates. */ private Set<LocalDate> getDatesFromChronologyWithinGregorianYear(int targetMonth, int targetDay, int gregorianYear, Chronology targetChrono) { Set<LocalDate> holidays = new HashSet<>(); LocalDate firstGregorianDate = LocalDate.of(gregorianYear, JANUARY, 1); LocalDate lastGregorianDate = LocalDate.of(gregorianYear, DECEMBER, 31); ChronoLocalDate firstTargetDate = targetChrono.date(firstGregorianDate); ChronoLocalDate lastTargetDate = targetChrono.date(lastGregorianDate); int targetYear = firstTargetDate.get(ChronoField.YEAR); final int lastYear = lastTargetDate.get(ChronoField.YEAR); for (; targetYear <= lastYear; ) { ChronoLocalDate d = targetChrono.date(targetYear, targetMonth, targetDay); if (!firstGregorianDate.isAfter(d) && !lastGregorianDate.isBefore(d)) { holidays.add(LocalDate.from(d)); } targetYear++; } return holidays; } /** * Shows if the requested date is contained in the Set of holidays. * * @param holidays a {@link java.util.Set} object. * @param date a {@link LocalDate} object. * @param holidayType a {@link HolidayType} object * @return contains this date */ public boolean contains(final Set<Holiday> holidays, final LocalDate date, final HolidayType holidayType) { return holidays.stream().anyMatch(h -> h.getDate().equals(date) && (holidayType == null || h.getType() == holidayType)); } /** * Calls #contains(holidays, date, null) * @param holidays the holidays to search through * @param date the date to look for * @return the date is contained in the set of holidays */ public boolean contains(final Set<Holiday> holidays, final LocalDate date) { return contains(holidays, date, null); } }