View Javadoc

1   /*
2    *  Copyright 2001-2005 Stephen Colebourne
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
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  /**
37   * Abstract implementation for calendar systems that use a typical
38   * day/month/year/leapYear model.
39   * Most of the utility methods required by subclasses are package-private,
40   * reflecting the intention that they be defined in the same package.
41   * <p>
42   * BasicChronology is thread-safe and immutable, and all subclasses must
43   * be as well.
44   *
45   * @author Stephen Colebourne
46   * @author Brian S O'Neill
47   * @author Guy Allard
48   * @since 1.2, renamed from BaseGJChronology
49   */
50  abstract class BasicChronology extends AssembledChronology {
51  
52      /** Serialization lock */
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      * Checks if this chronology instance equals another.
190      * 
191      * @param obj  the object to compare to
192      * @return true if equal
193      * @since 1.6
194      */
195     public boolean equals(Object obj) {
196         return super.equals(obj);
197     }
198 
199     /**
200      * A suitable hash code for the chronology.
201      * 
202      * @return the hash code
203      * @since 1.6
204      */
205     public int hashCode() {
206         return getClass().getName().hashCode() * 11 + getZone().hashCode() + getMinimumDaysInFirstWeek();
207     }
208 
209     // Output
210     //-----------------------------------------------------------------------
211     /**
212      * Gets a debugging toString.
213      * 
214      * @return a debugging string
215      */
216     public String toString() {
217         StringBuilder sb = new StringBuilder(60);
218         String name = getClass().getName();
219         int index = name.lastIndexOf('.');
220         if (index >= 0) {
221             name = name.substring(index + 1);
222         }
223         sb.append(name);
224         sb.append('[');
225         DateTimeZone zone = getZone();
226         if (zone != null) {
227             sb.append(zone.getID());
228         }
229         if (getMinimumDaysInFirstWeek() != 4) {
230             sb.append(",mdfw=");
231             sb.append(getMinimumDaysInFirstWeek());
232         }
233         sb.append(']');
234         return sb.toString();
235     }
236 
237     protected void assemble(Fields fields) {
238         // First copy fields that are the same for all Gregorian and Julian
239         // chronologies.
240 
241         fields.millis = cMillisField;
242         fields.seconds = cSecondsField;
243         fields.minutes = cMinutesField;
244         fields.hours = cHoursField;
245         fields.halfdays = cHalfdaysField;
246         fields.days = cDaysField;
247         fields.weeks = cWeeksField;
248 
249         fields.millisOfSecond = cMillisOfSecondField;
250         fields.millisOfDay = cMillisOfDayField;
251         fields.secondOfMinute = cSecondOfMinuteField;
252         fields.secondOfDay = cSecondOfDayField;
253         fields.minuteOfHour = cMinuteOfHourField;
254         fields.minuteOfDay = cMinuteOfDayField;
255         fields.hourOfDay = cHourOfDayField;
256         fields.hourOfHalfday = cHourOfHalfdayField;
257         fields.clockhourOfDay = cClockhourOfDayField;
258         fields.clockhourOfHalfday = cClockhourOfHalfdayField;
259         fields.halfdayOfDay = cHalfdayOfDayField;
260 
261         // Now create fields that have unique behavior for Gregorian and Julian
262         // chronologies.
263 
264         fields.year = new BasicYearDateTimeField(this);
265         fields.yearOfEra = new GJYearOfEraDateTimeField(fields.year, this);
266 
267         // Define one-based centuryOfEra and yearOfCentury.
268         DateTimeField field = new OffsetDateTimeField(
269             fields.yearOfEra, 99);
270         fields.centuryOfEra = new DividedDateTimeField(
271             field, DateTimeFieldType.centuryOfEra(), 100);
272         
273         field = new RemainderDateTimeField(
274             (DividedDateTimeField) fields.centuryOfEra);
275         fields.yearOfCentury = new OffsetDateTimeField(
276             field, DateTimeFieldType.yearOfCentury(), 1);
277 
278         fields.era = new GJEraDateTimeField(this);
279         fields.dayOfWeek = new GJDayOfWeekDateTimeField(this, fields.days);
280         fields.dayOfMonth = new BasicDayOfMonthDateTimeField(this, fields.days);
281         fields.dayOfYear = new BasicDayOfYearDateTimeField(this, fields.days);
282         fields.monthOfYear = new GJMonthOfYearDateTimeField(this);
283         fields.weekyear = new BasicWeekyearDateTimeField(this);
284         fields.weekOfWeekyear = new BasicWeekOfWeekyearDateTimeField(this, fields.weeks);
285         
286         field = new RemainderDateTimeField(
287             fields.weekyear, DateTimeFieldType.weekyearOfCentury(), 100);
288         fields.weekyearOfCentury = new OffsetDateTimeField(
289             field, DateTimeFieldType.weekyearOfCentury(), 1);
290         
291         // The remaining (imprecise) durations are available from the newly
292         // created datetime fields.
293 
294         fields.years = fields.year.getDurationField();
295         fields.centuries = fields.centuryOfEra.getDurationField();
296         fields.months = fields.monthOfYear.getDurationField();
297         fields.weekyears = fields.weekyear.getDurationField();
298     }
299 
300     //-----------------------------------------------------------------------
301     /**
302      * Get the number of days in the year.
303      *
304      * @return 366
305      */
306     int getDaysInYearMax() {
307         return 366;
308     }
309 
310     /**
311      * Get the number of days in the year.
312      *
313      * @param year  the year to use
314      * @return 366 if a leap year, otherwise 365
315      */
316     int getDaysInYear(int year) {
317         return isLeapYear(year) ? 366 : 365;
318     }
319 
320     /**
321      * Get the number of weeks in the year.
322      *
323      * @param year  the year to use
324      * @return number of weeks in the year
325      */
326     int getWeeksInYear(int year) {
327         long firstWeekMillis1 = getFirstWeekOfYearMillis(year);
328         long firstWeekMillis2 = getFirstWeekOfYearMillis(year + 1);
329         return (int) ((firstWeekMillis2 - firstWeekMillis1) / DateTimeConstants.MILLIS_PER_WEEK);
330     }
331 
332     /**
333      * Get the millis for the first week of a year.
334      *
335      * @param year  the year to use
336      * @return millis
337      */
338     long getFirstWeekOfYearMillis(int year) {
339         long jan1millis = getYearMillis(year);
340         int jan1dayOfWeek = getDayOfWeek(jan1millis);
341         
342         if (jan1dayOfWeek > (8 - iMinDaysInFirstWeek)) {
343             // First week is end of previous year because it doesn't have enough days.
344             return jan1millis + (8 - jan1dayOfWeek)
345                 * (long)DateTimeConstants.MILLIS_PER_DAY;
346         } else {
347             // First week is start of this year because it has enough days.
348             return jan1millis - (jan1dayOfWeek - 1)
349                 * (long)DateTimeConstants.MILLIS_PER_DAY;
350         }
351     }
352 
353     /**
354      * Get the milliseconds for the start of a year.
355      *
356      * @param year The year to use.
357      * @return millis from 1970-01-01T00:00:00Z
358      */
359     long getYearMillis(int year) {
360         return getYearInfo(year).iFirstDayMillis;
361     }
362 
363     /**
364      * Get the milliseconds for the start of a month.
365      *
366      * @param year The year to use.
367      * @param month The month to use
368      * @return millis from 1970-01-01T00:00:00Z
369      */
370     long getYearMonthMillis(int year, int month) {
371         long millis = getYearMillis(year);
372         millis += getTotalMillisByYearMonth(year, month);
373         return millis;
374     }
375 
376     /**
377      * Get the milliseconds for a particular date.
378      *
379      * @param year The year to use.
380      * @param month The month to use
381      * @param dayOfMonth The day of the month to use
382      * @return millis from 1970-01-01T00:00:00Z
383      */
384     long getYearMonthDayMillis(int year, int month, int dayOfMonth) {
385         long millis = getYearMillis(year);
386         millis += getTotalMillisByYearMonth(year, month);
387         return millis + (dayOfMonth - 1) * (long)DateTimeConstants.MILLIS_PER_DAY;
388     }
389     
390     /**
391      * @param instant millis from 1970-01-01T00:00:00Z
392      */
393     int getYear(long instant) {
394         // Get an initial estimate of the year, and the millis value that
395         // represents the start of that year. Then verify estimate and fix if
396         // necessary.
397 
398         // Initial estimate uses values divided by two to avoid overflow.
399         long unitMillis = getAverageMillisPerYearDividedByTwo();
400         long i2 = (instant >> 1) + getApproxMillisAtEpochDividedByTwo();
401         if (i2 < 0) {
402             i2 = i2 - unitMillis + 1;
403         }
404         int year = (int) (i2 / unitMillis);
405 
406         long yearStart = getYearMillis(year);
407         long diff = instant - yearStart;
408 
409         if (diff < 0) {
410             year--;
411         } else if (diff >= DateTimeConstants.MILLIS_PER_DAY * 365L) {
412             // One year may need to be added to fix estimate.
413             long oneYear;
414             if (isLeapYear(year)) {
415                 oneYear = DateTimeConstants.MILLIS_PER_DAY * 366L;
416             } else {
417                 oneYear = DateTimeConstants.MILLIS_PER_DAY * 365L;
418             }
419 
420             yearStart += oneYear;
421 
422             if (yearStart <= instant) {
423                 // Didn't go too far, so actually add one year.
424                 year++;
425             }
426         }
427 
428         return year;
429     }
430 
431     /**
432      * @param millis from 1970-01-01T00:00:00Z
433      */
434     int getMonthOfYear(long millis) {
435         return getMonthOfYear(millis, getYear(millis));
436     }
437 
438     /**
439      * @param millis from 1970-01-01T00:00:00Z
440      * @param year precalculated year of millis
441      */
442     abstract int getMonthOfYear(long millis, int year);
443 
444     /**
445      * @param millis from 1970-01-01T00:00:00Z
446      */
447     int getDayOfMonth(long millis) {
448         int year = getYear(millis);
449         int month = getMonthOfYear(millis, year);
450         return getDayOfMonth(millis, year, month);
451     }
452 
453     /**
454      * @param millis from 1970-01-01T00:00:00Z
455      * @param year precalculated year of millis
456      */
457     int getDayOfMonth(long millis, int year) {
458         int month = getMonthOfYear(millis, year);
459         return getDayOfMonth(millis, year, month);
460     }
461 
462     /**
463      * @param millis from 1970-01-01T00:00:00Z
464      * @param year precalculated year of millis
465      * @param month precalculated month of millis
466      */
467     int getDayOfMonth(long millis, int year, int month) {
468         long dateMillis = getYearMillis(year);
469         dateMillis += getTotalMillisByYearMonth(year, month);
470         return (int) ((millis - dateMillis) / DateTimeConstants.MILLIS_PER_DAY) + 1;
471     }
472 
473     /**
474      * @param instant millis from 1970-01-01T00:00:00Z
475      */
476     int getDayOfYear(long instant) {
477         return getDayOfYear(instant, getYear(instant));
478     }
479 
480     /**
481      * @param instant millis from 1970-01-01T00:00:00Z
482      * @param year precalculated year of millis
483      */
484     int getDayOfYear(long instant, int year) {
485         long yearStart = getYearMillis(year);
486         return (int) ((instant - yearStart) / DateTimeConstants.MILLIS_PER_DAY) + 1;
487     }
488 
489     /**
490      * @param instant millis from 1970-01-01T00:00:00Z
491      */
492     int getWeekyear(long instant) {
493         int year = getYear(instant);
494         int week = getWeekOfWeekyear(instant, year);
495         if (week == 1) {
496             return getYear(instant + DateTimeConstants.MILLIS_PER_WEEK);
497         } else if (week > 51) {
498             return getYear(instant - (2 * DateTimeConstants.MILLIS_PER_WEEK));
499         } else {
500             return year;
501         }
502     }
503 
504     /**
505      * @param instant millis from 1970-01-01T00:00:00Z
506      */
507     int getWeekOfWeekyear(long instant) {
508         return getWeekOfWeekyear(instant, getYear(instant));
509     }
510 
511     /**
512      * @param instant millis from 1970-01-01T00:00:00Z
513      * @param year precalculated year of millis
514      */
515     int getWeekOfWeekyear(long instant, int year) {
516         long firstWeekMillis1 = getFirstWeekOfYearMillis(year);
517         if (instant < firstWeekMillis1) {
518             return getWeeksInYear(year - 1);
519         }
520         long firstWeekMillis2 = getFirstWeekOfYearMillis(year + 1);
521         if (instant >= firstWeekMillis2) {
522             return 1;
523         }
524         return (int) ((instant - firstWeekMillis1) / DateTimeConstants.MILLIS_PER_WEEK) + 1;
525     }
526 
527     /**
528      * @param instant millis from 1970-01-01T00:00:00Z
529      */
530     int getDayOfWeek(long instant) {
531         // 1970-01-01 is day of week 4, Thursday.
532 
533         long daysSince19700101;
534         if (instant >= 0) {
535             daysSince19700101 = instant / DateTimeConstants.MILLIS_PER_DAY;
536         } else {
537             daysSince19700101 = (instant - (DateTimeConstants.MILLIS_PER_DAY - 1))
538                 / DateTimeConstants.MILLIS_PER_DAY;
539             if (daysSince19700101 < -3) {
540                 return 7 + (int) ((daysSince19700101 + 4) % 7);
541             }
542         }
543 
544         return 1 + (int) ((daysSince19700101 + 3) % 7);
545     }
546 
547     /**
548      * @param instant millis from 1970-01-01T00:00:00Z
549      */
550     int getMillisOfDay(long instant) {
551         if (instant >= 0) {
552             return (int) (instant % DateTimeConstants.MILLIS_PER_DAY);
553         } else {
554             return (DateTimeConstants.MILLIS_PER_DAY - 1)
555                 + (int) ((instant + 1) % DateTimeConstants.MILLIS_PER_DAY);
556         }
557     }
558 
559     /**
560      * Gets the maximum number of days in any month.
561      * 
562      * @return 31
563      */
564     int getDaysInMonthMax() {
565         return 31;
566     }
567 
568     /**
569      * Gets the maximum number of days in the month specified by the instant.
570      * 
571      * @param instant  millis from 1970-01-01T00:00:00Z
572      * @return the maximum number of days in the month
573      */
574     int getDaysInMonthMax(long instant) {
575         int thisYear = getYear(instant);
576         int thisMonth = getMonthOfYear(instant, thisYear);
577         return getDaysInYearMonth(thisYear, thisMonth);
578     }
579 
580     /**
581      * Gets the maximum number of days in the month specified by the instant.
582      * The value represents what the user is trying to set, and can be
583      * used to optimise this method.
584      * 
585      * @param instant  millis from 1970-01-01T00:00:00Z
586      * @param value  the value being set
587      * @return the maximum number of days in the month
588      */
589     int getDaysInMonthMaxForSet(long instant, int value) {
590         return getDaysInMonthMax(instant);
591     }
592 
593     //-----------------------------------------------------------------------
594     /**
595      * Gets the milliseconds for a date at midnight.
596      * 
597      * @param year  the year
598      * @param monthOfYear  the month
599      * @param dayOfMonth  the day
600      * @return the milliseconds
601      */
602     long getDateMidnightMillis(int year, int monthOfYear, int dayOfMonth) {
603         FieldUtils.verifyValueBounds(DateTimeFieldType.year(), year, getMinYear(), getMaxYear());
604         FieldUtils.verifyValueBounds(DateTimeFieldType.monthOfYear(), monthOfYear, 1, getMaxMonth(year));
605         FieldUtils.verifyValueBounds(DateTimeFieldType.dayOfMonth(), dayOfMonth, 1, getDaysInYearMonth(year, monthOfYear));
606         return getYearMonthDayMillis(year, monthOfYear, dayOfMonth);
607     }
608 
609     /**
610      * Gets the difference between the two instants in years.
611      * 
612      * @param minuendInstant  the first instant
613      * @param subtrahendInstant  the second instant
614      * @return the difference
615      */
616     abstract long getYearDifference(long minuendInstant, long subtrahendInstant);
617 
618     /**
619      * Is the specified year a leap year?
620      * 
621      * @param year  the year to test
622      * @return true if leap
623      */
624     abstract boolean isLeapYear(int year);
625 
626     /**
627      * Gets the number of days in the specified month and year.
628      * 
629      * @param year  the year
630      * @param month  the month
631      * @return the number of days
632      */
633     abstract int getDaysInYearMonth(int year, int month);
634 
635     /**
636      * Gets the maximum days in the specified month.
637      * 
638      * @param month  the month
639      * @return the max days
640      */
641     abstract int getDaysInMonthMax(int month);
642 
643     /**
644      * Gets the total number of millis elapsed in this year at the start
645      * of the specified month, such as zero for month 1.
646      * 
647      * @param year  the year
648      * @param month  the month
649      * @return the elapsed millis at the start of the month
650      */
651     abstract long getTotalMillisByYearMonth(int year, int month);
652 
653     /**
654      * Gets the millisecond value of the first day of the year.
655      * 
656      * @return the milliseconds for the first of the year
657      */
658     abstract long calculateFirstDayOfYearMillis(int year);
659 
660     /**
661      * Gets the minimum supported year.
662      * 
663      * @return the year
664      */
665     abstract int getMinYear();
666 
667     /**
668      * Gets the maximum supported year.
669      * 
670      * @return the year
671      */
672     abstract int getMaxYear();
673 
674     /**
675      * Gets the maximum month for the specified year.
676      * This implementation calls getMaxMonth().
677      * 
678      * @param year  the year
679      * @return the maximum month value
680      */
681     int getMaxMonth(int year) {
682         return getMaxMonth();
683     }
684 
685     /**
686      * Gets the maximum number of months.
687      * 
688      * @return 12
689      */
690     int getMaxMonth() {
691         return 12;
692     }
693 
694     /**
695      * Gets an average value for the milliseconds per year.
696      * 
697      * @return the millis per year
698      */
699     abstract long getAverageMillisPerYear();
700 
701     /**
702      * Gets an average value for the milliseconds per year, divided by two.
703      * 
704      * @return the millis per year divided by two
705      */
706     abstract long getAverageMillisPerYearDividedByTwo();
707 
708     /**
709      * Gets an average value for the milliseconds per month.
710      * 
711      * @return the millis per month
712      */
713     abstract long getAverageMillisPerMonth();
714 
715     /**
716      * Returns a constant representing the approximate number of milliseconds
717      * elapsed from year 0 of this chronology, divided by two. This constant
718      * <em>must</em> be defined as:
719      * <pre>
720      *    (yearAtEpoch * averageMillisPerYear + millisOfYearAtEpoch) / 2
721      * </pre>
722      * where epoch is 1970-01-01 (Gregorian).
723      */
724     abstract long getApproxMillisAtEpochDividedByTwo();
725 
726     /**
727      * Sets the year from an instant and year.
728      * 
729      * @param instant  millis from 1970-01-01T00:00:00Z
730      * @param year  the year to set
731      * @return the updated millis
732      */
733     abstract long setYear(long instant, int year);
734 
735     //-----------------------------------------------------------------------
736     // Although accessed by multiple threads, this method doesn't need to be synchronized.
737     private YearInfo getYearInfo(int year) {
738         YearInfo info = iYearInfoCache[year & CACHE_MASK];
739         if (info == null || info.iYear != year) {
740             info = new YearInfo(year, calculateFirstDayOfYearMillis(year));
741             iYearInfoCache[year & CACHE_MASK] = info;
742         }
743         return info;
744     }
745 
746     private static class HalfdayField extends PreciseDateTimeField {
747         private static final long serialVersionUID = 581601443656929254L;
748 
749         HalfdayField() {
750             super(DateTimeFieldType.halfdayOfDay(), cHalfdaysField, cDaysField);
751         }
752 
753         public String getAsText(int fieldValue, Locale locale) {
754             return GJLocaleSymbols.forLocale(locale).halfdayValueToText(fieldValue);
755         }
756 
757         public long set(long millis, String text, Locale locale) {
758             return set(millis, GJLocaleSymbols.forLocale(locale).halfdayTextToValue(text));
759         }
760 
761         public int getMaximumTextLength(Locale locale) {
762             return GJLocaleSymbols.forLocale(locale).getHalfdayMaxTextLength();
763         }
764     }
765 
766     private static class YearInfo {
767         public final int iYear;
768         public final long iFirstDayMillis;
769 
770         YearInfo(int year, long firstDayMillis) {
771             iYear = year;
772             iFirstDayMillis = firstDayMillis;
773         }
774     }
775 
776 }