001 /*
002 * Copyright 2001-2009 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("AH");
079
080 /** Leap year 15-based pattern. */
081 public static final LeapYearPatternType LEAP_YEAR_15_BASED = new LeapYearPatternType(0, 623158436);
082 /** Leap year 16-based pattern. */
083 public static final LeapYearPatternType LEAP_YEAR_16_BASED = new LeapYearPatternType(1, 623191204);
084 /** Leap year Indian pattern. */
085 public static final LeapYearPatternType LEAP_YEAR_INDIAN = new LeapYearPatternType(2, 690562340);
086 /** Leap year Habash al-Hasib pattern. */
087 public static final LeapYearPatternType LEAP_YEAR_HABASH_AL_HASIB = new LeapYearPatternType(3, 153692453);
088
089 /** The lowest year that can be fully supported. */
090 private static final int MIN_YEAR = -292269337;
091
092 /**
093 * The highest year that can be fully supported.
094 * Although calculateFirstDayOfYearMillis can go higher without
095 * overflowing, the getYear method overflows when it adds the
096 * approximate millis at the epoch.
097 */
098 private static final int MAX_YEAR = 292271022;
099
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 }