2 *******************************************************************************
3 * Copyright (C) 1996-2010, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
8 package com.ibm.icu.util;
10 import java.util.Date;
13 * <b>Note:</b> The Holiday framework is a technology preview.
14 * Despite its age, is still draft API, and clients should treat it as such.
16 * A Holiday subclass which represents holidays that occur
17 * a fixed number of days before or after Easter. Supports both the
18 * Western and Orthodox methods for calculating Easter.
19 * @draft ICU 2.8 (retainAll)
20 * @provisional This API might change or be removed in a future release.
22 public class EasterHoliday extends Holiday
25 * Construct a holiday that falls on Easter Sunday every year
27 * @param name The name of the holiday
29 * @provisional This API might change or be removed in a future release.
31 public EasterHoliday(String name)
33 super(name, new EasterRule(0, false));
37 * Construct a holiday that falls a specified number of days before
38 * or after Easter Sunday each year.
40 * @param daysAfter The number of days before (-) or after (+) Easter
41 * @param name The name of the holiday
43 * @provisional This API might change or be removed in a future release.
45 public EasterHoliday(int daysAfter, String name)
47 super(name, new EasterRule(daysAfter, false));
51 * Construct a holiday that falls a specified number of days before
52 * or after Easter Sunday each year, using either the Western
53 * or Orthodox calendar.
55 * @param daysAfter The number of days before (-) or after (+) Easter
56 * @param orthodox Use the Orthodox calendar?
57 * @param name The name of the holiday
59 * @provisional This API might change or be removed in a future release.
61 public EasterHoliday(int daysAfter, boolean orthodox, String name)
63 super(name, new EasterRule(daysAfter, orthodox));
67 * Shrove Tuesday, aka Mardi Gras, 48 days before Easter
69 * @provisional This API might change or be removed in a future release.
71 static public final EasterHoliday SHROVE_TUESDAY = new EasterHoliday(-48, "Shrove Tuesday");
74 * Ash Wednesday, start of Lent, 47 days before Easter
76 * @provisional This API might change or be removed in a future release.
78 static public final EasterHoliday ASH_WEDNESDAY = new EasterHoliday(-47, "Ash Wednesday");
81 * Palm Sunday, 7 days before Easter
83 * @provisional This API might change or be removed in a future release.
85 static public final EasterHoliday PALM_SUNDAY = new EasterHoliday( -7, "Palm Sunday");
88 * Maundy Thursday, 3 days before Easter
90 * @provisional This API might change or be removed in a future release.
92 static public final EasterHoliday MAUNDY_THURSDAY = new EasterHoliday( -3, "Maundy Thursday");
95 * Good Friday, 2 days before Easter
97 * @provisional This API might change or be removed in a future release.
99 static public final EasterHoliday GOOD_FRIDAY = new EasterHoliday( -2, "Good Friday");
104 * @provisional This API might change or be removed in a future release.
106 static public final EasterHoliday EASTER_SUNDAY = new EasterHoliday( 0, "Easter Sunday");
109 * Easter Monday, 1 day after Easter
111 * @provisional This API might change or be removed in a future release.
113 static public final EasterHoliday EASTER_MONDAY = new EasterHoliday( 1, "Easter Monday");
116 * Ascension, 39 days after Easter
118 * @provisional This API might change or be removed in a future release.
120 static public final EasterHoliday ASCENSION = new EasterHoliday( 39, "Ascension");
123 * Pentecost (aka Whit Sunday), 49 days after Easter
125 * @provisional This API might change or be removed in a future release.
127 static public final EasterHoliday PENTECOST = new EasterHoliday( 49, "Pentecost");
130 * Whit Sunday (aka Pentecost), 49 days after Easter
132 * @provisional This API might change or be removed in a future release.
134 static public final EasterHoliday WHIT_SUNDAY = new EasterHoliday( 49, "Whit Sunday");
137 * Whit Monday, 50 days after Easter
139 * @provisional This API might change or be removed in a future release.
141 static public final EasterHoliday WHIT_MONDAY = new EasterHoliday( 50, "Whit Monday");
144 * Corpus Christi, 60 days after Easter
146 * @provisional This API might change or be removed in a future release.
148 static public final EasterHoliday CORPUS_CHRISTI = new EasterHoliday( 60, "Corpus Christi");
151 class EasterRule implements DateRule {
152 public EasterRule(int daysAfterEaster, boolean isOrthodox) {
153 this.daysAfterEaster = daysAfterEaster;
155 orthodox.setGregorianChange(new Date(Long.MAX_VALUE));
161 * Return the first occurrance of this rule on or after the given date
163 public Date firstAfter(Date start)
165 return doFirstBetween(start, null);
169 * Return the first occurrance of this rule on or after
170 * the given start date and before the given end date.
172 public Date firstBetween(Date start, Date end)
174 return doFirstBetween(start, end);
178 * Return true if the given Date is on the same day as Easter
180 public boolean isOn(Date date)
182 synchronized(calendar) {
183 calendar.setTime(date);
184 int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
186 calendar.setTime(computeInYear(calendar.getTime(), calendar));
188 return calendar.get(Calendar.DAY_OF_YEAR) == dayOfYear;
193 * Return true if Easter occurs between the two dates given
195 public boolean isBetween(Date start, Date end)
197 return firstBetween(start, end) != null; // TODO: optimize?
200 private Date doFirstBetween(Date start, Date end)
202 //System.out.println("doFirstBetween: start = " + start.toString());
203 //System.out.println("doFirstBetween: end = " + end.toString());
205 synchronized(calendar) {
206 // Figure out when this holiday lands in the given year
207 Date result = computeInYear(start, calendar);
209 //System.out.println(" result = " + result.toString());
211 // We might have gotten a date that's in the same year as "start", but
212 // earlier in the year. If so, go to next year
213 if (result.before(start))
215 calendar.setTime(start);
216 calendar.get(Calendar.YEAR); // JDK 1.1.2 bug workaround
217 calendar.add(Calendar.YEAR, 1);
219 //System.out.println(" Result before start, going to next year: "
220 // + calendar.getTime().toString());
222 result = computeInYear(calendar.getTime(), calendar);
223 //System.out.println(" result = " + result.toString());
226 if (end != null && result.after(end)) {
227 //System.out.println("Result after end, returning null");
235 * Compute the month and date on which this holiday falls in the year
236 * containing the date "date". First figure out which date Easter
237 * lands on in this year, and then add the offset for this holiday to get
240 * The algorithm here is taken from the
241 * <a href="http://www.faqs.org/faqs/calendars/faq/">Calendar FAQ</a>.
243 private Date computeInYear(Date date, GregorianCalendar cal)
245 if (cal == null) cal = calendar;
250 int year = cal.get(Calendar.YEAR);
251 int g = year % 19; // "Golden Number" of year - 1
252 int i = 0; // # of days from 3/21 to the Paschal full moon
253 int j = 0; // Weekday (0-based) of Paschal full moon
255 if (cal.getTime().after( cal.getGregorianChange()))
257 // We're past the Gregorian switchover, so use the Gregorian rules.
259 int h = (c - c/4 - (8*c+13)/25 + 19*g + 15) % 30;
260 i = h - (h/28)*(1 - (h/28)*(29/(h+1))*((21-g)/11));
261 j = (year + year/4 + i + 2 - c + c/4) % 7;
265 // Use the old Julian rules.
266 i = (19*g + 15) % 30;
267 j = (year + year/4 + i) % 7;
270 int m = 3 + (l+40)/44; // 1-based month in which Easter falls
271 int d = l + 28 - 31*(m/4); // Date of Easter within that month
274 cal.set(Calendar.ERA, GregorianCalendar.AD);
275 cal.set(Calendar.YEAR, year);
276 cal.set(Calendar.MONTH, m-1); // 0-based
277 cal.set(Calendar.DATE, d);
278 cal.getTime(); // JDK 1.1.2 bug workaround
279 cal.add(Calendar.DATE, daysAfterEaster);
281 return cal.getTime();
285 private static GregorianCalendar gregorian = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */);
286 private static GregorianCalendar orthodox = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */);
288 private int daysAfterEaster;
289 private GregorianCalendar calendar = gregorian;