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 }