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