1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public final class IslamicChronology extends BasicChronology {
67
68
69 private static final long serialVersionUID = -3663823829888L;
70
71
72
73
74
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
93
94
95
96
97
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
130
131
132
133
134
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<DateTimeZone, IslamicChronology[]> cCache = new HashMap<DateTimeZone, IslamicChronology[]>();
143
144
145 private static final IslamicChronology INSTANCE_UTC;
146 static {
147
148 INSTANCE_UTC = getInstance(DateTimeZone.UTC);
149 }
150
151
152 private final LeapYearPatternType iLeapYears;
153
154
155
156
157
158
159
160
161 public static IslamicChronology getInstanceUTC() {
162 return INSTANCE_UTC;
163 }
164
165
166
167
168
169
170 public static IslamicChronology getInstance() {
171 return getInstance(DateTimeZone.getDefault(), LEAP_YEAR_16_BASED);
172 }
173
174
175
176
177
178
179
180 public static IslamicChronology getInstance(DateTimeZone zone) {
181 return getInstance(zone, LEAP_YEAR_16_BASED);
182 }
183
184
185
186
187
188
189
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
206 chrono = new IslamicChronology(null, null, leapYears);
207
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
224
225
226
227
228 IslamicChronology(Chronology base, Object param, LeapYearPatternType leapYears) {
229 super(base, param, 4);
230 this.iLeapYears = leapYears;
231 }
232
233
234
235
236 private Object readResolve() {
237 Chronology base = getBase();
238 return base == null ? getInstanceUTC() : getInstance(base.getZone());
239 }
240
241
242
243
244
245
246
247 public LeapYearPatternType getLeapYearPatternType() {
248 return iLeapYears;
249 }
250
251
252
253
254
255
256
257
258 public Chronology withUTC() {
259 return INSTANCE_UTC;
260 }
261
262
263
264
265
266
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
280
281
282
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
305 int thisYear = getYear(instant);
306 int dayOfYear = getDayOfYear(instant, thisYear);
307 int millisOfDay = getMillisOfDay(instant);
308
309 if (dayOfYear > 354) {
310
311 if (!isLeapYear(year)) {
312
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
325 int minuendYear = getYear(minuendInstant);
326 int subtrahendYear = getYear(subtrahendInstant);
327
328
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
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
404
405
406
407
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
435
436
437
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;
453 }
454
455
456 int getMaxYear() {
457 return MAX_YEAR;
458 }
459
460
461 long getApproxMillisAtEpochDividedByTwo() {
462
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
480
481
482
483 public static class LeapYearPatternType implements Serializable {
484
485 private static final long serialVersionUID = 26581275372698L;
486
487
488
489
490
491
492
493
494
495 final byte index;
496
497 final int pattern;
498
499
500
501
502
503
504
505
506
507
508 LeapYearPatternType(int index, int pattern) {
509 super();
510 this.index = (byte) index;
511 this.pattern = pattern;
512 }
513
514
515
516
517
518
519 boolean isLeapYear(int year) {
520 int key = 1 << (year % 30);
521 return ((pattern & key) > 0);
522 }
523
524
525
526
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 }