1
16 package org.joda.time.chrono;
17
18 import java.util.Locale;
19
20 import org.joda.time.Chronology;
21 import org.joda.time.DateTimeConstants;
22 import org.joda.time.DateTimeField;
23 import org.joda.time.DateTimeFieldType;
24 import org.joda.time.DateTimeZone;
25 import org.joda.time.DurationField;
26 import org.joda.time.DurationFieldType;
27 import org.joda.time.field.DividedDateTimeField;
28 import org.joda.time.field.FieldUtils;
29 import org.joda.time.field.MillisDurationField;
30 import org.joda.time.field.OffsetDateTimeField;
31 import org.joda.time.field.PreciseDateTimeField;
32 import org.joda.time.field.PreciseDurationField;
33 import org.joda.time.field.RemainderDateTimeField;
34 import org.joda.time.field.ZeroIsMaxDateTimeField;
35
36
50 abstract class BasicChronology extends AssembledChronology {
51
52
53 private static final long serialVersionUID = 8283225332206808863L;
54
55 private static final DurationField cMillisField;
56 private static final DurationField cSecondsField;
57 private static final DurationField cMinutesField;
58 private static final DurationField cHoursField;
59 private static final DurationField cHalfdaysField;
60 private static final DurationField cDaysField;
61 private static final DurationField cWeeksField;
62
63 private static final DateTimeField cMillisOfSecondField;
64 private static final DateTimeField cMillisOfDayField;
65 private static final DateTimeField cSecondOfMinuteField;
66 private static final DateTimeField cSecondOfDayField;
67 private static final DateTimeField cMinuteOfHourField;
68 private static final DateTimeField cMinuteOfDayField;
69 private static final DateTimeField cHourOfDayField;
70 private static final DateTimeField cHourOfHalfdayField;
71 private static final DateTimeField cClockhourOfDayField;
72 private static final DateTimeField cClockhourOfHalfdayField;
73 private static final DateTimeField cHalfdayOfDayField;
74
75 static {
76 cMillisField = MillisDurationField.INSTANCE;
77 cSecondsField = new PreciseDurationField
78 (DurationFieldType.seconds(), DateTimeConstants.MILLIS_PER_SECOND);
79 cMinutesField = new PreciseDurationField
80 (DurationFieldType.minutes(), DateTimeConstants.MILLIS_PER_MINUTE);
81 cHoursField = new PreciseDurationField
82 (DurationFieldType.hours(), DateTimeConstants.MILLIS_PER_HOUR);
83 cHalfdaysField = new PreciseDurationField
84 (DurationFieldType.halfdays(), DateTimeConstants.MILLIS_PER_DAY / 2);
85 cDaysField = new PreciseDurationField
86 (DurationFieldType.days(), DateTimeConstants.MILLIS_PER_DAY);
87 cWeeksField = new PreciseDurationField
88 (DurationFieldType.weeks(), DateTimeConstants.MILLIS_PER_WEEK);
89
90 cMillisOfSecondField = new PreciseDateTimeField
91 (DateTimeFieldType.millisOfSecond(), cMillisField, cSecondsField);
92
93 cMillisOfDayField = new PreciseDateTimeField
94 (DateTimeFieldType.millisOfDay(), cMillisField, cDaysField);
95
96 cSecondOfMinuteField = new PreciseDateTimeField
97 (DateTimeFieldType.secondOfMinute(), cSecondsField, cMinutesField);
98
99 cSecondOfDayField = new PreciseDateTimeField
100 (DateTimeFieldType.secondOfDay(), cSecondsField, cDaysField);
101
102 cMinuteOfHourField = new PreciseDateTimeField
103 (DateTimeFieldType.minuteOfHour(), cMinutesField, cHoursField);
104
105 cMinuteOfDayField = new PreciseDateTimeField
106 (DateTimeFieldType.minuteOfDay(), cMinutesField, cDaysField);
107
108 cHourOfDayField = new PreciseDateTimeField
109 (DateTimeFieldType.hourOfDay(), cHoursField, cDaysField);
110
111 cHourOfHalfdayField = new PreciseDateTimeField
112 (DateTimeFieldType.hourOfHalfday(), cHoursField, cHalfdaysField);
113
114 cClockhourOfDayField = new ZeroIsMaxDateTimeField
115 (cHourOfDayField, DateTimeFieldType.clockhourOfDay());
116
117 cClockhourOfHalfdayField = new ZeroIsMaxDateTimeField
118 (cHourOfHalfdayField, DateTimeFieldType.clockhourOfHalfday());
119
120 cHalfdayOfDayField = new HalfdayField();
121 }
122
123 private static final int CACHE_SIZE = 1 << 10;
124 private static final int CACHE_MASK = CACHE_SIZE - 1;
125
126 private transient final YearInfo[] iYearInfoCache = new YearInfo[CACHE_SIZE];
127
128 private final int iMinDaysInFirstWeek;
129
130 BasicChronology(Chronology base, Object param, int minDaysInFirstWeek) {
131 super(base, param);
132
133 if (minDaysInFirstWeek < 1 || minDaysInFirstWeek > 7) {
134 throw new IllegalArgumentException
135 ("Invalid min days in first week: " + minDaysInFirstWeek);
136 }
137
138 iMinDaysInFirstWeek = minDaysInFirstWeek;
139 }
140
141 public DateTimeZone getZone() {
142 Chronology base;
143 if ((base = getBase()) != null) {
144 return base.getZone();
145 }
146 return DateTimeZone.UTC;
147 }
148
149 public long getDateTimeMillis(
150 int year, int monthOfYear, int dayOfMonth, int millisOfDay)
151 throws IllegalArgumentException {
152 Chronology base;
153 if ((base = getBase()) != null) {
154 return base.getDateTimeMillis(year, monthOfYear, dayOfMonth, millisOfDay);
155 }
156
157 FieldUtils.verifyValueBounds
158 (DateTimeFieldType.millisOfDay(), millisOfDay, 0, DateTimeConstants.MILLIS_PER_DAY - 1);
159 return getDateMidnightMillis(year, monthOfYear, dayOfMonth) + millisOfDay;
160 }
161
162 public long getDateTimeMillis(
163 int year, int monthOfYear, int dayOfMonth,
164 int hourOfDay, int minuteOfHour, int secondOfMinute, int millisOfSecond)
165 throws IllegalArgumentException {
166 Chronology base;
167 if ((base = getBase()) != null) {
168 return base.getDateTimeMillis(year, monthOfYear, dayOfMonth,
169 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
170 }
171
172 FieldUtils.verifyValueBounds(DateTimeFieldType.hourOfDay(), hourOfDay, 0, 23);
173 FieldUtils.verifyValueBounds(DateTimeFieldType.minuteOfHour(), minuteOfHour, 0, 59);
174 FieldUtils.verifyValueBounds(DateTimeFieldType.secondOfMinute(), secondOfMinute, 0, 59);
175 FieldUtils.verifyValueBounds(DateTimeFieldType.millisOfSecond(), millisOfSecond, 0, 999);
176
177 return getDateMidnightMillis(year, monthOfYear, dayOfMonth)
178 + hourOfDay * DateTimeConstants.MILLIS_PER_HOUR
179 + minuteOfHour * DateTimeConstants.MILLIS_PER_MINUTE
180 + secondOfMinute * DateTimeConstants.MILLIS_PER_SECOND
181 + millisOfSecond;
182 }
183
184 public int getMinimumDaysInFirstWeek() {
185 return iMinDaysInFirstWeek;
186 }
187
188
189
196 public boolean equals(Object obj) {
197 if (this == obj) {
198 return true;
199 }
200 if (obj != null && getClass() == obj.getClass()) {
201 BasicChronology chrono = (BasicChronology) obj;
202 return getMinimumDaysInFirstWeek() == chrono.getMinimumDaysInFirstWeek() &&
203 getZone().equals(chrono.getZone());
204 }
205 return false;
206 }
207
208
214 public int hashCode() {
215 return getClass().getName().hashCode() * 11 + getZone().hashCode() + getMinimumDaysInFirstWeek();
216 }
217
218
219
220
225 public String toString() {
226 StringBuilder sb = new StringBuilder(60);
227 String name = getClass().getName();
228 int index = name.lastIndexOf('.');
229 if (index >= 0) {
230 name = name.substring(index + 1);
231 }
232 sb.append(name);
233 sb.append('[');
234 DateTimeZone zone = getZone();
235 if (zone != null) {
236 sb.append(zone.getID());
237 }
238 if (getMinimumDaysInFirstWeek() != 4) {
239 sb.append(",mdfw=");
240 sb.append(getMinimumDaysInFirstWeek());
241 }
242 sb.append(']');
243 return sb.toString();
244 }
245
246 protected void assemble(Fields fields) {
247
248
249
250 fields.millis = cMillisField;
251 fields.seconds = cSecondsField;
252 fields.minutes = cMinutesField;
253 fields.hours = cHoursField;
254 fields.halfdays = cHalfdaysField;
255 fields.days = cDaysField;
256 fields.weeks = cWeeksField;
257
258 fields.millisOfSecond = cMillisOfSecondField;
259 fields.millisOfDay = cMillisOfDayField;
260 fields.secondOfMinute = cSecondOfMinuteField;
261 fields.secondOfDay = cSecondOfDayField;
262 fields.minuteOfHour = cMinuteOfHourField;
263 fields.minuteOfDay = cMinuteOfDayField;
264 fields.hourOfDay = cHourOfDayField;
265 fields.hourOfHalfday = cHourOfHalfdayField;
266 fields.clockhourOfDay = cClockhourOfDayField;
267 fields.clockhourOfHalfday = cClockhourOfHalfdayField;
268 fields.halfdayOfDay = cHalfdayOfDayField;
269
270
271
272
273 fields.year = new BasicYearDateTimeField(this);
274 fields.yearOfEra = new GJYearOfEraDateTimeField(fields.year, this);
275
276
277 DateTimeField field = new OffsetDateTimeField(
278 fields.yearOfEra, 99);
279 fields.centuryOfEra = new DividedDateTimeField(
280 field, DateTimeFieldType.centuryOfEra(), 100);
281 fields.centuries = fields.centuryOfEra.getDurationField();
282
283 field = new RemainderDateTimeField(
284 (DividedDateTimeField) fields.centuryOfEra);
285 fields.yearOfCentury = new OffsetDateTimeField(
286 field, DateTimeFieldType.yearOfCentury(), 1);
287
288 fields.era = new GJEraDateTimeField(this);
289 fields.dayOfWeek = new GJDayOfWeekDateTimeField(this, fields.days);
290 fields.dayOfMonth = new BasicDayOfMonthDateTimeField(this, fields.days);
291 fields.dayOfYear = new BasicDayOfYearDateTimeField(this, fields.days);
292 fields.monthOfYear = new GJMonthOfYearDateTimeField(this);
293 fields.weekyear = new BasicWeekyearDateTimeField(this);
294 fields.weekOfWeekyear = new BasicWeekOfWeekyearDateTimeField(this, fields.weeks);
295
296 field = new RemainderDateTimeField(
297 fields.weekyear, fields.centuries, DateTimeFieldType.weekyearOfCentury(), 100);
298 fields.weekyearOfCentury = new OffsetDateTimeField(
299 field, DateTimeFieldType.weekyearOfCentury(), 1);
300
301
302
303 fields.years = fields.year.getDurationField();
304 fields.months = fields.monthOfYear.getDurationField();
305 fields.weekyears = fields.weekyear.getDurationField();
306 }
307
308
309
314 int getDaysInYearMax() {
315 return 366;
316 }
317
318
324 int getDaysInYear(int year) {
325 return isLeapYear(year) ? 366 : 365;
326 }
327
328
334 int getWeeksInYear(int year) {
335 long firstWeekMillis1 = getFirstWeekOfYearMillis(year);
336 long firstWeekMillis2 = getFirstWeekOfYearMillis(year + 1);
337 return (int) ((firstWeekMillis2 - firstWeekMillis1) / DateTimeConstants.MILLIS_PER_WEEK);
338 }
339
340
346 long getFirstWeekOfYearMillis(int year) {
347 long jan1millis = getYearMillis(year);
348 int jan1dayOfWeek = getDayOfWeek(jan1millis);
349
350 if (jan1dayOfWeek > (8 - iMinDaysInFirstWeek)) {
351
352 return jan1millis + (8 - jan1dayOfWeek)
353 * (long)DateTimeConstants.MILLIS_PER_DAY;
354 } else {
355
356 return jan1millis - (jan1dayOfWeek - 1)
357 * (long)DateTimeConstants.MILLIS_PER_DAY;
358 }
359 }
360
361
367 long getYearMillis(int year) {
368 return getYearInfo(year).iFirstDayMillis;
369 }
370
371
378 long getYearMonthMillis(int year, int month) {
379 long millis = getYearMillis(year);
380 millis += getTotalMillisByYearMonth(year, month);
381 return millis;
382 }
383
384
392 long getYearMonthDayMillis(int year, int month, int dayOfMonth) {
393 long millis = getYearMillis(year);
394 millis += getTotalMillisByYearMonth(year, month);
395 return millis + (dayOfMonth - 1) * (long)DateTimeConstants.MILLIS_PER_DAY;
396 }
397
398
401 int getYear(long instant) {
402
403
404
405
406
407 long unitMillis = getAverageMillisPerYearDividedByTwo();
408 long i2 = (instant >> 1) + getApproxMillisAtEpochDividedByTwo();
409 if (i2 < 0) {
410 i2 = i2 - unitMillis + 1;
411 }
412 int year = (int) (i2 / unitMillis);
413
414 long yearStart = getYearMillis(year);
415 long diff = instant - yearStart;
416
417 if (diff < 0) {
418 year--;
419 } else if (diff >= DateTimeConstants.MILLIS_PER_DAY * 365L) {
420
421 long oneYear;
422 if (isLeapYear(year)) {
423 oneYear = DateTimeConstants.MILLIS_PER_DAY * 366L;
424 } else {
425 oneYear = DateTimeConstants.MILLIS_PER_DAY * 365L;
426 }
427
428 yearStart += oneYear;
429
430 if (yearStart <= instant) {
431
432 year++;
433 }
434 }
435
436 return year;
437 }
438
439
442 int getMonthOfYear(long millis) {
443 return getMonthOfYear(millis, getYear(millis));
444 }
445
446
450 abstract int getMonthOfYear(long millis, int year);
451
452
455 int getDayOfMonth(long millis) {
456 int year = getYear(millis);
457 int month = getMonthOfYear(millis, year);
458 return getDayOfMonth(millis, year, month);
459 }
460
461
465 int getDayOfMonth(long millis, int year) {
466 int month = getMonthOfYear(millis, year);
467 return getDayOfMonth(millis, year, month);
468 }
469
470
475 int getDayOfMonth(long millis, int year, int month) {
476 long dateMillis = getYearMillis(year);
477 dateMillis += getTotalMillisByYearMonth(year, month);
478 return (int) ((millis - dateMillis) / DateTimeConstants.MILLIS_PER_DAY) + 1;
479 }
480
481
484 int getDayOfYear(long instant) {
485 return getDayOfYear(instant, getYear(instant));
486 }
487
488
492 int getDayOfYear(long instant, int year) {
493 long yearStart = getYearMillis(year);
494 return (int) ((instant - yearStart) / DateTimeConstants.MILLIS_PER_DAY) + 1;
495 }
496
497
500 int getWeekyear(long instant) {
501 int year = getYear(instant);
502 int week = getWeekOfWeekyear(instant, year);
503 if (week == 1) {
504 return getYear(instant + DateTimeConstants.MILLIS_PER_WEEK);
505 } else if (week > 51) {
506 return getYear(instant - (2 * DateTimeConstants.MILLIS_PER_WEEK));
507 } else {
508 return year;
509 }
510 }
511
512
515 int getWeekOfWeekyear(long instant) {
516 return getWeekOfWeekyear(instant, getYear(instant));
517 }
518
519
523 int getWeekOfWeekyear(long instant, int year) {
524 long firstWeekMillis1 = getFirstWeekOfYearMillis(year);
525 if (instant < firstWeekMillis1) {
526 return getWeeksInYear(year - 1);
527 }
528 long firstWeekMillis2 = getFirstWeekOfYearMillis(year + 1);
529 if (instant >= firstWeekMillis2) {
530 return 1;
531 }
532 return (int) ((instant - firstWeekMillis1) / DateTimeConstants.MILLIS_PER_WEEK) + 1;
533 }
534
535
538 int getDayOfWeek(long instant) {
539
540
541 long daysSince19700101;
542 if (instant >= 0) {
543 daysSince19700101 = instant / DateTimeConstants.MILLIS_PER_DAY;
544 } else {
545 daysSince19700101 = (instant - (DateTimeConstants.MILLIS_PER_DAY - 1))
546 / DateTimeConstants.MILLIS_PER_DAY;
547 if (daysSince19700101 < -3) {
548 return 7 + (int) ((daysSince19700101 + 4) % 7);
549 }
550 }
551
552 return 1 + (int) ((daysSince19700101 + 3) % 7);
553 }
554
555
558 int getMillisOfDay(long instant) {
559 if (instant >= 0) {
560 return (int) (instant % DateTimeConstants.MILLIS_PER_DAY);
561 } else {
562 return (DateTimeConstants.MILLIS_PER_DAY - 1)
563 + (int) ((instant + 1) % DateTimeConstants.MILLIS_PER_DAY);
564 }
565 }
566
567
572 int getDaysInMonthMax() {
573 return 31;
574 }
575
576
582 int getDaysInMonthMax(long instant) {
583 int thisYear = getYear(instant);
584 int thisMonth = getMonthOfYear(instant, thisYear);
585 return getDaysInYearMonth(thisYear, thisMonth);
586 }
587
588
597 int getDaysInMonthMaxForSet(long instant, int value) {
598 return getDaysInMonthMax(instant);
599 }
600
601
602
610 long getDateMidnightMillis(int year, int monthOfYear, int dayOfMonth) {
611 FieldUtils.verifyValueBounds(DateTimeFieldType.year(), year, getMinYear(), getMaxYear());
612 FieldUtils.verifyValueBounds(DateTimeFieldType.monthOfYear(), monthOfYear, 1, getMaxMonth(year));
613 FieldUtils.verifyValueBounds(DateTimeFieldType.dayOfMonth(), dayOfMonth, 1, getDaysInYearMonth(year, monthOfYear));
614 return getYearMonthDayMillis(year, monthOfYear, dayOfMonth);
615 }
616
617
624 abstract long getYearDifference(long minuendInstant, long subtrahendInstant);
625
626
632 abstract boolean isLeapYear(int year);
633
634
640 boolean isLeapDay(long instant) {
641 return false;
642 }
643
644
651 abstract int getDaysInYearMonth(int year, int month);
652
653
659 abstract int getDaysInMonthMax(int month);
660
661
669 abstract long getTotalMillisByYearMonth(int year, int month);
670
671
676 abstract long calculateFirstDayOfYearMillis(int year);
677
678
683 abstract int getMinYear();
684
685
690 abstract int getMaxYear();
691
692
699 int getMaxMonth(int year) {
700 return getMaxMonth();
701 }
702
703
708 int getMaxMonth() {
709 return 12;
710 }
711
712
717 abstract long getAverageMillisPerYear();
718
719
724 abstract long getAverageMillisPerYearDividedByTwo();
725
726
731 abstract long getAverageMillisPerMonth();
732
733
742 abstract long getApproxMillisAtEpochDividedByTwo();
743
744
751 abstract long setYear(long instant, int year);
752
753
754
755 private YearInfo getYearInfo(int year) {
756 YearInfo info = iYearInfoCache[year & CACHE_MASK];
757 if (info == null || info.iYear != year) {
758 info = new YearInfo(year, calculateFirstDayOfYearMillis(year));
759 iYearInfoCache[year & CACHE_MASK] = info;
760 }
761 return info;
762 }
763
764 private static class HalfdayField extends PreciseDateTimeField {
765 @SuppressWarnings("unused")
766 private static final long serialVersionUID = 581601443656929254L;
767
768 HalfdayField() {
769 super(DateTimeFieldType.halfdayOfDay(), cHalfdaysField, cDaysField);
770 }
771
772 public String getAsText(int fieldValue, Locale locale) {
773 return GJLocaleSymbols.forLocale(locale).halfdayValueToText(fieldValue);
774 }
775
776 public long set(long millis, String text, Locale locale) {
777 return set(millis, GJLocaleSymbols.forLocale(locale).halfdayTextToValue(text));
778 }
779
780 public int getMaximumTextLength(Locale locale) {
781 return GJLocaleSymbols.forLocale(locale).getHalfdayMaxTextLength();
782 }
783 }
784
785 private static class YearInfo {
786 public final int iYear;
787 public final long iFirstDayMillis;
788
789 YearInfo(int year, long firstDayMillis) {
790 iYear = year;
791 iFirstDayMillis = firstDayMillis;
792 }
793 }
794
795 }
796