EMMA Coverage Report (generated Tue Oct 28 00:01:11 GMT 2008)
[all classes][org.joda.time.chrono]

COVERAGE SUMMARY FOR SOURCE FILE [IslamicChronology.java]

nameclass, %method, %block, %line, %
IslamicChronology.java100% (2/2)76%  (26/34)77%  (431/561)78%  (98.3/126)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class IslamicChronology$LeapYearPatternType100% (1/1)67%  (2/3)66%  (25/38)50%  (6/12)
readResolve (): Object 0%   (0/1)0%   (0/13)0%   (0/6)
IslamicChronology$LeapYearPatternType (int, int): void 100% (1/1)100% (10/10)100% (4/4)
isLeapYear (int): boolean 100% (1/1)100% (15/15)100% (2/2)
     
class IslamicChronology100% (1/1)77%  (24/31)78%  (406/523)81%  (92.3/114)
getApproxMillisAtEpochDividedByTwo (): long 0%   (0/1)0%   (0/2)0%   (0/1)
getAverageMillisPerYearDividedByTwo (): long 0%   (0/1)0%   (0/2)0%   (0/1)
getDaysInMonthMax (int): int 0%   (0/1)0%   (0/14)0%   (0/3)
getLeapYearPatternType (): IslamicChronology$LeapYearPatternType 0%   (0/1)0%   (0/3)0%   (0/1)
getYearDifference (long, long): long 0%   (0/1)0%   (0/32)0%   (0/8)
hashCode (): int 0%   (0/1)0%   (0/9)0%   (0/1)
readResolve (): Object 0%   (0/1)0%   (0/11)0%   (0/2)
calculateFirstDayOfYearMillis (int): long 100% (1/1)57%  (43/75)82%  (9/11)
setYear (long, int): long 100% (1/1)85%  (29/34)78%  (7/9)
getInstance (DateTimeZone, IslamicChronology$LeapYearPatternType): IslamicChr... 100% (1/1)95%  (86/91)97%  (17.4/18)
getYear (long): int 100% (1/1)96%  (45/47)99%  (8.9/9)
<static initializer> 100% (1/1)100% (37/37)100% (8/8)
IslamicChronology (Chronology, Object, IslamicChronology$LeapYearPatternType)... 100% (1/1)100% (9/9)100% (3/3)
assemble (AssembledChronology$Fields): void 100% (1/1)100% (22/22)100% (6/6)
getAverageMillisPerMonth (): long 100% (1/1)100% (2/2)100% (1/1)
getAverageMillisPerYear (): long 100% (1/1)100% (2/2)100% (1/1)
getDayOfMonth (long): int 100% (1/1)100% (19/19)100% (4/4)
getDaysInMonthMax (): int 100% (1/1)100% (2/2)100% (1/1)
getDaysInYear (int): int 100% (1/1)100% (8/8)100% (1/1)
getDaysInYearMax (): int 100% (1/1)100% (2/2)100% (1/1)
getDaysInYearMonth (int, int): int 100% (1/1)100% (18/18)100% (3/3)
getInstance (): IslamicChronology 100% (1/1)100% (4/4)100% (1/1)
getInstance (DateTimeZone): IslamicChronology 100% (1/1)100% (4/4)100% (1/1)
getInstanceUTC (): IslamicChronology 100% (1/1)100% (2/2)100% (1/1)
getMaxYear (): int 100% (1/1)100% (2/2)100% (1/1)
getMinYear (): int 100% (1/1)100% (2/2)100% (1/1)
getMonthOfYear (long, int): int 100% (1/1)100% (22/22)100% (4/4)
getTotalMillisByYearMonth (int, int): long 100% (1/1)100% (26/26)100% (5/5)
isLeapYear (int): boolean 100% (1/1)100% (5/5)100% (1/1)
withUTC (): Chronology 100% (1/1)100% (2/2)100% (1/1)
withZone (DateTimeZone): Chronology 100% (1/1)100% (13/13)100% (5/5)

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 */
16package org.joda.time.chrono;
17 
18import java.io.Serializable;
19import java.util.HashMap;
20import java.util.Map;
21 
22import org.joda.time.Chronology;
23import org.joda.time.DateTime;
24import org.joda.time.DateTimeConstants;
25import org.joda.time.DateTimeField;
26import 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 */
66public 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 cCache = new HashMap();
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 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}

[all classes][org.joda.time.chrono]
EMMA 2.0.5312 (C) Vladimir Roubtsov