1 /*
2  *  Copyright 2001-2014 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     /**
190      * Checks if this chronology instance equals another.
191      * 
192      * @param obj  the object to compare to
193      * @return true if equal
194      * @since 1.6
195      */

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     /**
209      * A suitable hash code for the chronology.
210      * 
211      * @return the hash code
212      * @since 1.6
213      */

214     public int hashCode() {
215         return getClass().getName().hashCode() * 11 + getZone().hashCode() + getMinimumDaysInFirstWeek();
216     }
217
218     // Output
219     //-----------------------------------------------------------------------
220     /**
221      * Gets a debugging toString.
222      * 
223      * @return a debugging string
224      */

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         // First copy fields that are the same for all Gregorian and Julian
248         // chronologies.
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         // Now create fields that have unique behavior for Gregorian and Julian
271         // chronologies.
272
273         fields.year = new BasicYearDateTimeField(this);
274         fields.yearOfEra = new GJYearOfEraDateTimeField(fields.year, this);
275
276         // Define one-based centuryOfEra and yearOfCentury.
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         // The remaining (imprecise) durations are available from the newly
302         // created datetime fields.
303         fields.years = fields.year.getDurationField();
304         fields.months = fields.monthOfYear.getDurationField();
305         fields.weekyears = fields.weekyear.getDurationField();
306     }
307
308     //-----------------------------------------------------------------------
309     /**
310      * Get the number of days in the year.
311      *
312      * @return 366
313      */

314     int getDaysInYearMax() {
315         return 366;
316     }
317
318     /**
319      * Get the number of days in the year.
320      *
321      * @param year  the year to use
322      * @return 366 if a leap year, otherwise 365
323      */

324     int getDaysInYear(int year) {
325         return isLeapYear(year) ? 366 : 365;
326     }
327
328     /**
329      * Get the number of weeks in the year.
330      *
331      * @param year  the year to use
332      * @return number of weeks in the year
333      */

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     /**
341      * Get the millis for the first week of a year.
342      *
343      * @param year  the year to use
344      * @return millis
345      */

346     long getFirstWeekOfYearMillis(int year) {
347         long jan1millis = getYearMillis(year);
348         int jan1dayOfWeek = getDayOfWeek(jan1millis);
349         
350         if (jan1dayOfWeek > (8 - iMinDaysInFirstWeek)) {
351             // First week is end of previous year because it doesn't have enough days.
352             return jan1millis + (8 - jan1dayOfWeek)
353                 * (long)DateTimeConstants.MILLIS_PER_DAY;
354         } else {
355             // First week is start of this year because it has enough days.
356             return jan1millis - (jan1dayOfWeek - 1)
357                 * (long)DateTimeConstants.MILLIS_PER_DAY;
358         }
359     }
360
361     /**
362      * Get the milliseconds for the start of a year.
363      *
364      * @param year The year to use.
365      * @return millis from 1970-01-01T00:00:00Z
366      */

367     long getYearMillis(int year) {
368         return getYearInfo(year).iFirstDayMillis;
369     }
370
371     /**
372      * Get the milliseconds for the start of a month.
373      *
374      * @param year The year to use.
375      * @param month The month to use
376      * @return millis from 1970-01-01T00:00:00Z
377      */

378     long getYearMonthMillis(int year, int month) {
379         long millis = getYearMillis(year);
380         millis += getTotalMillisByYearMonth(year, month);
381         return millis;
382     }
383
384     /**
385      * Get the milliseconds for a particular date.
386      *
387      * @param year The year to use.
388      * @param month The month to use
389      * @param dayOfMonth The day of the month to use
390      * @return millis from 1970-01-01T00:00:00Z
391      */

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     /**
399      * @param instant millis from 1970-01-01T00:00:00Z
400      */

401     int getYear(long instant) {
402         // Get an initial estimate of the year, and the millis value that
403         // represents the start of that year. Then verify estimate and fix if
404         // necessary.
405
406         // Initial estimate uses values divided by two to avoid overflow.
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             // One year may need to be added to fix estimate.
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                 // Didn't go too far, so actually add one year.
432                 year++;
433             }
434         }
435
436         return year;
437     }
438
439     /**
440      * @param millis from 1970-01-01T00:00:00Z
441      */

442     int getMonthOfYear(long millis) {
443         return getMonthOfYear(millis, getYear(millis));
444     }
445
446     /**
447      * @param millis from 1970-01-01T00:00:00Z
448      * @param year precalculated year of millis
449      */

450     abstract int getMonthOfYear(long millis, int year);
451
452     /**
453      * @param millis from 1970-01-01T00:00:00Z
454      */

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     /**
462      * @param millis from 1970-01-01T00:00:00Z
463      * @param year precalculated year of millis
464      */

465     int getDayOfMonth(long millis, int year) {
466         int month = getMonthOfYear(millis, year);
467         return getDayOfMonth(millis, year, month);
468     }
469
470     /**
471      * @param millis from 1970-01-01T00:00:00Z
472      * @param year precalculated year of millis
473      * @param month precalculated month of millis
474      */

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     /**
482      * @param instant millis from 1970-01-01T00:00:00Z
483      */

484     int getDayOfYear(long instant) {
485         return getDayOfYear(instant, getYear(instant));
486     }
487
488     /**
489      * @param instant millis from 1970-01-01T00:00:00Z
490      * @param year precalculated year of millis
491      */

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     /**
498      * @param instant millis from 1970-01-01T00:00:00Z
499      */

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     /**
513      * @param instant millis from 1970-01-01T00:00:00Z
514      */

515     int getWeekOfWeekyear(long instant) {
516         return getWeekOfWeekyear(instant, getYear(instant));
517     }
518
519     /**
520      * @param instant millis from 1970-01-01T00:00:00Z
521      * @param year precalculated year of millis
522      */

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     /**
536      * @param instant millis from 1970-01-01T00:00:00Z
537      */

538     int getDayOfWeek(long instant) {
539         // 1970-01-01 is day of week 4, Thursday.
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     /**
556      * @param instant millis from 1970-01-01T00:00:00Z
557      */

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     /**
568      * Gets the maximum number of days in any month.
569      * 
570      * @return 31
571      */

572     int getDaysInMonthMax() {
573         return 31;
574     }
575
576     /**
577      * Gets the maximum number of days in the month specified by the instant.
578      * 
579      * @param instant  millis from 1970-01-01T00:00:00Z
580      * @return the maximum number of days in the month
581      */

582     int getDaysInMonthMax(long instant) {
583         int thisYear = getYear(instant);
584         int thisMonth = getMonthOfYear(instant, thisYear);
585         return getDaysInYearMonth(thisYear, thisMonth);
586     }
587
588     /**
589      * Gets the maximum number of days in the month specified by the instant.
590      * The value represents what the user is trying to set, and can be
591      * used to optimise this method.
592      * 
593      * @param instant  millis from 1970-01-01T00:00:00Z
594      * @param value  the value being set
595      * @return the maximum number of days in the month
596      */

597     int getDaysInMonthMaxForSet(long instant, int value) {
598         return getDaysInMonthMax(instant);
599     }
600
601     //-----------------------------------------------------------------------
602     /**
603      * Gets the milliseconds for a date at midnight.
604      * 
605      * @param year  the year
606      * @param monthOfYear  the month
607      * @param dayOfMonth  the day
608      * @return the milliseconds
609      */

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     /**
618      * Gets the difference between the two instants in years.
619      * 
620      * @param minuendInstant  the first instant
621      * @param subtrahendInstant  the second instant
622      * @return the difference
623      */

624     abstract long getYearDifference(long minuendInstant, long subtrahendInstant);
625
626     /**
627      * Is the specified year a leap year?
628      * 
629      * @param year  the year to test
630      * @return true if leap
631      */

632     abstract boolean isLeapYear(int year);
633
634     /**
635      * Is the specified instant a leap day?
636      * 
637      * @param instant  the instant to test
638      * @return true if leap, default is false
639      */

640     boolean isLeapDay(long instant) {
641         return false;
642     }
643
644     /**
645      * Gets the number of days in the specified month and year.
646      * 
647      * @param year  the year
648      * @param month  the month
649      * @return the number of days
650      */

651     abstract int getDaysInYearMonth(int year, int month);
652
653     /**
654      * Gets the maximum days in the specified month.
655      * 
656      * @param month  the month
657      * @return the max days
658      */

659     abstract int getDaysInMonthMax(int month);
660
661     /**
662      * Gets the total number of millis elapsed in this year at the start
663      * of the specified month, such as zero for month 1.
664      * 
665      * @param year  the year
666      * @param month  the month
667      * @return the elapsed millis at the start of the month
668      */

669     abstract long getTotalMillisByYearMonth(int year, int month);
670
671     /**
672      * Gets the millisecond value of the first day of the year.
673      * 
674      * @return the milliseconds for the first of the year
675      */

676     abstract long calculateFirstDayOfYearMillis(int year);
677
678     /**
679      * Gets the minimum supported year.
680      * 
681      * @return the year
682      */

683     abstract int getMinYear();
684
685     /**
686      * Gets the maximum supported year.
687      * 
688      * @return the year
689      */

690     abstract int getMaxYear();
691
692     /**
693      * Gets the maximum month for the specified year.
694      * This implementation calls getMaxMonth().
695      * 
696      * @param year  the year
697      * @return the maximum month value
698      */

699     int getMaxMonth(int year) {
700         return getMaxMonth();
701     }
702
703     /**
704      * Gets the maximum number of months.
705      * 
706      * @return 12
707      */

708     int getMaxMonth() {
709         return 12;
710     }
711
712     /**
713      * Gets an average value for the milliseconds per year.
714      * 
715      * @return the millis per year
716      */

717     abstract long getAverageMillisPerYear();
718
719     /**
720      * Gets an average value for the milliseconds per year, divided by two.
721      * 
722      * @return the millis per year divided by two
723      */

724     abstract long getAverageMillisPerYearDividedByTwo();
725
726     /**
727      * Gets an average value for the milliseconds per month.
728      * 
729      * @return the millis per month
730      */

731     abstract long getAverageMillisPerMonth();
732
733     /**
734      * Returns a constant representing the approximate number of milliseconds
735      * elapsed from year 0 of this chronology, divided by two. This constant
736      * <em>must</em> be defined as:
737      * <pre>
738      *    (yearAtEpoch * averageMillisPerYear + millisOfYearAtEpoch) / 2
739      * </pre>
740      * where epoch is 1970-01-01 (Gregorian).
741      */

742     abstract long getApproxMillisAtEpochDividedByTwo();
743
744     /**
745      * Sets the year from an instant and year.
746      * 
747      * @param instant  millis from 1970-01-01T00:00:00Z
748      * @param year  the year to set
749      * @return the updated millis
750      */

751     abstract long setYear(long instant, int year);
752
753     //-----------------------------------------------------------------------
754     // Although accessed by multiple threads, this method doesn't need to be synchronized.
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