EasterHoliday.java revision 836e6b40a94ec3fb7545a76cb072960442b7eee9
1/* GENERATED SOURCE. DO NOT MODIFY. */
2/*
3 *******************************************************************************
4 * Copyright (C) 1996-2014, International Business Machines Corporation and    *
5 * others. All Rights Reserved.                                                *
6 *******************************************************************************
7 */
8
9package android.icu.util;
10
11import java.util.Date;
12
13/**
14 * <b>Note:</b> The Holiday framework is a technology preview.
15 * Despite its age, is still draft API, and clients should treat it as such.
16 *
17 * A Holiday subclass which represents holidays that occur
18 * a fixed number of days before or after Easter.  Supports both the
19 * Western and Orthodox methods for calculating Easter.
20 * {@literal @}draft ICU 2.8 (retainAll)
21 * {@literal @}provisional This API might change or be removed in a future release.
22 * @hide Only a subset of ICU is exposed in Android
23 * @hide draft / provisional / internal are hidden on Android
24 * @hide All android.icu classes are currently hidden
25 */
26public class EasterHoliday extends Holiday
27{
28    /**
29     * Construct a holiday that falls on Easter Sunday every year
30     *
31     * @param name The name of the holiday
32     * {@literal @}draft ICU 2.8
33     * {@literal @}provisional This API might change or be removed in a future release.
34     * @hide draft / provisional / internal are hidden on Android
35     */
36    public EasterHoliday(String name)
37    {
38        super(name, new EasterRule(0, false));
39    }
40
41    /**
42     * Construct a holiday that falls a specified number of days before
43     * or after Easter Sunday each year.
44     *
45     * @param daysAfter The number of days before (-) or after (+) Easter
46     * @param name      The name of the holiday
47     * {@literal @}draft ICU 2.8
48     * {@literal @}provisional This API might change or be removed in a future release.
49     * @hide draft / provisional / internal are hidden on Android
50     */
51    public EasterHoliday(int daysAfter, String name)
52    {
53        super(name, new EasterRule(daysAfter, false));
54    }
55
56    /**
57     * Construct a holiday that falls a specified number of days before
58     * or after Easter Sunday each year, using either the Western
59     * or Orthodox calendar.
60     *
61     * @param daysAfter The number of days before (-) or after (+) Easter
62     * @param orthodox  Use the Orthodox calendar?
63     * @param name      The name of the holiday
64     * {@literal @}draft ICU 2.8
65     * {@literal @}provisional This API might change or be removed in a future release.
66     * @hide draft / provisional / internal are hidden on Android
67     */
68    public EasterHoliday(int daysAfter, boolean orthodox, String name)
69    {
70        super(name, new EasterRule(daysAfter, orthodox));
71    }
72
73    /**
74     * Shrove Tuesday, aka Mardi Gras, 48 days before Easter
75     * {@literal @}draft ICU 2.8
76     * {@literal @}provisional This API might change or be removed in a future release.
77     * @hide draft / provisional / internal are hidden on Android
78     */
79    static public final EasterHoliday SHROVE_TUESDAY  = new EasterHoliday(-48,    "Shrove Tuesday");
80
81    /**
82     * Ash Wednesday, start of Lent, 47 days before Easter
83     * {@literal @}draft ICU 2.8
84     * {@literal @}provisional This API might change or be removed in a future release.
85     * @hide draft / provisional / internal are hidden on Android
86     */
87    static public final EasterHoliday ASH_WEDNESDAY   = new EasterHoliday(-47,    "Ash Wednesday");
88
89    /**
90     * Palm Sunday, 7 days before Easter
91     * {@literal @}draft ICU 2.8
92     * {@literal @}provisional This API might change or be removed in a future release.
93     * @hide draft / provisional / internal are hidden on Android
94     */
95    static public final EasterHoliday PALM_SUNDAY     = new EasterHoliday( -7,    "Palm Sunday");
96
97    /**
98     * Maundy Thursday, 3 days before Easter
99     * {@literal @}draft ICU 2.8
100     * {@literal @}provisional This API might change or be removed in a future release.
101     * @hide draft / provisional / internal are hidden on Android
102     */
103    static public final EasterHoliday MAUNDY_THURSDAY = new EasterHoliday( -3,    "Maundy Thursday");
104
105    /**
106     * Good Friday, 2 days before Easter
107     * {@literal @}draft ICU 2.8
108     * {@literal @}provisional This API might change or be removed in a future release.
109     * @hide draft / provisional / internal are hidden on Android
110     */
111    static public final EasterHoliday GOOD_FRIDAY     = new EasterHoliday( -2,    "Good Friday");
112
113    /**
114     * Easter Sunday
115     * {@literal @}draft ICU 2.8
116     * {@literal @}provisional This API might change or be removed in a future release.
117     * @hide draft / provisional / internal are hidden on Android
118     */
119    static public final EasterHoliday EASTER_SUNDAY   = new EasterHoliday(  0,    "Easter Sunday");
120
121    /**
122     * Easter Monday, 1 day after Easter
123     * {@literal @}draft ICU 2.8
124     * {@literal @}provisional This API might change or be removed in a future release.
125     * @hide draft / provisional / internal are hidden on Android
126     */
127    static public final EasterHoliday EASTER_MONDAY   = new EasterHoliday(  1,    "Easter Monday");
128
129    /**
130     * Ascension, 39 days after Easter
131     * {@literal @}draft ICU 2.8
132     * {@literal @}provisional This API might change or be removed in a future release.
133     * @hide draft / provisional / internal are hidden on Android
134     */
135    static public final EasterHoliday ASCENSION       = new EasterHoliday( 39,    "Ascension");
136
137    /**
138     * Pentecost (aka Whit Sunday), 49 days after Easter
139     * {@literal @}draft ICU 2.8
140     * {@literal @}provisional This API might change or be removed in a future release.
141     * @hide draft / provisional / internal are hidden on Android
142     */
143    static public final EasterHoliday PENTECOST       = new EasterHoliday( 49,    "Pentecost");
144
145    /**
146     * Whit Sunday (aka Pentecost), 49 days after Easter
147     * {@literal @}draft ICU 2.8
148     * {@literal @}provisional This API might change or be removed in a future release.
149     * @hide draft / provisional / internal are hidden on Android
150     */
151    static public final EasterHoliday WHIT_SUNDAY     = new EasterHoliday( 49,    "Whit Sunday");
152
153    /**
154     * Whit Monday, 50 days after Easter
155     * {@literal @}draft ICU 2.8
156     * {@literal @}provisional This API might change or be removed in a future release.
157     * @hide draft / provisional / internal are hidden on Android
158     */
159    static public final EasterHoliday WHIT_MONDAY     = new EasterHoliday( 50,    "Whit Monday");
160
161    /**
162     * Corpus Christi, 60 days after Easter
163     * {@literal @}draft ICU 2.8
164     * {@literal @}provisional This API might change or be removed in a future release.
165     * @hide draft / provisional / internal are hidden on Android
166     */
167    static public final EasterHoliday CORPUS_CHRISTI  = new EasterHoliday( 60,    "Corpus Christi");
168}
169
170class EasterRule implements DateRule {
171    public EasterRule(int daysAfterEaster, boolean isOrthodox) {
172        this.daysAfterEaster = daysAfterEaster;
173        if (isOrthodox) {
174            orthodox.setGregorianChange(new Date(Long.MAX_VALUE));
175            calendar = orthodox;
176        }
177    }
178
179    /**
180     * Return the first occurrance of this rule on or after the given date
181     */
182    public Date firstAfter(Date start)
183    {
184        return doFirstBetween(start, null);
185    }
186
187    /**
188     * Return the first occurrance of this rule on or after
189     * the given start date and before the given end date.
190     */
191    public Date firstBetween(Date start, Date end)
192    {
193        return doFirstBetween(start, end);
194    }
195
196    /**
197     * Return true if the given Date is on the same day as Easter
198     */
199    public boolean isOn(Date date)
200    {
201        synchronized(calendar) {
202            calendar.setTime(date);
203            int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
204
205            calendar.setTime(computeInYear(calendar.getTime(), calendar));
206
207            return calendar.get(Calendar.DAY_OF_YEAR) == dayOfYear;
208        }
209    }
210
211    /**
212     * Return true if Easter occurs between the two dates given
213     */
214    public boolean isBetween(Date start, Date end)
215    {
216        return firstBetween(start, end) != null; // TODO: optimize?
217    }
218
219    private Date doFirstBetween(Date start, Date end)
220    {
221        //System.out.println("doFirstBetween: start   = " + start.toString());
222        //System.out.println("doFirstBetween: end     = " + end.toString());
223
224        synchronized(calendar) {
225            // Figure out when this holiday lands in the given year
226            Date result = computeInYear(start, calendar);
227
228         //System.out.println("                result  = " + result.toString());
229
230            // We might have gotten a date that's in the same year as "start", but
231            // earlier in the year.  If so, go to next year
232            if (result.before(start))
233            {
234                calendar.setTime(start);
235                calendar.get(Calendar.YEAR);    // JDK 1.1.2 bug workaround
236                calendar.add(Calendar.YEAR, 1);
237
238                //System.out.println("                Result before start, going to next year: "
239                //                        + calendar.getTime().toString());
240
241                result = computeInYear(calendar.getTime(), calendar);
242                //System.out.println("                result  = " + result.toString());
243            }
244
245            if (end != null && !result.before(end)) {
246                //System.out.println("Result after end, returning null");
247                return null;
248            }
249            return result;
250        }
251    }
252
253    /**
254     * Compute the month and date on which this holiday falls in the year
255     * containing the date "date".  First figure out which date Easter
256     * lands on in this year, and then add the offset for this holiday to get
257     * the right date.
258     * <p>
259     * The algorithm here is taken from the
260     * <a href="http://www.faqs.org/faqs/calendars/faq/">Calendar FAQ</a>.
261     */
262    private Date computeInYear(Date date, GregorianCalendar cal)
263    {
264        if (cal == null) cal = calendar;
265
266        synchronized(cal) {
267            cal.setTime(date);
268
269            int year = cal.get(Calendar.YEAR);
270            int g = year % 19;  // "Golden Number" of year - 1
271            int i = 0;          // # of days from 3/21 to the Paschal full moon
272            int j = 0;          // Weekday (0-based) of Paschal full moon
273
274            if (cal.getTime().after( cal.getGregorianChange()))
275            {
276                // We're past the Gregorian switchover, so use the Gregorian rules.
277                int c = year / 100;
278                int h = (c - c/4 - (8*c+13)/25 + 19*g + 15) % 30;
279                i = h - (h/28)*(1 - (h/28)*(29/(h+1))*((21-g)/11));
280                j = (year + year/4 + i + 2 - c + c/4) % 7;
281            }
282            else
283            {
284                // Use the old Julian rules.
285                i = (19*g + 15) % 30;
286                j = (year + year/4 + i) % 7;
287            }
288            int l = i - j;
289            int m = 3 + (l+40)/44;              // 1-based month in which Easter falls
290            int d = l + 28 - 31*(m/4);          // Date of Easter within that month
291
292            cal.clear();
293            cal.set(Calendar.ERA, GregorianCalendar.AD);
294            cal.set(Calendar.YEAR, year);
295            cal.set(Calendar.MONTH, m-1);       // 0-based
296            cal.set(Calendar.DATE, d);
297            cal.getTime();                      // JDK 1.1.2 bug workaround
298            cal.add(Calendar.DATE, daysAfterEaster);
299
300            return cal.getTime();
301        }
302    }
303
304    private static GregorianCalendar gregorian = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */);
305    private static GregorianCalendar orthodox = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */);
306
307    private int               daysAfterEaster;
308    private GregorianCalendar calendar = gregorian;
309}
310