]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/classes/core/src/com/ibm/icu/util/EasterHoliday.java
Upgrade ICU4J.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / core / src / com / ibm / icu / util / EasterHoliday.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 1996-2010, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7
8 package com.ibm.icu.util;
9
10 import java.util.Date;
11
12 /**
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.
15  * 
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.
21  */
22 public class EasterHoliday extends Holiday
23 {
24     /**
25      * Construct a holiday that falls on Easter Sunday every year
26      *
27      * @param name The name of the holiday
28      * @draft ICU 2.8
29      * @provisional This API might change or be removed in a future release.
30      */
31     public EasterHoliday(String name)
32     {
33         super(name, new EasterRule(0, false));
34     }
35
36     /**
37      * Construct a holiday that falls a specified number of days before
38      * or after Easter Sunday each year.
39      *
40      * @param daysAfter The number of days before (-) or after (+) Easter
41      * @param name      The name of the holiday
42      * @draft ICU 2.8
43      * @provisional This API might change or be removed in a future release.
44      */
45     public EasterHoliday(int daysAfter, String name)
46     {
47         super(name, new EasterRule(daysAfter, false));
48     }
49
50     /**
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.
54      *
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
58      * @draft ICU 2.8
59      * @provisional This API might change or be removed in a future release.
60      */
61     public EasterHoliday(int daysAfter, boolean orthodox, String name)
62     {
63         super(name, new EasterRule(daysAfter, orthodox));
64     }
65
66     /**
67      * Shrove Tuesday, aka Mardi Gras, 48 days before Easter
68      * @draft ICU 2.8
69      * @provisional This API might change or be removed in a future release.
70      */
71     static public final EasterHoliday SHROVE_TUESDAY  = new EasterHoliday(-48,    "Shrove Tuesday");
72
73     /**
74      * Ash Wednesday, start of Lent, 47 days before Easter
75      * @draft ICU 2.8
76      * @provisional This API might change or be removed in a future release.
77      */
78     static public final EasterHoliday ASH_WEDNESDAY   = new EasterHoliday(-47,    "Ash Wednesday");
79
80     /**
81      * Palm Sunday, 7 days before Easter
82      * @draft ICU 2.8
83      * @provisional This API might change or be removed in a future release.
84      */
85     static public final EasterHoliday PALM_SUNDAY     = new EasterHoliday( -7,    "Palm Sunday");
86
87     /**
88      * Maundy Thursday, 3 days before Easter
89      * @draft ICU 2.8
90      * @provisional This API might change or be removed in a future release.
91      */
92     static public final EasterHoliday MAUNDY_THURSDAY = new EasterHoliday( -3,    "Maundy Thursday");
93
94     /**
95      * Good Friday, 2 days before Easter
96      * @draft ICU 2.8
97      * @provisional This API might change or be removed in a future release.
98      */
99     static public final EasterHoliday GOOD_FRIDAY     = new EasterHoliday( -2,    "Good Friday");
100
101     /**
102      * Easter Sunday
103      * @draft ICU 2.8
104      * @provisional This API might change or be removed in a future release.
105      */
106     static public final EasterHoliday EASTER_SUNDAY   = new EasterHoliday(  0,    "Easter Sunday");
107
108     /**
109      * Easter Monday, 1 day after Easter
110      * @draft ICU 2.8
111      * @provisional This API might change or be removed in a future release.
112      */
113     static public final EasterHoliday EASTER_MONDAY   = new EasterHoliday(  1,    "Easter Monday");
114
115     /**
116      * Ascension, 39 days after Easter
117      * @draft ICU 2.8
118      * @provisional This API might change or be removed in a future release.
119      */
120     static public final EasterHoliday ASCENSION       = new EasterHoliday( 39,    "Ascension");
121
122     /**
123      * Pentecost (aka Whit Sunday), 49 days after Easter
124      * @draft ICU 2.8
125      * @provisional This API might change or be removed in a future release.
126      */
127     static public final EasterHoliday PENTECOST       = new EasterHoliday( 49,    "Pentecost");
128
129     /**
130      * Whit Sunday (aka Pentecost), 49 days after Easter
131      * @draft ICU 2.8
132      * @provisional This API might change or be removed in a future release.
133      */
134     static public final EasterHoliday WHIT_SUNDAY     = new EasterHoliday( 49,    "Whit Sunday");
135
136     /**
137      * Whit Monday, 50 days after Easter
138      * @draft ICU 2.8
139      * @provisional This API might change or be removed in a future release.
140      */
141     static public final EasterHoliday WHIT_MONDAY     = new EasterHoliday( 50,    "Whit Monday");
142
143     /**
144      * Corpus Christi, 60 days after Easter
145      * @draft ICU 2.8
146      * @provisional This API might change or be removed in a future release.
147      */
148     static public final EasterHoliday CORPUS_CHRISTI  = new EasterHoliday( 60,    "Corpus Christi");
149 }
150
151 class EasterRule implements DateRule {
152     public EasterRule(int daysAfterEaster, boolean isOrthodox) {
153         this.daysAfterEaster = daysAfterEaster;
154         if (isOrthodox) {
155             orthodox.setGregorianChange(new Date(Long.MAX_VALUE));
156             calendar = orthodox;
157         }
158     }
159
160     /**
161      * Return the first occurrance of this rule on or after the given date
162      */
163     public Date firstAfter(Date start)
164     {
165         return doFirstBetween(start, null);
166     }
167
168     /**
169      * Return the first occurrance of this rule on or after
170      * the given start date and before the given end date.
171      */
172     public Date firstBetween(Date start, Date end)
173     {
174         return doFirstBetween(start, end);
175     }
176
177     /**
178      * Return true if the given Date is on the same day as Easter
179      */
180     public boolean isOn(Date date)
181     {
182         synchronized(calendar) {
183             calendar.setTime(date);
184             int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
185
186             calendar.setTime(computeInYear(calendar.getTime(), calendar));
187
188             return calendar.get(Calendar.DAY_OF_YEAR) == dayOfYear;
189         }
190     }
191
192     /**
193      * Return true if Easter occurs between the two dates given
194      */
195     public boolean isBetween(Date start, Date end)
196     {
197         return firstBetween(start, end) != null; // TODO: optimize?
198     }
199
200     private Date doFirstBetween(Date start, Date end)
201     {
202         //System.out.println("doFirstBetween: start   = " + start.toString());
203         //System.out.println("doFirstBetween: end     = " + end.toString());
204
205         synchronized(calendar) {
206             // Figure out when this holiday lands in the given year
207             Date result = computeInYear(start, calendar);
208
209          //System.out.println("                result  = " + result.toString());
210
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))
214             {
215                 calendar.setTime(start);
216                 calendar.get(Calendar.YEAR);    // JDK 1.1.2 bug workaround
217                 calendar.add(Calendar.YEAR, 1);
218
219                 //System.out.println("                Result before start, going to next year: "
220                 //                        + calendar.getTime().toString());
221
222                 result = computeInYear(calendar.getTime(), calendar);
223                 //System.out.println("                result  = " + result.toString());
224             }
225
226             if (end != null && result.after(end)) {
227                 //System.out.println("Result after end, returning null");
228                 return null;
229             }
230             return result;
231         }
232     }
233
234     /**
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
238      * the right date.
239      * <p>
240      * The algorithm here is taken from the
241      * <a href="http://www.faqs.org/faqs/calendars/faq/">Calendar FAQ</a>.
242      */
243     private Date computeInYear(Date date, GregorianCalendar cal)
244     {
245         if (cal == null) cal = calendar;
246
247         synchronized(cal) {
248             cal.setTime(date);
249
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
254
255             if (cal.getTime().after( cal.getGregorianChange()))
256             {
257                 // We're past the Gregorian switchover, so use the Gregorian rules.
258                 int c = year / 100;
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;
262             }
263             else
264             {
265                 // Use the old Julian rules.
266                 i = (19*g + 15) % 30;
267                 j = (year + year/4 + i) % 7;
268             }
269             int l = i - j;
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
272
273             cal.clear();
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);
280
281             return cal.getTime();
282         }
283     }
284
285     private static GregorianCalendar gregorian = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */);
286     private static GregorianCalendar orthodox = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */);
287
288     private int               daysAfterEaster;
289     private GregorianCalendar calendar = gregorian;
290 }