2 *******************************************************************************
\r
3 * Copyright (C) 1996-2006, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
8 package com.ibm.icu.util;
\r
10 import java.util.Date;
\r
13 * A Holiday subclass which represents holidays that occur
\r
14 * a fixed number of days before or after Easter. Supports both the
\r
15 * Western and Orthodox methods for calculating Easter.
\r
16 * @draft ICU 2.8 (retainAll)
\r
17 * @provisional This API might change or be removed in a future release.
\r
19 public class EasterHoliday extends Holiday
\r
22 * Construct a holiday that falls on Easter Sunday every year
\r
24 * @param name The name of the holiday
\r
26 * @provisional This API might change or be removed in a future release.
\r
28 public EasterHoliday(String name)
\r
30 super(name, new EasterRule(0, false));
\r
34 * Construct a holiday that falls a specified number of days before
\r
35 * or after Easter Sunday each year.
\r
37 * @param daysAfter The number of days before (-) or after (+) Easter
\r
38 * @param name The name of the holiday
\r
40 * @provisional This API might change or be removed in a future release.
\r
42 public EasterHoliday(int daysAfter, String name)
\r
44 super(name, new EasterRule(daysAfter, false));
\r
48 * Construct a holiday that falls a specified number of days before
\r
49 * or after Easter Sunday each year, using either the Western
\r
50 * or Orthodox calendar.
\r
52 * @param daysAfter The number of days before (-) or after (+) Easter
\r
53 * @param orthodox Use the Orthodox calendar?
\r
54 * @param name The name of the holiday
\r
56 * @provisional This API might change or be removed in a future release.
\r
58 public EasterHoliday(int daysAfter, boolean orthodox, String name)
\r
60 super(name, new EasterRule(daysAfter, orthodox));
\r
64 * Shrove Tuesday, aka Mardi Gras, 48 days before Easter
\r
66 * @provisional This API might change or be removed in a future release.
\r
68 static public final EasterHoliday SHROVE_TUESDAY = new EasterHoliday(-48, "Shrove Tuesday");
\r
71 * Ash Wednesday, start of Lent, 47 days before Easter
\r
73 * @provisional This API might change or be removed in a future release.
\r
75 static public final EasterHoliday ASH_WEDNESDAY = new EasterHoliday(-47, "Ash Wednesday");
\r
78 * Palm Sunday, 7 days before Easter
\r
80 * @provisional This API might change or be removed in a future release.
\r
82 static public final EasterHoliday PALM_SUNDAY = new EasterHoliday( -7, "Palm Sunday");
\r
85 * Maundy Thursday, 3 days before Easter
\r
87 * @provisional This API might change or be removed in a future release.
\r
89 static public final EasterHoliday MAUNDY_THURSDAY = new EasterHoliday( -3, "Maundy Thursday");
\r
92 * Good Friday, 2 days before Easter
\r
94 * @provisional This API might change or be removed in a future release.
\r
96 static public final EasterHoliday GOOD_FRIDAY = new EasterHoliday( -2, "Good Friday");
\r
101 * @provisional This API might change or be removed in a future release.
\r
103 static public final EasterHoliday EASTER_SUNDAY = new EasterHoliday( 0, "Easter Sunday");
\r
106 * Easter Monday, 1 day after Easter
\r
108 * @provisional This API might change or be removed in a future release.
\r
110 static public final EasterHoliday EASTER_MONDAY = new EasterHoliday( 1, "Easter Monday");
\r
113 * Ascension, 39 days after Easter
\r
115 * @provisional This API might change or be removed in a future release.
\r
117 static public final EasterHoliday ASCENSION = new EasterHoliday( 39, "Ascension");
\r
120 * Pentecost (aka Whit Sunday), 49 days after Easter
\r
122 * @provisional This API might change or be removed in a future release.
\r
124 static public final EasterHoliday PENTECOST = new EasterHoliday( 49, "Pentecost");
\r
127 * Whit Sunday (aka Pentecost), 49 days after Easter
\r
129 * @provisional This API might change or be removed in a future release.
\r
131 static public final EasterHoliday WHIT_SUNDAY = new EasterHoliday( 49, "Whit Sunday");
\r
134 * Whit Monday, 50 days after Easter
\r
136 * @provisional This API might change or be removed in a future release.
\r
138 static public final EasterHoliday WHIT_MONDAY = new EasterHoliday( 50, "Whit Monday");
\r
141 * Corpus Christi, 60 days after Easter
\r
143 * @provisional This API might change or be removed in a future release.
\r
145 static public final EasterHoliday CORPUS_CHRISTI = new EasterHoliday( 60, "Corpus Christi");
\r
148 class EasterRule implements DateRule {
\r
149 public EasterRule(int daysAfterEaster, boolean isOrthodox) {
\r
150 this.daysAfterEaster = daysAfterEaster;
\r
152 orthodox.setGregorianChange(new Date(Long.MAX_VALUE));
\r
153 calendar = orthodox;
\r
158 * Return the first occurrance of this rule on or after the given date
\r
160 public Date firstAfter(Date start)
\r
162 return doFirstBetween(start, null);
\r
166 * Return the first occurrance of this rule on or after
\r
167 * the given start date and before the given end date.
\r
169 public Date firstBetween(Date start, Date end)
\r
171 return doFirstBetween(start, end);
\r
175 * Return true if the given Date is on the same day as Easter
\r
177 public boolean isOn(Date date)
\r
179 synchronized(calendar) {
\r
180 calendar.setTime(date);
\r
181 int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
\r
183 calendar.setTime(computeInYear(calendar.getTime(), calendar));
\r
185 return calendar.get(Calendar.DAY_OF_YEAR) == dayOfYear;
\r
190 * Return true if Easter occurs between the two dates given
\r
192 public boolean isBetween(Date start, Date end)
\r
194 return firstBetween(start, end) != null; // TODO: optimize?
\r
197 private Date doFirstBetween(Date start, Date end)
\r
199 //System.out.println("doFirstBetween: start = " + start.toString());
\r
200 //System.out.println("doFirstBetween: end = " + end.toString());
\r
202 synchronized(calendar) {
\r
203 // Figure out when this holiday lands in the given year
\r
204 Date result = computeInYear(start, calendar);
\r
206 //System.out.println(" result = " + result.toString());
\r
208 // We might have gotten a date that's in the same year as "start", but
\r
209 // earlier in the year. If so, go to next year
\r
210 if (result.before(start))
\r
212 calendar.setTime(start);
\r
213 calendar.get(Calendar.YEAR); // JDK 1.1.2 bug workaround
\r
214 calendar.add(Calendar.YEAR, 1);
\r
216 //System.out.println(" Result before start, going to next year: "
\r
217 // + calendar.getTime().toString());
\r
219 result = computeInYear(calendar.getTime(), calendar);
\r
220 //System.out.println(" result = " + result.toString());
\r
223 if (end != null && result.after(end)) {
\r
224 //System.out.println("Result after end, returning null");
\r
232 * Compute the month and date on which this holiday falls in the year
\r
233 * containing the date "date". First figure out which date Easter
\r
234 * lands on in this year, and then add the offset for this holiday to get
\r
237 * The algorithm here is taken from the
\r
238 * <a href="http://www.faqs.org/faqs/calendars/faq/">Calendar FAQ</a>.
\r
240 private Date computeInYear(Date date, GregorianCalendar cal)
\r
242 if (cal == null) cal = calendar;
\r
244 synchronized(cal) {
\r
247 int year = cal.get(Calendar.YEAR);
\r
248 int g = year % 19; // "Golden Number" of year - 1
\r
249 int i = 0; // # of days from 3/21 to the Paschal full moon
\r
250 int j = 0; // Weekday (0-based) of Paschal full moon
\r
252 if (cal.getTime().after( cal.getGregorianChange()))
\r
254 // We're past the Gregorian switchover, so use the Gregorian rules.
\r
255 int c = year / 100;
\r
256 int h = (c - c/4 - (8*c+13)/25 + 19*g + 15) % 30;
\r
257 i = h - (h/28)*(1 - (h/28)*(29/(h+1))*((21-g)/11));
\r
258 j = (year + year/4 + i + 2 - c + c/4) % 7;
\r
262 // Use the old Julian rules.
\r
263 i = (19*g + 15) % 30;
\r
264 j = (year + year/4 + i) % 7;
\r
267 int m = 3 + (l+40)/44; // 1-based month in which Easter falls
\r
268 int d = l + 28 - 31*(m/4); // Date of Easter within that month
\r
271 cal.set(Calendar.ERA, GregorianCalendar.AD);
\r
272 cal.set(Calendar.YEAR, year);
\r
273 cal.set(Calendar.MONTH, m-1); // 0-based
\r
274 cal.set(Calendar.DATE, d);
\r
275 cal.getTime(); // JDK 1.1.2 bug workaround
\r
276 cal.add(Calendar.DATE, daysAfterEaster);
\r
278 return cal.getTime();
\r
282 private static GregorianCalendar gregorian = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */);
\r
283 private static GregorianCalendar orthodox = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */);
\r
285 private int daysAfterEaster;
\r
286 private GregorianCalendar calendar = gregorian;
\r