KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > joda > time > chrono > IslamicChronology


1 /*
2  * Copyright 2001-2005 Stephen Colebourne
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.joda.time.chrono;
17
18 import java.io.Serializable JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.Map JavaDoc;
21
22 import org.joda.time.Chronology;
23 import org.joda.time.DateTime;
24 import org.joda.time.DateTimeConstants;
25 import org.joda.time.DateTimeField;
26 import org.joda.time.DateTimeZone;
27
28 /**
29  * Implements the Islamic, or Hijri, calendar system using arithmetic rules.
30  * <p>
31  * This calendar is a lunar calendar with a shorter year than ISO.
32  * Year 1 in the Islamic calendar began on July 16, 622 CE (Julian), thus
33  * Islamic years do not begin at the same time as Julian years. This chronology
34  * is not proleptic, as it does not allow dates before the first Islamic year.
35  * <p>
36  * There are two basic forms of the Islamic calendar, the tabular and the
37  * observed. The observed form cannot easily be used by computers as it
38  * relies on human observation of the new moon.
39  * The tabular calendar, implemented here, is an arithmetical approximation
40  * of the observed form that follows relatively simple rules.
41  * <p>
42  * The tabular form of the calendar defines 12 months of alternately
43  * 30 and 29 days. The last month is extended to 30 days in a leap year.
44  * Leap years occur according to a 30 year cycle. There are four recognised
45  * patterns of leap years in the 30 year cycle:
46  * <pre>
47  * Years 2, 5, 7, 10, 13, 15, 18, 21, 24, 26 & 29 - 15-based, used by Microsoft
48  * Years 2, 5, 7, 10, 13, 16, 18, 21, 24, 26 & 29 - 16-based, most commonly used
49  * Years 2, 5, 8, 10, 13, 16, 19, 21, 24, 27 & 29 - Indian
50  * Years 2, 5, 8, 11, 13, 16, 19, 21, 24, 27 & 30 - Habash al-Hasib
51  * </pre>
52  * You can select which pattern to use via the factory methods, or use the
53  * default (16-based).
54  * <p>
55  * This implementation defines a day as midnight to midnight exactly as per
56  * the ISO chronology. This correct start of day is at sunset on the previous
57  * day, however this cannot readily be modelled and has been ignored.
58  * <p>
59  * IslamicChronology is thread-safe and immutable.
60  *
61  * @see <a HREF="http://en.wikipedia.org/wiki/Islamic_calendar">Wikipedia</a>
62  *
63  * @author Stephen Colebourne
64  * @since 1.2
65  */

66 public final class IslamicChronology extends BasicChronology {
67
68     /** Serialization lock */
69     private static final long serialVersionUID = -3663823829888L;
70
71     /**
72      * Constant value for 'Anno Hegirae', equivalent
73      * to the value returned for AD/CE.
74      */

75     public static final int AH = DateTimeConstants.CE;
76
77     /** A singleton era field. */
78     private static final DateTimeField ERA_FIELD = new BasicSingleEraDateTimeField("AH");
79
80     /** Leap year 15-based pattern. */
81     public static final LeapYearPatternType LEAP_YEAR_15_BASED = new LeapYearPatternType(0, 623158436);
82     /** Leap year 16-based pattern. */
83     public static final LeapYearPatternType LEAP_YEAR_16_BASED = new LeapYearPatternType(1, 623191204);
84     /** Leap year Indian pattern. */
85     public static final LeapYearPatternType LEAP_YEAR_INDIAN = new LeapYearPatternType(2, 690562340);
86     /** Leap year Habash al-Hasib pattern. */
87     public static final LeapYearPatternType LEAP_YEAR_HABASH_AL_HASIB = new LeapYearPatternType(3, 153692453);
88
89     /** The lowest year that can be fully supported. */
90     private static final int MIN_YEAR = -292269337;
91
92     /**
93      * The highest year that can be fully supported.
94      * Although calculateFirstDayOfYearMillis can go higher without
95      * overflowing, the getYear method overflows when it adds the
96      * approximate millis at the epoch.
97      */

98     private static final int MAX_YEAR = 292271022;
99
100     /** The days in a pair of months. */
101     private static final int MONTH_PAIR_LENGTH = 59;
102
103     /** The length of the long month. */
104     private static final int LONG_MONTH_LENGTH = 30;
105
106     /** The length of the short month. */
107     private static final int SHORT_MONTH_LENGTH = 29;
108
109     /** The length of the long month in millis. */
110     private static final long MILLIS_PER_MONTH_PAIR = 59L * DateTimeConstants.MILLIS_PER_DAY;
111
112     /** The length of the long month in millis. */
113     private static final long MILLIS_PER_MONTH = (long) (29.53056 * DateTimeConstants.MILLIS_PER_DAY);
114
115     /** The length of the long month in millis. */
116     private static final long MILLIS_PER_LONG_MONTH = 30L * DateTimeConstants.MILLIS_PER_DAY;
117
118     /** The typical millis per year. */
119     private static final long MILLIS_PER_YEAR = (long) (354.36667 * DateTimeConstants.MILLIS_PER_DAY);
120
121     /** The typical millis per year. */
122     private static final long MILLIS_PER_SHORT_YEAR = 354L * DateTimeConstants.MILLIS_PER_DAY;
123
124     /** The typical millis per year. */
125     private static final long MILLIS_PER_LONG_YEAR = 355L * DateTimeConstants.MILLIS_PER_DAY;
126
127     /** The millis of 0001-01-01. */
128     private static final long MILLIS_YEAR_1 = -42521587200000L;
129                                     // -42520809600000L;
130
// long start = 0L - 278L * DateTimeConstants.MILLIS_PER_DAY;
131
// long cy = 46L * MILLIS_PER_CYCLE; // 1381-01-01
132
// long rem = 5L * MILLIS_PER_SHORT_YEAR +
133
// 3L * MILLIS_PER_LONG_YEAR; // 1389-01-01
134

135     /** The length of the cycle of leap years. */
136     private static final int CYCLE = 30;
137
138     /** The millis of a 30 year cycle. */
139     private static final long MILLIS_PER_CYCLE = ((19L * 354L + 11L * 355L) * DateTimeConstants.MILLIS_PER_DAY);
140
141     /** Cache of zone to chronology arrays */
142     private static final Map JavaDoc cCache = new HashMap JavaDoc();
143
144     /** Singleton instance of a UTC IslamicChronology */
145     private static final IslamicChronology INSTANCE_UTC;
146     static {
147         // init after static fields
148
INSTANCE_UTC = getInstance(DateTimeZone.UTC);
149     }
150
151     /** The leap years to use. */
152     private final LeapYearPatternType iLeapYears;
153
154     //-----------------------------------------------------------------------
155
/**
156      * Gets an instance of the IslamicChronology.
157      * The time zone of the returned instance is UTC.
158      *
159      * @return a singleton UTC instance of the chronology
160      */

161     public static IslamicChronology getInstanceUTC() {
162         return INSTANCE_UTC;
163     }
164
165     /**
166      * Gets an instance of the IslamicChronology in the default time zone.
167      *
168      * @return a chronology in the default time zone
169      */

170     public static IslamicChronology getInstance() {
171         return getInstance(DateTimeZone.getDefault(), LEAP_YEAR_16_BASED);
172     }
173
174     /**
175      * Gets an instance of the IslamicChronology in the given time zone.
176      *
177      * @param zone the time zone to get the chronology in, null is default
178      * @return a chronology in the specified time zone
179      */

180     public static IslamicChronology getInstance(DateTimeZone zone) {
181         return getInstance(zone, LEAP_YEAR_16_BASED);
182     }
183
184     /**
185      * Gets an instance of the IslamicChronology in the given time zone.
186      *
187      * @param zone the time zone to get the chronology in, null is default
188      * @param leapYears the type defining the leap year pattern
189      * @return a chronology in the specified time zone
190      */

191     public static IslamicChronology getInstance(DateTimeZone zone, LeapYearPatternType leapYears) {
192         if (zone == null) {
193             zone = DateTimeZone.getDefault();
194         }
195         IslamicChronology chrono;
196         synchronized (cCache) {
197             IslamicChronology[] chronos = (IslamicChronology[]) cCache.get(zone);
198             if (chronos == null) {
199                 chronos = new IslamicChronology[4];
200                 cCache.put(zone, chronos);
201             }
202             chrono = chronos[leapYears.index];
203             if (chrono == null) {
204                 if (zone == DateTimeZone.UTC) {
205                     // First create without a lower limit.
206
chrono = new IslamicChronology(null, null, leapYears);
207                     // Impose lower limit and make another IslamicChronology.
208
DateTime lowerLimit = new DateTime(1, 1, 1, 0, 0, 0, 0, chrono);
209                     chrono = new IslamicChronology(
210                         LimitChronology.getInstance(chrono, lowerLimit, null),
211                          null, leapYears);
212                 } else {
213                     chrono = getInstance(DateTimeZone.UTC, leapYears);
214                     chrono = new IslamicChronology
215                         (ZonedChronology.getInstance(chrono, zone), null, leapYears);
216                 }
217                 chronos[leapYears.index] = chrono;
218             }
219         }
220         return chrono;
221     }
222
223     // Constructors and instance variables
224
//-----------------------------------------------------------------------
225
/**
226      * Restricted constructor.
227      */

228     IslamicChronology(Chronology base, Object JavaDoc param, LeapYearPatternType leapYears) {
229         super(base, param, 4);
230         this.iLeapYears = leapYears;
231     }
232
233     /**
234      * Serialization singleton.
235      */

236     private Object JavaDoc readResolve() {
237         Chronology base = getBase();
238         return base == null ? getInstanceUTC() : getInstance(base.getZone());
239     }
240
241     //-----------------------------------------------------------------------
242
/**
243      * Gets the leap year pattern type.
244      *
245      * @return the pattern type
246      */

247     public LeapYearPatternType getLeapYearPatternType() {
248         return iLeapYears;
249     }
250
251     // Conversion
252
//-----------------------------------------------------------------------
253
/**
254      * Gets the Chronology in the UTC time zone.
255      *
256      * @return the chronology in UTC
257      */

258     public Chronology withUTC() {
259         return INSTANCE_UTC;
260     }
261
262     /**
263      * Gets the Chronology in a specific time zone.
264      *
265      * @param zone the zone to get the chronology in, null is default
266      * @return the chronology
267      */

268     public Chronology withZone(DateTimeZone zone) {
269         if (zone == null) {
270             zone = DateTimeZone.getDefault();
271         }
272         if (zone == getZone()) {
273             return this;
274         }
275         return getInstance(zone);
276     }
277
278     //-----------------------------------------------------------------------
279
int getYear(long instant) {
280         long millisIslamic = instant - MILLIS_YEAR_1;
281         long cycles = millisIslamic / MILLIS_PER_CYCLE;
282         long cycleRemainder = millisIslamic % MILLIS_PER_CYCLE;
283         
284         int year = (int) ((cycles * CYCLE) + 1L);
285         long yearMillis = (isLeapYear(year) ? MILLIS_PER_LONG_YEAR : MILLIS_PER_SHORT_YEAR);
286         while (cycleRemainder >= yearMillis) {
287             cycleRemainder -= yearMillis;
288             yearMillis = (isLeapYear(++year) ? MILLIS_PER_LONG_YEAR : MILLIS_PER_SHORT_YEAR);
289         }
290         return year;
291     }
292
293     long setYear(long instant, int year) {
294         // optimsed implementation of set, due to fixed months
295
int thisYear = getYear(instant);
296         int dayOfYear = getDayOfYear(instant, thisYear);
297         int millisOfDay = getMillisOfDay(instant);
298
299         if (dayOfYear > 354) {
300             // Current year is leap, and day is leap.
301
if (!isLeapYear(year)) {
302                 // Moving to a non-leap year, leap day doesn't exist.
303
dayOfYear--;
304             }
305         }
306
307         instant = getYearMonthDayMillis(year, 1, dayOfYear);
308         instant += millisOfDay;
309         return instant;
310     }
311
312     //-----------------------------------------------------------------------
313
long getYearDifference(long minuendInstant, long subtrahendInstant) {
314         // optimsed implementation of getDifference, due to fixed months
315
int minuendYear = getYear(minuendInstant);
316         int subtrahendYear = getYear(subtrahendInstant);
317
318         // Inlined remainder method to avoid duplicate calls to get.
319
long minuendRem = minuendInstant - getYearMillis(minuendYear);
320         long subtrahendRem = subtrahendInstant - getYearMillis(subtrahendYear);
321
322         int difference = minuendYear - subtrahendYear;
323         if (minuendRem < subtrahendRem) {
324             difference--;
325         }
326         return difference;
327     }
328
329     //-----------------------------------------------------------------------
330
long getTotalMillisByYearMonth(int year, int month) {
331         if (--month % 2 == 1) {
332             month /= 2;
333             return month * MILLIS_PER_MONTH_PAIR + MILLIS_PER_LONG_MONTH;
334         } else {
335             month /= 2;
336             return month * MILLIS_PER_MONTH_PAIR;
337         }
338     }
339
340     //-----------------------------------------------------------------------
341
int getDayOfMonth(long millis) {
342         // optimised for simple months
343
int doy = getDayOfYear(millis) - 1;
344         if (doy == 354) {
345             return 30;
346         }
347         return (doy % MONTH_PAIR_LENGTH) % LONG_MONTH_LENGTH + 1;
348     }
349
350     //-----------------------------------------------------------------------
351
boolean isLeapYear(int year) {
352         return iLeapYears.isLeapYear(year);
353     }
354
355     //-----------------------------------------------------------------------
356
int getDaysInYearMax() {
357         return 355;
358     }
359
360     //-----------------------------------------------------------------------
361
int getDaysInYear(int year) {
362         return isLeapYear(year) ? 355 : 354;
363     }
364
365     //-----------------------------------------------------------------------
366
int getDaysInYearMonth(int year, int month) {
367         if (month == 12 && isLeapYear(year)) {
368             return LONG_MONTH_LENGTH;
369         }
370         return (--month % 2 == 0 ? LONG_MONTH_LENGTH : SHORT_MONTH_LENGTH);
371     }
372
373     //-----------------------------------------------------------------------
374
int getDaysInMonthMax() {
375         return LONG_MONTH_LENGTH;
376     }
377
378     //-----------------------------------------------------------------------
379
int getDaysInMonthMax(int month) {
380         if (month == 12) {
381             return LONG_MONTH_LENGTH;
382         }
383         return (--month % 2 == 0 ? LONG_MONTH_LENGTH : SHORT_MONTH_LENGTH);
384     }
385
386     //-----------------------------------------------------------------------
387
int getMonthOfYear(long millis, int year) {
388         int doyZeroBased = (int) ((millis - getYearMillis(year)) / DateTimeConstants.MILLIS_PER_DAY);
389         if (doyZeroBased == 354) {
390             return 12;
391         }
392         return ((doyZeroBased * 2) / MONTH_PAIR_LENGTH) + 1;
393 // return (int) (doyZeroBased / 29.9f) + 1;
394
//
395
// int monthPairZeroBased = doyZeroBased / MONTH_PAIR_LENGTH;
396
// int monthPairRemainder = doyZeroBased % MONTH_PAIR_LENGTH;
397
// return (monthPairZeroBased * 2) + 1 + (monthPairRemainder >= LONG_MONTH_LENGTH ? 1 : 0);
398
}
399
400     //-----------------------------------------------------------------------
401
long getAverageMillisPerYear() {
402         return MILLIS_PER_YEAR;
403     }
404
405     //-----------------------------------------------------------------------
406
long getAverageMillisPerYearDividedByTwo() {
407         return MILLIS_PER_YEAR / 2;
408     }
409
410     //-----------------------------------------------------------------------
411
long getAverageMillisPerMonth() {
412         return MILLIS_PER_MONTH;
413     }
414
415     //-----------------------------------------------------------------------
416
long calculateFirstDayOfYearMillis(int year) {
417         if (year > MAX_YEAR) {
418             throw new ArithmeticException JavaDoc("Year is too large: " + year + " > " + MAX_YEAR);
419         }
420         if (year < MIN_YEAR) {
421             throw new ArithmeticException JavaDoc("Year is too small: " + year + " < " + MIN_YEAR);
422         }
423
424         // Java epoch is 1970-01-01 Gregorian which is 0622-07-16 Islamic.
425
// 0001-01-01 Islamic is -42520809600000L
426
// would prefer to calculate against year zero, but leap year
427
// can be in that year so it doesn't work
428
year--;
429         long cycle = year / CYCLE;
430         long millis = MILLIS_YEAR_1 + cycle * MILLIS_PER_CYCLE;
431         int cycleRemainder = (year % CYCLE) + 1;
432         
433         for (int i = 1; i < cycleRemainder; i++) {
434             millis += (isLeapYear(i) ? MILLIS_PER_LONG_YEAR : MILLIS_PER_SHORT_YEAR);
435         }
436         
437         return millis;
438     }
439
440     //-----------------------------------------------------------------------
441
int getMinYear() {
442         return 1; //MIN_YEAR;
443
}
444
445     //-----------------------------------------------------------------------
446
int getMaxYear() {
447         return MAX_YEAR;
448     }
449
450     //-----------------------------------------------------------------------
451
long getApproxMillisAtEpochDividedByTwo() {
452         // Epoch 1970-01-01 ISO = 1389-10-22 Islamic
453
return (-MILLIS_YEAR_1) / 2;
454     }
455
456     //-----------------------------------------------------------------------
457
protected void assemble(Fields fields) {
458         if (getBase() == null) {
459             super.assemble(fields);
460
461             fields.era = ERA_FIELD;
462             fields.monthOfYear = new BasicMonthOfYearDateTimeField(this, 12);
463             fields.months = fields.monthOfYear.getDurationField();
464         }
465     }
466
467     //-----------------------------------------------------------------------
468
/**
469      * Opaque object describing a leap year pattern for the Islamic Chronology.
470      *
471      * @since 1.2
472      */

473     public static class LeapYearPatternType implements Serializable JavaDoc {
474         /** Serialization lock */
475         private static final long serialVersionUID = 26581275372698L;
476 // /** Leap year raw data encoded into bits. */
477
// private static final int[][] LEAP_YEARS = {
478
// {2, 5, 7, 10, 13, 15, 18, 21, 24, 26, 29}, // 623158436
479
// {2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29}, // 623191204
480
// {2, 5, 8, 10, 13, 16, 19, 21, 24, 27, 29}, // 690562340
481
// {0, 2, 5, 8, 11, 13, 16, 19, 21, 24, 27}, // 153692453
482
// };
483

484         /** The index. */
485         final byte index;
486         /** The leap year pattern, a bit-based 1=true pattern. */
487         final int pattern;
488         
489         /**
490          * Constructor.
491          * This constructor takes a bit pattern where bits 0-29 correspond
492          * to years 0-29 in the 30 year Islamic cycle of years. This allows
493          * a highly efficient lookup by bit-matching.
494          *
495          * @param index the index
496          * @param pattern the bit pattern
497          */

498         LeapYearPatternType(int index, int pattern) {
499             super();
500             this.index = (byte) index;
501             this.pattern = pattern;
502         }
503         
504         /**
505          * Is the year a leap year.
506          * @param year the year to query
507          * @return true if leap
508          */

509         boolean isLeapYear(int year) {
510             int key = 1 << (year % 30);
511             return ((pattern & key) > 0);
512         }
513         
514         /**
515          * Ensure a singleton is returned if possible.
516          * @return the singleton instance
517          */

518         private Object JavaDoc readResolve() {
519             switch (index) {
520                 case 0:
521                     return LEAP_YEAR_15_BASED;
522                 case 1:
523                     return LEAP_YEAR_16_BASED;
524                 case 2:
525                     return LEAP_YEAR_INDIAN;
526                 case 3:
527                     return LEAP_YEAR_HABASH_AL_HASIB;
528                 default:
529                     return this;
530             }
531         }
532     }
533 }
534
Popular Tags