KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibm > icu > util > IslamicCalendar


1 /*
2  *******************************************************************************
3  * Copyright (C) 1996-2006, International Business Machines Corporation and *
4  * others. All Rights Reserved. *
5  *******************************************************************************
6  */

7 package com.ibm.icu.util;
8 import com.ibm.icu.util.TimeZone;
9 import com.ibm.icu.impl.CalendarAstronomer;
10 import com.ibm.icu.impl.CalendarCache;
11 import java.util.Date JavaDoc;
12 import java.util.Locale JavaDoc;
13
14 /**
15  * <code>IslamicCalendar</code> is a subclass of <code>Calendar</code>
16  * that that implements the Islamic civil and religious calendars. It
17  * is used as the civil calendar in most of the Arab world and the
18  * liturgical calendar of the Islamic faith worldwide. This calendar
19  * is also known as the "Hijri" calendar, since it starts at the time
20  * of Mohammed's emigration (or "hijra") to Medinah on Thursday,
21  * July 15, 622 AD (Julian).
22  * <p>
23  * The Islamic calendar is strictly lunar, and thus an Islamic year of twelve
24  * lunar months does not correspond to the solar year used by most other
25  * calendar systems, including the Gregorian. An Islamic year is, on average,
26  * about 354 days long, so each successive Islamic year starts about 11 days
27  * earlier in the corresponding Gregorian year.
28  * <p>
29  * Each month of the calendar starts when the new moon's crescent is visible
30  * at sunset. However, in order to keep the time fields in this class
31  * synchronized with those of the other calendars and with local clock time,
32  * we treat days and months as beginning at midnight,
33  * roughly 6 hours after the corresponding sunset.
34  * <p>
35  * There are two main variants of the Islamic calendar in existence. The first
36  * is the <em>civil</em> calendar, which uses a fixed cycle of alternating 29-
37  * and 30-day months, with a leap day added to the last month of 11 out of
38  * every 30 years. This calendar is easily calculated and thus predictable in
39  * advance, so it is used as the civil calendar in a number of Arab countries.
40  * This is the default behavior of a newly-created <code>IslamicCalendar</code>
41  * object.
42  * <p>
43  * The Islamic <em>religious</em> calendar, however, is based on the <em>observation</em>
44  * of the crescent moon. It is thus affected by the position at which the
45  * observations are made, seasonal variations in the time of sunset, the
46  * eccentricities of the moon's orbit, and even the weather at the observation
47  * site. This makes it impossible to calculate in advance, and it causes the
48  * start of a month in the religious calendar to differ from the civil calendar
49  * by up to three days.
50  * <p>
51  * Using astronomical calculations for the position of the sun and moon, the
52  * moon's illumination, and other factors, it is possible to determine the start
53  * of a lunar month with a fairly high degree of certainty. However, these
54  * calculations are extremely complicated and thus slow, so most algorithms,
55  * including the one used here, are only approximations of the true astronical
56  * calculations. At present, the approximations used in this class are fairly
57  * simplistic; they will be improved in later versions of the code.
58  * <p>
59  * The {@link #setCivil setCivil} method determines
60  * which approach is used to determine the start of a month. By default, the
61  * fixed-cycle civil calendar is used. However, if <code>setCivil(false)</code>
62  * is called, an approximation of the true lunar calendar will be used.
63  * <p>
64  * This class should not be subclassed.</p>
65  * <p>
66  * IslamicCalendar usually should be instantiated using
67  * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a <code>ULocale</code>
68  * with the tag <code>"@calendar=islamic"</code> or <code>"@calendar=islamic-civil"</code>.</p>
69  *
70  * @see com.ibm.icu.util.GregorianCalendar
71  * @see com.ibm.icu.util.Calendar
72  *
73  * @author Laura Werner
74  * @author Alan Liu
75  * @stable ICU 2.8
76  */

77 public class IslamicCalendar extends Calendar {
78     // jdk1.4.2 serialver
79
private static final long serialVersionUID = -6253365474073869325L;
80
81     private static String JavaDoc copyright = "Copyright \u00a9 1997-1998 IBM Corp. All Rights Reserved.";
82
83     //-------------------------------------------------------------------------
84
// Constants...
85
//-------------------------------------------------------------------------
86

87     /**
88      * Constant for Muharram, the 1st month of the Islamic year.
89      * @stable ICU 2.8
90      */

91     public static final int MUHARRAM = 0;
92
93     /**
94      * Constant for Safar, the 2nd month of the Islamic year.
95      * @stable ICU 2.8
96      */

97     public static final int SAFAR = 1;
98
99     /**
100      * Constant for Rabi' al-awwal (or Rabi' I), the 3rd month of the Islamic year.
101      * @stable ICU 2.8
102      */

103     public static final int RABI_1 = 2;
104
105     /**
106      * Constant for Rabi' al-thani or (Rabi' II), the 4th month of the Islamic year.
107      * @stable ICU 2.8
108      */

109     public static final int RABI_2 = 3;
110
111     /**
112      * Constant for Jumada al-awwal or (Jumada I), the 5th month of the Islamic year.
113      * @stable ICU 2.8
114      */

115     public static final int JUMADA_1 = 4;
116
117     /**
118      * Constant for Jumada al-thani or (Jumada II), the 6th month of the Islamic year.
119      * @stable ICU 2.8
120      */

121     public static final int JUMADA_2 = 5;
122
123     /**
124      * Constant for Rajab, the 7th month of the Islamic year.
125      * @stable ICU 2.8
126      */

127     public static final int RAJAB = 6;
128
129     /**
130      * Constant for Sha'ban, the 8th month of the Islamic year.
131      * @stable ICU 2.8
132      */

133     public static final int SHABAN = 7;
134
135     /**
136      * Constant for Ramadan, the 9th month of the Islamic year.
137      * @stable ICU 2.8
138      */

139     public static final int RAMADAN = 8;
140
141     /**
142      * Constant for Shawwal, the 10th month of the Islamic year.
143      * @stable ICU 2.8
144      */

145     public static final int SHAWWAL = 9;
146
147     /**
148      * Constant for Dhu al-Qi'dah, the 11th month of the Islamic year.
149      * @stable ICU 2.8
150      */

151     public static final int DHU_AL_QIDAH = 10;
152
153     /**
154      * Constant for Dhu al-Hijjah, the 12th month of the Islamic year.
155      * @stable ICU 2.8
156      */

157     public static final int DHU_AL_HIJJAH = 11;
158
159
160     private static final long HIJRA_MILLIS = -42521587200000L; // 7/16/622 AD 00:00
161

162     //-------------------------------------------------------------------------
163
// Constructors...
164
//-------------------------------------------------------------------------
165

166     /**
167      * Constructs a default <code>IslamicCalendar</code> using the current time
168      * in the default time zone with the default locale.
169      * @stable ICU 2.8
170      */

171     public IslamicCalendar()
172     {
173         this(TimeZone.getDefault(), ULocale.getDefault());
174     }
175
176     /**
177      * Constructs an <code>IslamicCalendar</code> based on the current time
178      * in the given time zone with the default locale.
179      * @param zone the given time zone.
180      * @stable ICU 2.8
181      */

182     public IslamicCalendar(TimeZone zone)
183     {
184         this(zone, ULocale.getDefault());
185     }
186
187     /**
188      * Constructs an <code>IslamicCalendar</code> based on the current time
189      * in the default time zone with the given locale.
190      *
191      * @param aLocale the given locale.
192      * @stable ICU 2.8
193      */

194     public IslamicCalendar(Locale JavaDoc aLocale)
195     {
196         this(TimeZone.getDefault(), aLocale);
197     }
198
199     /**
200      * Constructs an <code>IslamicCalendar</code> based on the current time
201      * in the default time zone with the given locale.
202      *
203      * @param locale the given ulocale.
204      * @draft ICU 3.2
205      * @provisional This API might change or be removed in a future release.
206      */

207     public IslamicCalendar(ULocale locale)
208     {
209         this(TimeZone.getDefault(), locale);
210     }
211
212     /**
213      * Constructs an <code>IslamicCalendar</code> based on the current time
214      * in the given time zone with the given locale.
215      *
216      * @param zone the given time zone.
217      * @param aLocale the given locale.
218      * @stable ICU 2.8
219      */

220     public IslamicCalendar(TimeZone zone, Locale JavaDoc aLocale)
221     {
222         super(zone, aLocale);
223         setTimeInMillis(System.currentTimeMillis());
224     }
225
226     /**
227      * Constructs an <code>IslamicCalendar</code> based on the current time
228      * in the given time zone with the given locale.
229      *
230      * @param zone the given time zone.
231      * @param locale the given ulocale.
232      * @draft ICU 3.2
233      * @provisional This API might change or be removed in a future release.
234      */

235     public IslamicCalendar(TimeZone zone, ULocale locale)
236     {
237         super(zone, locale);
238         setTimeInMillis(System.currentTimeMillis());
239     }
240
241     /**
242      * Constructs an <code>IslamicCalendar</code> with the given date set
243      * in the default time zone with the default locale.
244      *
245      * @param date The date to which the new calendar is set.
246      * @stable ICU 2.8
247      */

248     public IslamicCalendar(Date JavaDoc date) {
249         super(TimeZone.getDefault(), ULocale.getDefault());
250         this.setTime(date);
251     }
252
253     /**
254      * Constructs an <code>IslamicCalendar</code> with the given date set
255      * in the default time zone with the default locale.
256      *
257      * @param year the value used to set the {@link #YEAR YEAR} time field in the calendar.
258      * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar.
259      * Note that the month value is 0-based. e.g., 0 for Muharram.
260      * @param date the value used to set the {@link #DATE DATE} time field in the calendar.
261      * @stable ICU 2.8
262      */

263     public IslamicCalendar(int year, int month, int date)
264     {
265         super(TimeZone.getDefault(), ULocale.getDefault());
266         this.set(Calendar.YEAR, year);
267         this.set(Calendar.MONTH, month);
268         this.set(Calendar.DATE, date);
269     }
270
271     /**
272      * Constructs an <code>IslamicCalendar</code> with the given date
273      * and time set for the default time zone with the default locale.
274      *
275      * @param year the value used to set the {@link #YEAR YEAR} time field in the calendar.
276      * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar.
277      * Note that the month value is 0-based. e.g., 0 for Muharram.
278      * @param date the value used to set the {@link #DATE DATE} time field in the calendar.
279      * @param hour the value used to set the {@link #HOUR_OF_DAY HOUR_OF_DAY} time field
280      * in the calendar.
281      * @param minute the value used to set the {@link #MINUTE MINUTE} time field
282      * in the calendar.
283      * @param second the value used to set the {@link #SECOND SECOND} time field
284      * in the calendar.
285      * @stable ICU 2.8
286      */

287     public IslamicCalendar(int year, int month, int date, int hour,
288                              int minute, int second)
289     {
290         super(TimeZone.getDefault(), ULocale.getDefault());
291         this.set(Calendar.YEAR, year);
292         this.set(Calendar.MONTH, month);
293         this.set(Calendar.DATE, date);
294         this.set(Calendar.HOUR_OF_DAY, hour);
295         this.set(Calendar.MINUTE, minute);
296         this.set(Calendar.SECOND, second);
297     }
298
299     /**
300      * Determines whether this object uses the fixed-cycle Islamic civil calendar
301      * or an approximation of the religious, astronomical calendar.
302      *
303      * @param beCivil <code>true</code> to use the civil calendar,
304      * <code>false</code> to use the astronomical calendar.
305      * @stable ICU 2.8
306      */

307     public void setCivil(boolean beCivil)
308     {
309         if (civil != beCivil) {
310             // The fields of the calendar will become invalid, because the calendar
311
// rules are different
312
long m = getTimeInMillis();
313             civil = beCivil;
314             clear();
315             setTimeInMillis(m);
316         }
317     }
318     
319     /**
320      * Returns <code>true</code> if this object is using the fixed-cycle civil
321      * calendar, or <code>false</code> if using the religious, astronomical
322      * calendar.
323      * @stable ICU 2.8
324      */

325     public boolean isCivil() {
326         return civil;
327     }
328     
329     //-------------------------------------------------------------------------
330
// Minimum / Maximum access functions
331
//-------------------------------------------------------------------------
332

333     private static final int LIMITS[][] = {
334         // Minimum Greatest Least Maximum
335
// Minimum Maximum
336
{ 0, 0, 0, 0 }, // ERA
337
{ 1, 1, 5000000, 5000000 }, // YEAR
338
{ 0, 0, 11, 11 }, // MONTH
339
{ 1, 1, 51, 52 }, // WEEK_OF_YEAR
340
{ 0, 0, 5, 6 }, // WEEK_OF_MONTH
341
{ 1, 1, 29, 30 }, // DAY_OF_MONTH
342
{ 1, 1, 354, 355 }, // DAY_OF_YEAR
343
{/* */}, // DAY_OF_WEEK
344
{ -1, -1, 4, 5 }, // DAY_OF_WEEK_IN_MONTH
345
{/* */}, // AM_PM
346
{/* */}, // HOUR
347
{/* */}, // HOUR_OF_DAY
348
{/* */}, // MINUTE
349
{/* */}, // SECOND
350
{/* */}, // MILLISECOND
351
{/* */}, // ZONE_OFFSET
352
{/* */}, // DST_OFFSET
353
{ -5000001, -5000001, 5000001, 5000001 }, // YEAR_WOY
354
{/* */}, // DOW_LOCAL
355
{ -5000000, -5000000, 5000000, 5000000 }, // EXTENDED_YEAR
356
{/* */}, // JULIAN_DAY
357
{/* */}, // MILLISECONDS_IN_DAY
358
};
359
360     /**
361      * @stable ICU 2.8
362      */

363     protected int handleGetLimit(int field, int limitType) {
364         return LIMITS[field][limitType];
365     }
366
367     //-------------------------------------------------------------------------
368
// Assorted calculation utilities
369
//
370

371 // Unused code - Alan 2003-05
372
// /**
373
// * Find the day of the week for a given day
374
// *
375
// * @param day The # of days since the start of the Islamic calendar.
376
// */
377
// // private and uncalled, perhaps not used yet?
378
// ///CLOVER:OFF
379
// private static final int absoluteDayToDayOfWeek(long day)
380
// {
381
// // Calculate the day of the week.
382
// // This relies on the fact that the epoch was a Thursday.
383
// int dayOfWeek = (int)(day + THURSDAY) % 7 + SUNDAY;
384
// if (dayOfWeek < 0) {
385
// dayOfWeek += 7;
386
// }
387
// return dayOfWeek;
388
// }
389
// ///CLOVER:ON
390

391     /**
392      * Determine whether a year is a leap year in the Islamic civil calendar
393      */

394     private final static boolean civilLeapYear(int year)
395     {
396         return (14 + 11 * year) % 30 < 11;
397         
398     }
399     
400     /**
401      * Return the day # on which the given year starts. Days are counted
402      * from the Hijri epoch, origin 0.
403      */

404     private long yearStart(int year) {
405         if (civil) {
406             return (year-1)*354 + (long)Math.floor((3+11*year)/30.0);
407         } else {
408             return trueMonthStart(12*(year-1));
409         }
410     }
411     
412     /**
413      * Return the day # on which the given month starts. Days are counted
414      * from the Hijri epoch, origin 0.
415      *
416      * @param year The hijri year
417      * @param year The hijri month, 0-based
418      */

419     private long monthStart(int year, int month) {
420         if (civil) {
421             return (long)Math.ceil(29.5*month)
422                     + (year-1)*354 + (long)Math.floor((3+11*year)/30.0);
423         } else {
424             return trueMonthStart(12*(year-1) + month);
425         }
426     }
427     
428     /**
429      * Find the day number on which a particular month of the true/lunar
430      * Islamic calendar starts.
431      *
432      * @param month The month in question, origin 0 from the Hijri epoch
433      *
434      * @return The day number on which the given month starts.
435      */

436     private static final long trueMonthStart(long month)
437     {
438         long start = cache.get(month);
439
440         if (start == CalendarCache.EMPTY)
441         {
442             // Make a guess at when the month started, using the average length
443
long origin = HIJRA_MILLIS
444                         + (long)Math.floor(month * CalendarAstronomer.SYNODIC_MONTH - 1) * ONE_DAY;
445
446             double age = moonAge(origin);
447
448             if (moonAge(origin) >= 0) {
449                 // The month has already started
450
do {
451                     origin -= ONE_DAY;
452                     age = moonAge(origin);
453                 } while (age >= 0);
454             }
455             else {
456                 // Preceding month has not ended yet.
457
do {
458                     origin += ONE_DAY;
459                     age = moonAge(origin);
460                 } while (age < 0);
461             }
462
463             start = (origin - HIJRA_MILLIS) / ONE_DAY + 1;
464             
465             cache.put(month, start);
466         }
467         return start;
468     }
469
470     /**
471      * Return the "age" of the moon at the given time; this is the difference
472      * in ecliptic latitude between the moon and the sun. This method simply
473      * calls CalendarAstronomer.moonAge, converts to degrees,
474      * and adjusts the resultto be in the range [-180, 180].
475      *
476      * @param time The time at which the moon's age is desired,
477      * in millis since 1/1/1970.
478      */

479     static final double moonAge(long time)
480     {
481         double age = 0;
482         
483         synchronized(astro) {
484             astro.setTime(time);
485             age = astro.getMoonAge();
486         }
487         // Convert to degrees and normalize...
488
age = age * 180 / Math.PI;
489         if (age > 180) {
490             age = age - 360;
491         }
492
493         return age;
494     }
495
496     //-------------------------------------------------------------------------
497
// Internal data....
498
//
499

500     // And an Astronomer object for the moon age calculations
501
private static CalendarAstronomer astro = new CalendarAstronomer();
502     
503     private static CalendarCache cache = new CalendarCache();
504     
505     /**
506      * <code>true</code> if this object uses the fixed-cycle Islamic civil calendar,
507      * and <code>false</code> if it approximates the true religious calendar using
508      * astronomical calculations for the time of the new moon.
509      *
510      * @serial
511      */

512     private boolean civil = true;
513
514     //----------------------------------------------------------------------
515
// Calendar framework
516
//----------------------------------------------------------------------
517

518     /**
519      * Return the length (in days) of the given month.
520      *
521      * @param extendedYear The hijri year
522      * @param month The hijri month, 0-based
523      * @stable ICU 2.8
524      */

525     protected int handleGetMonthLength(int extendedYear, int month) {
526
527         int length = 0;
528         
529         if (civil) {
530             length = 29 + (month+1) % 2;
531             if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) {
532                 length++;
533             }
534         } else {
535             month = 12*(extendedYear-1) + month;
536             length = (int)( trueMonthStart(month+1) - trueMonthStart(month) );
537         }
538         return length;
539     }
540
541     /**
542      * Return the number of days in the given Islamic year
543      * @stable ICU 2.8
544      */

545     protected int handleGetYearLength(int extendedYear) {
546         if (civil) {
547             return 354 + (civilLeapYear(extendedYear) ? 1 : 0);
548         } else {
549             int month = 12*(extendedYear-1);
550             return (int)(trueMonthStart(month + 12) - trueMonthStart(month));
551         }
552     }
553     
554     //-------------------------------------------------------------------------
555
// Functions for converting from field values to milliseconds....
556
//-------------------------------------------------------------------------
557

558     // Return JD of start of given month/year
559
/**
560      * @stable ICU 2.8
561      */

562     protected int handleComputeMonthStart(int eyear, int month, boolean useMonth) {
563         return (int) monthStart(eyear, month) + 1948439;
564     }
565
566     //-------------------------------------------------------------------------
567
// Functions for converting from milliseconds to field values
568
//-------------------------------------------------------------------------
569

570     /**
571      * @stable ICU 2.8
572      */

573     protected int handleGetExtendedYear() {
574         int year;
575         if (newerField(EXTENDED_YEAR, YEAR) == EXTENDED_YEAR) {
576             year = internalGet(EXTENDED_YEAR, 1); // Default to year 1
577
} else {
578             year = internalGet(YEAR, 1); // Default to year 1
579
}
580         return year;
581     }
582
583     /**
584      * Override Calendar to compute several fields specific to the Islamic
585      * calendar system. These are:
586      *
587      * <ul><li>ERA
588      * <li>YEAR
589      * <li>MONTH
590      * <li>DAY_OF_MONTH
591      * <li>DAY_OF_YEAR
592      * <li>EXTENDED_YEAR</ul>
593      *
594      * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
595      * method is called. The getGregorianXxx() methods return Gregorian
596      * calendar equivalents for the given Julian day.
597      * @stable ICU 2.8
598      */

599     protected void handleComputeFields(int julianDay) {
600         int year, month, dayOfMonth, dayOfYear;
601         long monthStart;
602         long days = julianDay - 1948440;
603
604         if (civil) {
605             // Use the civil calendar approximation, which is just arithmetic
606
year = (int)Math.floor( (30 * days + 10646) / 10631.0 );
607             month = (int)Math.ceil((days - 29 - yearStart(year)) / 29.5 );
608             month = Math.min(month, 11);
609             monthStart = monthStart(year, month);
610         } else {
611             // Guess at the number of elapsed full months since the epoch
612
int months = (int)Math.floor(days / CalendarAstronomer.SYNODIC_MONTH);
613
614             monthStart = (long)Math.floor(months * CalendarAstronomer.SYNODIC_MONTH - 1);
615
616             if ( days - monthStart >= 28 && moonAge(internalGetTimeInMillis()) > 0) {
617                 // If we're near the end of the month, assume next month and search backwards
618
months++;
619             }
620
621             // Find out the last time that the new moon was actually visible at this longitude
622
// This returns midnight the night that the moon was visible at sunset.
623
while ((monthStart = trueMonthStart(months)) > days) {
624                 // If it was after the date in question, back up a month and try again
625
months--;
626             }
627
628             year = months / 12 + 1;
629             month = months % 12;
630         }
631
632         dayOfMonth = (int)(days - monthStart(year, month)) + 1;
633
634         // Now figure out the day of the year.
635
dayOfYear = (int)(days - monthStart(year, 0) + 1);
636
637         internalSet(ERA, 0);
638         internalSet(YEAR, year);
639         internalSet(EXTENDED_YEAR, year);
640         internalSet(MONTH, month);
641         internalSet(DAY_OF_MONTH, dayOfMonth);
642         internalSet(DAY_OF_YEAR, dayOfYear);
643     }
644
645     /**
646      * Return the current Calendar type.
647      * @return type of calendar (gregorian, etc.)
648      * @internal ICU 3.0
649      * @deprecated This API is ICU internal only.
650      */

651     public String JavaDoc getType() {
652         return "islamic";
653     }
654
655     /*
656     private static CalendarFactory factory;
657     public static CalendarFactory factory() {
658         if (factory == null) {
659             factory = new CalendarFactory() {
660                 public Calendar create(TimeZone tz, ULocale loc) {
661                     return new IslamicCalendar(tz, loc);
662                 }
663
664                 public String factoryName() {
665                     return "Islamic";
666                 }
667             };
668         }
669         return factory;
670     }
671     */

672 }
673
Popular Tags