001: /*
002: * Copyright 2001-2005 Stephen Colebourne
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.joda.time.chrono;
017:
018: import java.io.Serializable;
019: import java.util.HashMap;
020: import java.util.Map;
021:
022: import org.joda.time.Chronology;
023: import org.joda.time.DateTime;
024: import org.joda.time.DateTimeConstants;
025: import org.joda.time.DateTimeField;
026: import org.joda.time.DateTimeZone;
027:
028: /**
029: * Implements the Islamic, or Hijri, calendar system using arithmetic rules.
030: * <p>
031: * This calendar is a lunar calendar with a shorter year than ISO.
032: * Year 1 in the Islamic calendar began on July 16, 622 CE (Julian), thus
033: * Islamic years do not begin at the same time as Julian years. This chronology
034: * is not proleptic, as it does not allow dates before the first Islamic year.
035: * <p>
036: * There are two basic forms of the Islamic calendar, the tabular and the
037: * observed. The observed form cannot easily be used by computers as it
038: * relies on human observation of the new moon.
039: * The tabular calendar, implemented here, is an arithmetical approximation
040: * of the observed form that follows relatively simple rules.
041: * <p>
042: * The tabular form of the calendar defines 12 months of alternately
043: * 30 and 29 days. The last month is extended to 30 days in a leap year.
044: * Leap years occur according to a 30 year cycle. There are four recognised
045: * patterns of leap years in the 30 year cycle:
046: * <pre>
047: * Years 2, 5, 7, 10, 13, 15, 18, 21, 24, 26 & 29 - 15-based, used by Microsoft
048: * Years 2, 5, 7, 10, 13, 16, 18, 21, 24, 26 & 29 - 16-based, most commonly used
049: * Years 2, 5, 8, 10, 13, 16, 19, 21, 24, 27 & 29 - Indian
050: * Years 2, 5, 8, 11, 13, 16, 19, 21, 24, 27 & 30 - Habash al-Hasib
051: * </pre>
052: * You can select which pattern to use via the factory methods, or use the
053: * default (16-based).
054: * <p>
055: * This implementation defines a day as midnight to midnight exactly as per
056: * the ISO chronology. This correct start of day is at sunset on the previous
057: * day, however this cannot readily be modelled and has been ignored.
058: * <p>
059: * IslamicChronology is thread-safe and immutable.
060: *
061: * @see <a href="http://en.wikipedia.org/wiki/Islamic_calendar">Wikipedia</a>
062: *
063: * @author Stephen Colebourne
064: * @since 1.2
065: */
066: public final class IslamicChronology extends BasicChronology {
067:
068: /** Serialization lock */
069: private static final long serialVersionUID = -3663823829888L;
070:
071: /**
072: * Constant value for 'Anno Hegirae', equivalent
073: * to the value returned for AD/CE.
074: */
075: public static final int AH = DateTimeConstants.CE;
076:
077: /** A singleton era field. */
078: private static final DateTimeField ERA_FIELD = new BasicSingleEraDateTimeField(
079: "AH");
080:
081: /** Leap year 15-based pattern. */
082: public static final LeapYearPatternType LEAP_YEAR_15_BASED = new LeapYearPatternType(
083: 0, 623158436);
084: /** Leap year 16-based pattern. */
085: public static final LeapYearPatternType LEAP_YEAR_16_BASED = new LeapYearPatternType(
086: 1, 623191204);
087: /** Leap year Indian pattern. */
088: public static final LeapYearPatternType LEAP_YEAR_INDIAN = new LeapYearPatternType(
089: 2, 690562340);
090: /** Leap year Habash al-Hasib pattern. */
091: public static final LeapYearPatternType LEAP_YEAR_HABASH_AL_HASIB = new LeapYearPatternType(
092: 3, 153692453);
093:
094: /** The lowest year that can be fully supported. */
095: private static final int MIN_YEAR = -292269337;
096:
097: /**
098: * The highest year that can be fully supported.
099: * Although calculateFirstDayOfYearMillis can go higher without
100: * overflowing, the getYear method overflows when it adds the
101: * approximate millis at the epoch.
102: */
103: private static final int MAX_YEAR = 292271022;
104:
105: /** The days in a pair of months. */
106: private static final int MONTH_PAIR_LENGTH = 59;
107:
108: /** The length of the long month. */
109: private static final int LONG_MONTH_LENGTH = 30;
110:
111: /** The length of the short month. */
112: private static final int SHORT_MONTH_LENGTH = 29;
113:
114: /** The length of the long month in millis. */
115: private static final long MILLIS_PER_MONTH_PAIR = 59L * DateTimeConstants.MILLIS_PER_DAY;
116:
117: /** The length of the long month in millis. */
118: private static final long MILLIS_PER_MONTH = (long) (29.53056 * DateTimeConstants.MILLIS_PER_DAY);
119:
120: /** The length of the long month in millis. */
121: private static final long MILLIS_PER_LONG_MONTH = 30L * DateTimeConstants.MILLIS_PER_DAY;
122:
123: /** The typical millis per year. */
124: private static final long MILLIS_PER_YEAR = (long) (354.36667 * DateTimeConstants.MILLIS_PER_DAY);
125:
126: /** The typical millis per year. */
127: private static final long MILLIS_PER_SHORT_YEAR = 354L * DateTimeConstants.MILLIS_PER_DAY;
128:
129: /** The typical millis per year. */
130: private static final long MILLIS_PER_LONG_YEAR = 355L * DateTimeConstants.MILLIS_PER_DAY;
131:
132: /** The millis of 0001-01-01. */
133: private static final long MILLIS_YEAR_1 = -42521587200000L;
134: // -42520809600000L;
135: // long start = 0L - 278L * DateTimeConstants.MILLIS_PER_DAY;
136: // long cy = 46L * MILLIS_PER_CYCLE; // 1381-01-01
137: // long rem = 5L * MILLIS_PER_SHORT_YEAR +
138: // 3L * MILLIS_PER_LONG_YEAR; // 1389-01-01
139:
140: /** The length of the cycle of leap years. */
141: private static final int CYCLE = 30;
142:
143: /** The millis of a 30 year cycle. */
144: private static final long MILLIS_PER_CYCLE = ((19L * 354L + 11L * 355L) * DateTimeConstants.MILLIS_PER_DAY);
145:
146: /** Cache of zone to chronology arrays */
147: private static final Map cCache = new HashMap();
148:
149: /** Singleton instance of a UTC IslamicChronology */
150: private static final IslamicChronology INSTANCE_UTC;
151: static {
152: // init after static fields
153: INSTANCE_UTC = getInstance(DateTimeZone.UTC);
154: }
155:
156: /** The leap years to use. */
157: private final LeapYearPatternType iLeapYears;
158:
159: //-----------------------------------------------------------------------
160: /**
161: * Gets an instance of the IslamicChronology.
162: * The time zone of the returned instance is UTC.
163: *
164: * @return a singleton UTC instance of the chronology
165: */
166: public static IslamicChronology getInstanceUTC() {
167: return INSTANCE_UTC;
168: }
169:
170: /**
171: * Gets an instance of the IslamicChronology in the default time zone.
172: *
173: * @return a chronology in the default time zone
174: */
175: public static IslamicChronology getInstance() {
176: return getInstance(DateTimeZone.getDefault(),
177: LEAP_YEAR_16_BASED);
178: }
179:
180: /**
181: * Gets an instance of the IslamicChronology in the given time zone.
182: *
183: * @param zone the time zone to get the chronology in, null is default
184: * @return a chronology in the specified time zone
185: */
186: public static IslamicChronology getInstance(DateTimeZone zone) {
187: return getInstance(zone, LEAP_YEAR_16_BASED);
188: }
189:
190: /**
191: * Gets an instance of the IslamicChronology in the given time zone.
192: *
193: * @param zone the time zone to get the chronology in, null is default
194: * @param leapYears the type defining the leap year pattern
195: * @return a chronology in the specified time zone
196: */
197: public static IslamicChronology getInstance(DateTimeZone zone,
198: LeapYearPatternType leapYears) {
199: if (zone == null) {
200: zone = DateTimeZone.getDefault();
201: }
202: IslamicChronology chrono;
203: synchronized (cCache) {
204: IslamicChronology[] chronos = (IslamicChronology[]) cCache
205: .get(zone);
206: if (chronos == null) {
207: chronos = new IslamicChronology[4];
208: cCache.put(zone, chronos);
209: }
210: chrono = chronos[leapYears.index];
211: if (chrono == null) {
212: if (zone == DateTimeZone.UTC) {
213: // First create without a lower limit.
214: chrono = new IslamicChronology(null, null,
215: leapYears);
216: // Impose lower limit and make another IslamicChronology.
217: DateTime lowerLimit = new DateTime(1, 1, 1, 0, 0,
218: 0, 0, chrono);
219: chrono = new IslamicChronology(LimitChronology
220: .getInstance(chrono, lowerLimit, null),
221: null, leapYears);
222: } else {
223: chrono = getInstance(DateTimeZone.UTC, leapYears);
224: chrono = new IslamicChronology(ZonedChronology
225: .getInstance(chrono, zone), null, leapYears);
226: }
227: chronos[leapYears.index] = chrono;
228: }
229: }
230: return chrono;
231: }
232:
233: // Constructors and instance variables
234: //-----------------------------------------------------------------------
235: /**
236: * Restricted constructor.
237: */
238: IslamicChronology(Chronology base, Object param,
239: LeapYearPatternType leapYears) {
240: super (base, param, 4);
241: this .iLeapYears = leapYears;
242: }
243:
244: /**
245: * Serialization singleton.
246: */
247: private Object readResolve() {
248: Chronology base = getBase();
249: return base == null ? getInstanceUTC() : getInstance(base
250: .getZone());
251: }
252:
253: //-----------------------------------------------------------------------
254: /**
255: * Gets the leap year pattern type.
256: *
257: * @return the pattern type
258: */
259: public LeapYearPatternType getLeapYearPatternType() {
260: return iLeapYears;
261: }
262:
263: // Conversion
264: //-----------------------------------------------------------------------
265: /**
266: * Gets the Chronology in the UTC time zone.
267: *
268: * @return the chronology in UTC
269: */
270: public Chronology withUTC() {
271: return INSTANCE_UTC;
272: }
273:
274: /**
275: * Gets the Chronology in a specific time zone.
276: *
277: * @param zone the zone to get the chronology in, null is default
278: * @return the chronology
279: */
280: public Chronology withZone(DateTimeZone zone) {
281: if (zone == null) {
282: zone = DateTimeZone.getDefault();
283: }
284: if (zone == getZone()) {
285: return this ;
286: }
287: return getInstance(zone);
288: }
289:
290: //-----------------------------------------------------------------------
291: int getYear(long instant) {
292: long millisIslamic = instant - MILLIS_YEAR_1;
293: long cycles = millisIslamic / MILLIS_PER_CYCLE;
294: long cycleRemainder = millisIslamic % MILLIS_PER_CYCLE;
295:
296: int year = (int) ((cycles * CYCLE) + 1L);
297: long yearMillis = (isLeapYear(year) ? MILLIS_PER_LONG_YEAR
298: : MILLIS_PER_SHORT_YEAR);
299: while (cycleRemainder >= yearMillis) {
300: cycleRemainder -= yearMillis;
301: yearMillis = (isLeapYear(++year) ? MILLIS_PER_LONG_YEAR
302: : MILLIS_PER_SHORT_YEAR);
303: }
304: return year;
305: }
306:
307: long setYear(long instant, int year) {
308: // optimsed implementation of set, due to fixed months
309: int this Year = getYear(instant);
310: int dayOfYear = getDayOfYear(instant, this Year);
311: int millisOfDay = getMillisOfDay(instant);
312:
313: if (dayOfYear > 354) {
314: // Current year is leap, and day is leap.
315: if (!isLeapYear(year)) {
316: // Moving to a non-leap year, leap day doesn't exist.
317: dayOfYear--;
318: }
319: }
320:
321: instant = getYearMonthDayMillis(year, 1, dayOfYear);
322: instant += millisOfDay;
323: return instant;
324: }
325:
326: //-----------------------------------------------------------------------
327: long getYearDifference(long minuendInstant, long subtrahendInstant) {
328: // optimsed implementation of getDifference, due to fixed months
329: int minuendYear = getYear(minuendInstant);
330: int subtrahendYear = getYear(subtrahendInstant);
331:
332: // Inlined remainder method to avoid duplicate calls to get.
333: long minuendRem = minuendInstant - getYearMillis(minuendYear);
334: long subtrahendRem = subtrahendInstant
335: - getYearMillis(subtrahendYear);
336:
337: int difference = minuendYear - subtrahendYear;
338: if (minuendRem < subtrahendRem) {
339: difference--;
340: }
341: return difference;
342: }
343:
344: //-----------------------------------------------------------------------
345: long getTotalMillisByYearMonth(int year, int month) {
346: if (--month % 2 == 1) {
347: month /= 2;
348: return month * MILLIS_PER_MONTH_PAIR
349: + MILLIS_PER_LONG_MONTH;
350: } else {
351: month /= 2;
352: return month * MILLIS_PER_MONTH_PAIR;
353: }
354: }
355:
356: //-----------------------------------------------------------------------
357: int getDayOfMonth(long millis) {
358: // optimised for simple months
359: int doy = getDayOfYear(millis) - 1;
360: if (doy == 354) {
361: return 30;
362: }
363: return (doy % MONTH_PAIR_LENGTH) % LONG_MONTH_LENGTH + 1;
364: }
365:
366: //-----------------------------------------------------------------------
367: boolean isLeapYear(int year) {
368: return iLeapYears.isLeapYear(year);
369: }
370:
371: //-----------------------------------------------------------------------
372: int getDaysInYearMax() {
373: return 355;
374: }
375:
376: //-----------------------------------------------------------------------
377: int getDaysInYear(int year) {
378: return isLeapYear(year) ? 355 : 354;
379: }
380:
381: //-----------------------------------------------------------------------
382: int getDaysInYearMonth(int year, int month) {
383: if (month == 12 && isLeapYear(year)) {
384: return LONG_MONTH_LENGTH;
385: }
386: return (--month % 2 == 0 ? LONG_MONTH_LENGTH
387: : SHORT_MONTH_LENGTH);
388: }
389:
390: //-----------------------------------------------------------------------
391: int getDaysInMonthMax() {
392: return LONG_MONTH_LENGTH;
393: }
394:
395: //-----------------------------------------------------------------------
396: int getDaysInMonthMax(int month) {
397: if (month == 12) {
398: return LONG_MONTH_LENGTH;
399: }
400: return (--month % 2 == 0 ? LONG_MONTH_LENGTH
401: : SHORT_MONTH_LENGTH);
402: }
403:
404: //-----------------------------------------------------------------------
405: int getMonthOfYear(long millis, int year) {
406: int doyZeroBased = (int) ((millis - getYearMillis(year)) / DateTimeConstants.MILLIS_PER_DAY);
407: if (doyZeroBased == 354) {
408: return 12;
409: }
410: return ((doyZeroBased * 2) / MONTH_PAIR_LENGTH) + 1;
411: // return (int) (doyZeroBased / 29.9f) + 1;
412: //
413: // int monthPairZeroBased = doyZeroBased / MONTH_PAIR_LENGTH;
414: // int monthPairRemainder = doyZeroBased % MONTH_PAIR_LENGTH;
415: // return (monthPairZeroBased * 2) + 1 + (monthPairRemainder >= LONG_MONTH_LENGTH ? 1 : 0);
416: }
417:
418: //-----------------------------------------------------------------------
419: long getAverageMillisPerYear() {
420: return MILLIS_PER_YEAR;
421: }
422:
423: //-----------------------------------------------------------------------
424: long getAverageMillisPerYearDividedByTwo() {
425: return MILLIS_PER_YEAR / 2;
426: }
427:
428: //-----------------------------------------------------------------------
429: long getAverageMillisPerMonth() {
430: return MILLIS_PER_MONTH;
431: }
432:
433: //-----------------------------------------------------------------------
434: long calculateFirstDayOfYearMillis(int year) {
435: if (year > MAX_YEAR) {
436: throw new ArithmeticException("Year is too large: " + year
437: + " > " + MAX_YEAR);
438: }
439: if (year < MIN_YEAR) {
440: throw new ArithmeticException("Year is too small: " + year
441: + " < " + MIN_YEAR);
442: }
443:
444: // Java epoch is 1970-01-01 Gregorian which is 0622-07-16 Islamic.
445: // 0001-01-01 Islamic is -42520809600000L
446: // would prefer to calculate against year zero, but leap year
447: // can be in that year so it doesn't work
448: year--;
449: long cycle = year / CYCLE;
450: long millis = MILLIS_YEAR_1 + cycle * MILLIS_PER_CYCLE;
451: int cycleRemainder = (year % CYCLE) + 1;
452:
453: for (int i = 1; i < cycleRemainder; i++) {
454: millis += (isLeapYear(i) ? MILLIS_PER_LONG_YEAR
455: : MILLIS_PER_SHORT_YEAR);
456: }
457:
458: return millis;
459: }
460:
461: //-----------------------------------------------------------------------
462: int getMinYear() {
463: return 1; //MIN_YEAR;
464: }
465:
466: //-----------------------------------------------------------------------
467: int getMaxYear() {
468: return MAX_YEAR;
469: }
470:
471: //-----------------------------------------------------------------------
472: long getApproxMillisAtEpochDividedByTwo() {
473: // Epoch 1970-01-01 ISO = 1389-10-22 Islamic
474: return (-MILLIS_YEAR_1) / 2;
475: }
476:
477: //-----------------------------------------------------------------------
478: protected void assemble(Fields fields) {
479: if (getBase() == null) {
480: super .assemble(fields);
481:
482: fields.era = ERA_FIELD;
483: fields.monthOfYear = new BasicMonthOfYearDateTimeField(
484: this , 12);
485: fields.months = fields.monthOfYear.getDurationField();
486: }
487: }
488:
489: //-----------------------------------------------------------------------
490: /**
491: * Opaque object describing a leap year pattern for the Islamic Chronology.
492: *
493: * @since 1.2
494: */
495: public static class LeapYearPatternType implements Serializable {
496: /** Serialization lock */
497: private static final long serialVersionUID = 26581275372698L;
498: // /** Leap year raw data encoded into bits. */
499: // private static final int[][] LEAP_YEARS = {
500: // {2, 5, 7, 10, 13, 15, 18, 21, 24, 26, 29}, // 623158436
501: // {2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29}, // 623191204
502: // {2, 5, 8, 10, 13, 16, 19, 21, 24, 27, 29}, // 690562340
503: // {0, 2, 5, 8, 11, 13, 16, 19, 21, 24, 27}, // 153692453
504: // };
505:
506: /** The index. */
507: final byte index;
508: /** The leap year pattern, a bit-based 1=true pattern. */
509: final int pattern;
510:
511: /**
512: * Constructor.
513: * This constructor takes a bit pattern where bits 0-29 correspond
514: * to years 0-29 in the 30 year Islamic cycle of years. This allows
515: * a highly efficient lookup by bit-matching.
516: *
517: * @param index the index
518: * @param pattern the bit pattern
519: */
520: LeapYearPatternType(int index, int pattern) {
521: super ();
522: this .index = (byte) index;
523: this .pattern = pattern;
524: }
525:
526: /**
527: * Is the year a leap year.
528: * @param year the year to query
529: * @return true if leap
530: */
531: boolean isLeapYear(int year) {
532: int key = 1 << (year % 30);
533: return ((pattern & key) > 0);
534: }
535:
536: /**
537: * Ensure a singleton is returned if possible.
538: * @return the singleton instance
539: */
540: private Object readResolve() {
541: switch (index) {
542: case 0:
543: return LEAP_YEAR_15_BASED;
544: case 1:
545: return LEAP_YEAR_16_BASED;
546: case 2:
547: return LEAP_YEAR_INDIAN;
548: case 3:
549: return LEAP_YEAR_HABASH_AL_HASIB;
550: default:
551: return this;
552: }
553: }
554: }
555: }
|