View Javadoc

1   /*
2    *  Copyright 2001-2009 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;
19  import java.util.HashMap;
20  import java.util.Map;
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<DateTimeZone, IslamicChronology[]> cCache = new HashMap<DateTimeZone, IslamicChronology[]>();
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 = 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 param, LeapYearPatternType leapYears) {
229         super(base, param, 4);
230         this.iLeapYears = leapYears;
231     }
232 
233     /**
234      * Serialization singleton.
235      */
236     private Object 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      * A suitable hash code for the chronology.
280      * 
281      * @return the hash code
282      * @since 1.6
283      */
284     public int hashCode() {
285         return super.hashCode() * 13 + getLeapYearPatternType().hashCode();
286     }
287 
288     //-----------------------------------------------------------------------
289     int getYear(long instant) {
290         long millisIslamic = instant - MILLIS_YEAR_1;
291         long cycles = millisIslamic / MILLIS_PER_CYCLE;
292         long cycleRemainder = millisIslamic % MILLIS_PER_CYCLE;
293         
294         int year = (int) ((cycles * CYCLE) + 1L);
295         long yearMillis = (isLeapYear(year) ? MILLIS_PER_LONG_YEAR : MILLIS_PER_SHORT_YEAR);
296         while (cycleRemainder >= yearMillis) {
297             cycleRemainder -= yearMillis;
298             yearMillis = (isLeapYear(++year) ? MILLIS_PER_LONG_YEAR : MILLIS_PER_SHORT_YEAR);
299         }
300         return year;
301     }
302 
303     long setYear(long instant, int year) {
304         // optimsed implementation of set, due to fixed months
305         int thisYear = getYear(instant);
306         int dayOfYear = getDayOfYear(instant, thisYear);
307         int millisOfDay = getMillisOfDay(instant);
308 
309         if (dayOfYear > 354) {
310             // Current year is leap, and day is leap.
311             if (!isLeapYear(year)) {
312                 // Moving to a non-leap year, leap day doesn't exist.
313                 dayOfYear--;
314             }
315         }
316 
317         instant = getYearMonthDayMillis(year, 1, dayOfYear);
318         instant += millisOfDay;
319         return instant;
320     }
321 
322     //-----------------------------------------------------------------------
323     long getYearDifference(long minuendInstant, long subtrahendInstant) {
324         // optimsed implementation of getDifference, due to fixed months
325         int minuendYear = getYear(minuendInstant);
326         int subtrahendYear = getYear(subtrahendInstant);
327 
328         // Inlined remainder method to avoid duplicate calls to get.
329         long minuendRem = minuendInstant - getYearMillis(minuendYear);
330         long subtrahendRem = subtrahendInstant - getYearMillis(subtrahendYear);
331 
332         int difference = minuendYear - subtrahendYear;
333         if (minuendRem < subtrahendRem) {
334             difference--;
335         }
336         return difference;
337     }
338 
339     //-----------------------------------------------------------------------
340     long getTotalMillisByYearMonth(int year, int month) {
341         if (--month % 2 == 1) {
342             month /= 2;
343             return month * MILLIS_PER_MONTH_PAIR + MILLIS_PER_LONG_MONTH;
344         } else {
345             month /= 2;
346             return month * MILLIS_PER_MONTH_PAIR;
347         }
348     }
349 
350     //-----------------------------------------------------------------------
351     int getDayOfMonth(long millis) {
352         // optimised for simple months
353         int doy = getDayOfYear(millis) - 1;
354         if (doy == 354) {
355             return 30;
356         }
357         return (doy % MONTH_PAIR_LENGTH) % LONG_MONTH_LENGTH + 1;
358     }
359 
360     //-----------------------------------------------------------------------
361     boolean isLeapYear(int year) {
362         return iLeapYears.isLeapYear(year);
363     }
364 
365     //-----------------------------------------------------------------------
366     int getDaysInYearMax() {
367         return 355;
368     }
369 
370     //-----------------------------------------------------------------------
371     int getDaysInYear(int year) {
372         return isLeapYear(year) ? 355 : 354;
373     }
374 
375     //-----------------------------------------------------------------------
376     int getDaysInYearMonth(int year, int month) {
377         if (month == 12 && isLeapYear(year)) {
378             return LONG_MONTH_LENGTH;
379         }
380         return (--month % 2 == 0 ? LONG_MONTH_LENGTH : SHORT_MONTH_LENGTH);
381     }
382 
383     //-----------------------------------------------------------------------
384     int getDaysInMonthMax() {
385         return LONG_MONTH_LENGTH;
386     }
387 
388     //-----------------------------------------------------------------------
389     int getDaysInMonthMax(int month) {
390         if (month == 12) {
391             return LONG_MONTH_LENGTH;
392         }
393         return (--month % 2 == 0 ? LONG_MONTH_LENGTH : SHORT_MONTH_LENGTH);
394     }
395 
396     //-----------------------------------------------------------------------
397     int getMonthOfYear(long millis, int year) {
398         int doyZeroBased = (int) ((millis - getYearMillis(year)) / DateTimeConstants.MILLIS_PER_DAY);
399         if (doyZeroBased == 354) {
400             return 12;
401         }
402         return ((doyZeroBased * 2) / MONTH_PAIR_LENGTH) + 1;
403 //        return (int) (doyZeroBased / 29.9f) + 1;
404 //        
405 //        int monthPairZeroBased = doyZeroBased / MONTH_PAIR_LENGTH;
406 //        int monthPairRemainder = doyZeroBased % MONTH_PAIR_LENGTH;
407 //        return (monthPairZeroBased * 2) + 1 + (monthPairRemainder >= LONG_MONTH_LENGTH ? 1 : 0);
408     }
409 
410     //-----------------------------------------------------------------------
411     long getAverageMillisPerYear() {
412         return MILLIS_PER_YEAR;
413     }
414 
415     //-----------------------------------------------------------------------
416     long getAverageMillisPerYearDividedByTwo() {
417         return MILLIS_PER_YEAR / 2;
418     }
419 
420     //-----------------------------------------------------------------------
421     long getAverageMillisPerMonth() {
422         return MILLIS_PER_MONTH;
423     }
424 
425     //-----------------------------------------------------------------------
426     long calculateFirstDayOfYearMillis(int year) {
427         if (year > MAX_YEAR) {
428             throw new ArithmeticException("Year is too large: " + year + " > " + MAX_YEAR);
429         }
430         if (year < MIN_YEAR) {
431             throw new ArithmeticException("Year is too small: " + year + " < " + MIN_YEAR);
432         }
433 
434         // Java epoch is 1970-01-01 Gregorian which is 0622-07-16 Islamic.
435         // 0001-01-01 Islamic is -42520809600000L
436         // would prefer to calculate against year zero, but leap year
437         // can be in that year so it doesn't work
438         year--;
439         long cycle = year / CYCLE;
440         long millis = MILLIS_YEAR_1 + cycle * MILLIS_PER_CYCLE;
441         int cycleRemainder = (year % CYCLE) + 1;
442         
443         for (int i = 1; i < cycleRemainder; i++) {
444             millis += (isLeapYear(i) ? MILLIS_PER_LONG_YEAR : MILLIS_PER_SHORT_YEAR);
445         }
446         
447         return millis;
448     }
449 
450     //-----------------------------------------------------------------------
451     int getMinYear() {
452         return 1; //MIN_YEAR;
453     }
454 
455     //-----------------------------------------------------------------------
456     int getMaxYear() {
457         return MAX_YEAR;
458     }
459 
460     //-----------------------------------------------------------------------
461     long getApproxMillisAtEpochDividedByTwo() {
462         // Epoch 1970-01-01 ISO = 1389-10-22 Islamic
463         return (-MILLIS_YEAR_1) / 2;
464     }
465 
466     //-----------------------------------------------------------------------
467     protected void assemble(Fields fields) {
468         if (getBase() == null) {
469             super.assemble(fields);
470 
471             fields.era = ERA_FIELD;
472             fields.monthOfYear = new BasicMonthOfYearDateTimeField(this, 12);
473             fields.months = fields.monthOfYear.getDurationField();
474         }
475     }
476 
477     //-----------------------------------------------------------------------
478     /**
479      * Opaque object describing a leap year pattern for the Islamic Chronology.
480      *
481      * @since 1.2
482      */
483     public static class LeapYearPatternType implements Serializable {
484         /** Serialization lock */
485         private static final long serialVersionUID = 26581275372698L;
486 //        /** Leap year raw data encoded into bits. */
487 //        private static final int[][] LEAP_YEARS = {
488 //            {2, 5, 7, 10, 13, 15, 18, 21, 24, 26, 29},  // 623158436
489 //            {2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29},  // 623191204
490 //            {2, 5, 8, 10, 13, 16, 19, 21, 24, 27, 29},  // 690562340
491 //            {0, 2, 5, 8, 11, 13, 16, 19, 21, 24, 27},   // 153692453
492 //        };
493         
494         /** The index. */
495         final byte index;
496         /** The leap year pattern, a bit-based 1=true pattern. */
497         final int pattern;
498         
499         /**
500          * Constructor.
501          * This constructor takes a bit pattern where bits 0-29 correspond
502          * to years 0-29 in the 30 year Islamic cycle of years. This allows
503          * a highly efficient lookup by bit-matching.
504          *
505          * @param index  the index
506          * @param pattern  the bit pattern
507          */
508         LeapYearPatternType(int index, int pattern) {
509             super();
510             this.index = (byte) index;
511             this.pattern = pattern;
512         }
513         
514         /**
515          * Is the year a leap year.
516          * @param year  the year to query
517          * @return true if leap
518          */
519         boolean isLeapYear(int year) {
520             int key = 1 << (year % 30);
521             return ((pattern & key) > 0);
522         }
523         
524         /**
525          * Ensure a singleton is returned if possible.
526          * @return the singleton instance
527          */
528         private Object readResolve() {
529             switch (index) {
530                 case 0:
531                     return LEAP_YEAR_15_BASED;
532                 case 1:
533                     return LEAP_YEAR_16_BASED;
534                 case 2:
535                     return LEAP_YEAR_INDIAN;
536                 case 3:
537                     return LEAP_YEAR_HABASH_AL_HASIB;
538                 default:
539                     return this;
540             }
541         }
542     }
543 }