1 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 66 public final class IslamicChronology extends BasicChronology { 67 68 69 private static final long serialVersionUID = -3663823829888L; 70 71 75 public static final int AH = DateTimeConstants.CE; 76 77 78 private static final DateTimeField ERA_FIELD = new BasicSingleEraDateTimeField("AH"); 79 80 81 public static final LeapYearPatternType LEAP_YEAR_15_BASED = new LeapYearPatternType(0, 623158436); 82 83 public static final LeapYearPatternType LEAP_YEAR_16_BASED = new LeapYearPatternType(1, 623191204); 84 85 public static final LeapYearPatternType LEAP_YEAR_INDIAN = new LeapYearPatternType(2, 690562340); 86 87 public static final LeapYearPatternType LEAP_YEAR_HABASH_AL_HASIB = new LeapYearPatternType(3, 153692453); 88 89 90 private static final int MIN_YEAR = -292269337; 91 92 98 private static final int MAX_YEAR = 292271022; 99 100 101 private static final int MONTH_PAIR_LENGTH = 59; 102 103 104 private static final int LONG_MONTH_LENGTH = 30; 105 106 107 private static final int SHORT_MONTH_LENGTH = 29; 108 109 110 private static final long MILLIS_PER_MONTH_PAIR = 59L * DateTimeConstants.MILLIS_PER_DAY; 111 112 113 private static final long MILLIS_PER_MONTH = (long) (29.53056 * DateTimeConstants.MILLIS_PER_DAY); 114 115 116 private static final long MILLIS_PER_LONG_MONTH = 30L * DateTimeConstants.MILLIS_PER_DAY; 117 118 119 private static final long MILLIS_PER_YEAR = (long) (354.36667 * DateTimeConstants.MILLIS_PER_DAY); 120 121 122 private static final long MILLIS_PER_SHORT_YEAR = 354L * DateTimeConstants.MILLIS_PER_DAY; 123 124 125 private static final long MILLIS_PER_LONG_YEAR = 355L * DateTimeConstants.MILLIS_PER_DAY; 126 127 128 private static final long MILLIS_YEAR_1 = -42521587200000L; 129 135 136 private static final int CYCLE = 30; 137 138 139 private static final long MILLIS_PER_CYCLE = ((19L * 354L + 11L * 355L) * DateTimeConstants.MILLIS_PER_DAY); 140 141 142 private static final Map cCache = new HashMap (); 143 144 145 private static final IslamicChronology INSTANCE_UTC; 146 static { 147 INSTANCE_UTC = getInstance(DateTimeZone.UTC); 149 } 150 151 152 private final LeapYearPatternType iLeapYears; 153 154 161 public static IslamicChronology getInstanceUTC() { 162 return INSTANCE_UTC; 163 } 164 165 170 public static IslamicChronology getInstance() { 171 return getInstance(DateTimeZone.getDefault(), LEAP_YEAR_16_BASED); 172 } 173 174 180 public static IslamicChronology getInstance(DateTimeZone zone) { 181 return getInstance(zone, LEAP_YEAR_16_BASED); 182 } 183 184 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 chrono = new IslamicChronology(null, null, leapYears); 207 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 228 IslamicChronology(Chronology base, Object param, LeapYearPatternType leapYears) { 229 super(base, param, 4); 230 this.iLeapYears = leapYears; 231 } 232 233 236 private Object readResolve() { 237 Chronology base = getBase(); 238 return base == null ? getInstanceUTC() : getInstance(base.getZone()); 239 } 240 241 247 public LeapYearPatternType getLeapYearPatternType() { 248 return iLeapYears; 249 } 250 251 258 public Chronology withUTC() { 259 return INSTANCE_UTC; 260 } 261 262 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 int getYear(long instant) { 280 long millisIslamic = instant - MILLIS_YEAR_1; 281 long cycles = millisIslamic / MILLIS_PER_CYCLE; 282 long cycleRemainder = millisIslamic % MILLIS_PER_CYCLE; 283 284 int year = (int) ((cycles * CYCLE) + 1L); 285 long yearMillis = (isLeapYear(year) ? MILLIS_PER_LONG_YEAR : MILLIS_PER_SHORT_YEAR); 286 while (cycleRemainder >= yearMillis) { 287 cycleRemainder -= yearMillis; 288 yearMillis = (isLeapYear(++year) ? MILLIS_PER_LONG_YEAR : MILLIS_PER_SHORT_YEAR); 289 } 290 return year; 291 } 292 293 long setYear(long instant, int year) { 294 int thisYear = getYear(instant); 296 int dayOfYear = getDayOfYear(instant, thisYear); 297 int millisOfDay = getMillisOfDay(instant); 298 299 if (dayOfYear > 354) { 300 if (!isLeapYear(year)) { 302 dayOfYear--; 304 } 305 } 306 307 instant = getYearMonthDayMillis(year, 1, dayOfYear); 308 instant += millisOfDay; 309 return instant; 310 } 311 312 long getYearDifference(long minuendInstant, long subtrahendInstant) { 314 int minuendYear = getYear(minuendInstant); 316 int subtrahendYear = getYear(subtrahendInstant); 317 318 long minuendRem = minuendInstant - getYearMillis(minuendYear); 320 long subtrahendRem = subtrahendInstant - getYearMillis(subtrahendYear); 321 322 int difference = minuendYear - subtrahendYear; 323 if (minuendRem < subtrahendRem) { 324 difference--; 325 } 326 return difference; 327 } 328 329 long getTotalMillisByYearMonth(int year, int month) { 331 if (--month % 2 == 1) { 332 month /= 2; 333 return month * MILLIS_PER_MONTH_PAIR + MILLIS_PER_LONG_MONTH; 334 } else { 335 month /= 2; 336 return month * MILLIS_PER_MONTH_PAIR; 337 } 338 } 339 340 int getDayOfMonth(long millis) { 342 int doy = getDayOfYear(millis) - 1; 344 if (doy == 354) { 345 return 30; 346 } 347 return (doy % MONTH_PAIR_LENGTH) % LONG_MONTH_LENGTH + 1; 348 } 349 350 boolean isLeapYear(int year) { 352 return iLeapYears.isLeapYear(year); 353 } 354 355 int getDaysInYearMax() { 357 return 355; 358 } 359 360 int getDaysInYear(int year) { 362 return isLeapYear(year) ? 355 : 354; 363 } 364 365 int getDaysInYearMonth(int year, int month) { 367 if (month == 12 && isLeapYear(year)) { 368 return LONG_MONTH_LENGTH; 369 } 370 return (--month % 2 == 0 ? LONG_MONTH_LENGTH : SHORT_MONTH_LENGTH); 371 } 372 373 int getDaysInMonthMax() { 375 return LONG_MONTH_LENGTH; 376 } 377 378 int getDaysInMonthMax(int month) { 380 if (month == 12) { 381 return LONG_MONTH_LENGTH; 382 } 383 return (--month % 2 == 0 ? LONG_MONTH_LENGTH : SHORT_MONTH_LENGTH); 384 } 385 386 int getMonthOfYear(long millis, int year) { 388 int doyZeroBased = (int) ((millis - getYearMillis(year)) / DateTimeConstants.MILLIS_PER_DAY); 389 if (doyZeroBased == 354) { 390 return 12; 391 } 392 return ((doyZeroBased * 2) / MONTH_PAIR_LENGTH) + 1; 393 } 399 400 long getAverageMillisPerYear() { 402 return MILLIS_PER_YEAR; 403 } 404 405 long getAverageMillisPerYearDividedByTwo() { 407 return MILLIS_PER_YEAR / 2; 408 } 409 410 long getAverageMillisPerMonth() { 412 return MILLIS_PER_MONTH; 413 } 414 415 long calculateFirstDayOfYearMillis(int year) { 417 if (year > MAX_YEAR) { 418 throw new ArithmeticException ("Year is too large: " + year + " > " + MAX_YEAR); 419 } 420 if (year < MIN_YEAR) { 421 throw new ArithmeticException ("Year is too small: " + year + " < " + MIN_YEAR); 422 } 423 424 year--; 429 long cycle = year / CYCLE; 430 long millis = MILLIS_YEAR_1 + cycle * MILLIS_PER_CYCLE; 431 int cycleRemainder = (year % CYCLE) + 1; 432 433 for (int i = 1; i < cycleRemainder; i++) { 434 millis += (isLeapYear(i) ? MILLIS_PER_LONG_YEAR : MILLIS_PER_SHORT_YEAR); 435 } 436 437 return millis; 438 } 439 440 int getMinYear() { 442 return 1; } 444 445 int getMaxYear() { 447 return MAX_YEAR; 448 } 449 450 long getApproxMillisAtEpochDividedByTwo() { 452 return (-MILLIS_YEAR_1) / 2; 454 } 455 456 protected void assemble(Fields fields) { 458 if (getBase() == null) { 459 super.assemble(fields); 460 461 fields.era = ERA_FIELD; 462 fields.monthOfYear = new BasicMonthOfYearDateTimeField(this, 12); 463 fields.months = fields.monthOfYear.getDurationField(); 464 } 465 } 466 467 473 public static class LeapYearPatternType implements Serializable { 474 475 private static final long serialVersionUID = 26581275372698L; 476 484 485 final byte index; 486 487 final int pattern; 488 489 498 LeapYearPatternType(int index, int pattern) { 499 super(); 500 this.index = (byte) index; 501 this.pattern = pattern; 502 } 503 504 509 boolean isLeapYear(int year) { 510 int key = 1 << (year % 30); 511 return ((pattern & key) > 0); 512 } 513 514 518 private Object readResolve() { 519 switch (index) { 520 case 0: 521 return LEAP_YEAR_15_BASED; 522 case 1: 523 return LEAP_YEAR_16_BASED; 524 case 2: 525 return LEAP_YEAR_INDIAN; 526 case 3: 527 return LEAP_YEAR_HABASH_AL_HASIB; 528 default: 529 return this; 530 } 531 } 532 } 533 } 534 | Popular Tags |