blob: 5f14e20970841dbcffc11a4f75a3d91b171647c6 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 1996-2006, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
import java.util.Date;
/**
* A Holiday subclass which represents holidays that occur
* a fixed number of days before or after Easter. Supports both the
* Western and Orthodox methods for calculating Easter.
* @draft ICU 2.8 (retainAll)
* @provisional This API might change or be removed in a future release.
*/
public class EasterHoliday extends Holiday
{
/**
* Construct a holiday that falls on Easter Sunday every year
*
* @param name The name of the holiday
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
public EasterHoliday(String name)
{
super(name, new EasterRule(0, false));
}
/**
* Construct a holiday that falls a specified number of days before
* or after Easter Sunday each year.
*
* @param daysAfter The number of days before (-) or after (+) Easter
* @param name The name of the holiday
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
public EasterHoliday(int daysAfter, String name)
{
super(name, new EasterRule(daysAfter, false));
}
/**
* Construct a holiday that falls a specified number of days before
* or after Easter Sunday each year, using either the Western
* or Orthodox calendar.
*
* @param daysAfter The number of days before (-) or after (+) Easter
* @param orthodox Use the Orthodox calendar?
* @param name The name of the holiday
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
public EasterHoliday(int daysAfter, boolean orthodox, String name)
{
super(name, new EasterRule(daysAfter, orthodox));
}
/**
* Shrove Tuesday, aka Mardi Gras, 48 days before Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday SHROVE_TUESDAY = new EasterHoliday(-48, "Shrove Tuesday");
/**
* Ash Wednesday, start of Lent, 47 days before Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday ASH_WEDNESDAY = new EasterHoliday(-47, "Ash Wednesday");
/**
* Palm Sunday, 7 days before Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday PALM_SUNDAY = new EasterHoliday( -7, "Palm Sunday");
/**
* Maundy Thursday, 3 days before Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday MAUNDY_THURSDAY = new EasterHoliday( -3, "Maundy Thursday");
/**
* Good Friday, 2 days before Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday GOOD_FRIDAY = new EasterHoliday( -2, "Good Friday");
/**
* Easter Sunday
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday EASTER_SUNDAY = new EasterHoliday( 0, "Easter Sunday");
/**
* Easter Monday, 1 day after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday EASTER_MONDAY = new EasterHoliday( 1, "Easter Monday");
/**
* Ascension, 39 days after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday ASCENSION = new EasterHoliday( 39, "Ascension");
/**
* Pentecost (aka Whit Sunday), 49 days after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday PENTECOST = new EasterHoliday( 49, "Pentecost");
/**
* Whit Sunday (aka Pentecost), 49 days after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday WHIT_SUNDAY = new EasterHoliday( 49, "Whit Sunday");
/**
* Whit Monday, 50 days after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday WHIT_MONDAY = new EasterHoliday( 50, "Whit Monday");
/**
* Corpus Christi, 60 days after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday CORPUS_CHRISTI = new EasterHoliday( 60, "Corpus Christi");
}
class EasterRule implements DateRule {
public EasterRule(int daysAfterEaster, boolean isOrthodox) {
this.daysAfterEaster = daysAfterEaster;
if (isOrthodox) {
orthodox.setGregorianChange(new Date(Long.MAX_VALUE));
calendar = orthodox;
}
}
/**
* Return the first occurrance of this rule on or after the given date
*/
public Date firstAfter(Date start)
{
return doFirstBetween(start, null);
}
/**
* Return the first occurrance of this rule on or after
* the given start date and before the given end date.
*/
public Date firstBetween(Date start, Date end)
{
return doFirstBetween(start, end);
}
/**
* Return true if the given Date is on the same day as Easter
*/
public boolean isOn(Date date)
{
synchronized(calendar) {
calendar.setTime(date);
int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
calendar.setTime(computeInYear(calendar.getTime(), calendar));
return calendar.get(Calendar.DAY_OF_YEAR) == dayOfYear;
}
}
/**
* Return true if Easter occurs between the two dates given
*/
public boolean isBetween(Date start, Date end)
{
return firstBetween(start, end) != null; // TODO: optimize?
}
private Date doFirstBetween(Date start, Date end)
{
//System.out.println("doFirstBetween: start = " + start.toString());
//System.out.println("doFirstBetween: end = " + end.toString());
synchronized(calendar) {
// Figure out when this holiday lands in the given year
Date result = computeInYear(start, calendar);
//System.out.println(" result = " + result.toString());
// We might have gotten a date that's in the same year as "start", but
// earlier in the year. If so, go to next year
if (result.before(start))
{
calendar.setTime(start);
calendar.get(Calendar.YEAR); // JDK 1.1.2 bug workaround
calendar.add(Calendar.YEAR, 1);
//System.out.println(" Result before start, going to next year: "
// + calendar.getTime().toString());
result = computeInYear(calendar.getTime(), calendar);
//System.out.println(" result = " + result.toString());
}
if (end != null && result.after(end)) {
//System.out.println("Result after end, returning null");
return null;
}
return result;
}
}
/**
* Compute the month and date on which this holiday falls in the year
* containing the date "date". First figure out which date Easter
* lands on in this year, and then add the offset for this holiday to get
* the right date.
* <p>
* The algorithm here is taken from the
* <a href="http://www.faqs.org/faqs/calendars/faq/">Calendar FAQ</a>.
*/
private Date computeInYear(Date date, GregorianCalendar cal)
{
if (cal == null) cal = calendar;
synchronized(cal) {
cal.setTime(date);
int year = cal.get(Calendar.YEAR);
int g = year % 19; // "Golden Number" of year - 1
int i = 0; // # of days from 3/21 to the Paschal full moon
int j = 0; // Weekday (0-based) of Paschal full moon
if (cal.getTime().after( cal.getGregorianChange()))
{
// We're past the Gregorian switchover, so use the Gregorian rules.
int c = year / 100;
int h = (c - c/4 - (8*c+13)/25 + 19*g + 15) % 30;
i = h - (h/28)*(1 - (h/28)*(29/(h+1))*((21-g)/11));
j = (year + year/4 + i + 2 - c + c/4) % 7;
}
else
{
// Use the old Julian rules.
i = (19*g + 15) % 30;
j = (year + year/4 + i) % 7;
}
int l = i - j;
int m = 3 + (l+40)/44; // 1-based month in which Easter falls
int d = l + 28 - 31*(m/4); // Date of Easter within that month
cal.clear();
cal.set(Calendar.ERA, GregorianCalendar.AD);
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, m-1); // 0-based
cal.set(Calendar.DATE, d);
cal.getTime(); // JDK 1.1.2 bug workaround
cal.add(Calendar.DATE, daysAfterEaster);
return cal.getTime();
}
}
private static GregorianCalendar gregorian = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */);
private static GregorianCalendar orthodox = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */);
private int daysAfterEaster;
private GregorianCalendar calendar = gregorian;
}