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