1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package java.util;
19 
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 
24 /**
25  * {@code GregorianCalendar} is a concrete subclass of {@link Calendar}
26  * and provides the standard calendar used by most of the world.
27  *
28  * <p>
29  * The standard (Gregorian) calendar has 2 eras, BC and AD.
30  *
31  * <p>
32  * This implementation handles a single discontinuity, which corresponds by
33  * default to the date the Gregorian calendar was instituted (October 15, 1582
34  * in some countries, later in others). The cutover date may be changed by the
35  * caller by calling {@code setGregorianChange()}.
36  *
37  * <p>
38  * Historically, in those countries which adopted the Gregorian calendar first,
39  * October 4, 1582 was thus followed by October 15, 1582. This calendar models
40  * this correctly. Before the Gregorian cutover, {@code GregorianCalendar}
41  * implements the Julian calendar. The only difference between the Gregorian and
42  * the Julian calendar is the leap year rule. The Julian calendar specifies leap
43  * years every four years, whereas the Gregorian calendar omits century years
44  * which are not divisible by 400.
45  *
46  * <p>
47  * {@code GregorianCalendar} implements <em>proleptic</em> Gregorian
48  * and Julian calendars. That is, dates are computed by extrapolating the
49  * current rules indefinitely far backward and forward in time. As a result,
50  * {@code GregorianCalendar} may be used for all years to generate
51  * meaningful and consistent results. However, dates obtained using
52  * {@code GregorianCalendar} are historically accurate only from March 1,
53  * 4 AD onward, when modern Julian calendar rules were adopted. Before this
54  * date, leap year rules were applied irregularly, and before 45 BC the Julian
55  * calendar did not even exist.
56  *
57  * <p>
58  * Prior to the institution of the Gregorian calendar, New Year's Day was March
59  * 25. To avoid confusion, this calendar always uses January 1. A manual
60  * adjustment may be made if desired for dates that are prior to the Gregorian
61  * changeover and which fall between January 1 and March 24.
62  *
63  * <p>
64  * Values calculated for the {@code WEEK_OF_YEAR} field range from 1 to
65  * 53. Week 1 for a year is the earliest seven day period starting on
66  * {@code getFirstDayOfWeek()} that contains at least
67  * {@code getMinimalDaysInFirstWeek()} days from that year. It thus
68  * depends on the values of {@code getMinimalDaysInFirstWeek()},
69  * {@code getFirstDayOfWeek()}, and the day of the week of January 1.
70  * Weeks between week 1 of one year and week 1 of the following year are
71  * numbered sequentially from 2 to 52 or 53 (as needed).
72  *
73  * <p>
74  * For example, January 1, 1998 was a Thursday. If
75  * {@code getFirstDayOfWeek()} is {@code MONDAY} and
76  * {@code getMinimalDaysInFirstWeek()} is 4 (these are the values
77  * reflecting ISO 8601 and many national standards), then week 1 of 1998 starts
78  * on December 29, 1997, and ends on January 4, 1998. If, however,
79  * {@code getFirstDayOfWeek()} is {@code SUNDAY}, then week 1 of
80  * 1998 starts on January 4, 1998, and ends on January 10, 1998; the first three
81  * days of 1998 then are part of week 53 of 1997.
82  *
83  * <p>
84  * Values calculated for the {@code WEEK_OF_MONTH} field range from 0 or
85  * 1 to 4 or 5. Week 1 of a month (the days with <code>WEEK_OF_MONTH =
86  * 1</code>)
87  * is the earliest set of at least {@code getMinimalDaysInFirstWeek()}
88  * contiguous days in that month, ending on the day before
89  * {@code getFirstDayOfWeek()}. Unlike week 1 of a year, week 1 of a
90  * month may be shorter than 7 days, need not start on
91  * {@code getFirstDayOfWeek()}, and will not include days of the
92  * previous month. Days of a month before week 1 have a
93  * {@code WEEK_OF_MONTH} of 0.
94  *
95  * <p>
96  * For example, if {@code getFirstDayOfWeek()} is {@code SUNDAY}
97  * and {@code getMinimalDaysInFirstWeek()} is 4, then the first week of
98  * January 1998 is Sunday, January 4 through Saturday, January 10. These days
99  * have a {@code WEEK_OF_MONTH} of 1. Thursday, January 1 through
100  * Saturday, January 3 have a {@code WEEK_OF_MONTH} of 0. If
101  * {@code getMinimalDaysInFirstWeek()} is changed to 3, then January 1
102  * through January 3 have a {@code WEEK_OF_MONTH} of 1.
103  *
104  * <p>
105  * <strong>Example:</strong> <blockquote>
106  *
107  * <pre>
108  * // get the supported ids for GMT-08:00 (Pacific Standard Time)
109  * String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000);
110  * // if no ids were returned, something is wrong. get out.
111  * if (ids.length == 0)
112  *     System.exit(0);
113  *
114  *  // begin output
115  * System.out.println("Current Time");
116  *
117  * // create a Pacific Standard Time time zone
118  * SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, ids[0]);
119  *
120  * // set up rules for daylight savings time
121  * pdt.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);
122  * pdt.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);
123  *
124  * // create a GregorianCalendar with the Pacific Daylight time zone
125  * // and the current date and time
126  * Calendar calendar = new GregorianCalendar(pdt);
127  * Date trialTime = new Date();
128  * calendar.setTime(trialTime);
129  *
130  * // print out a bunch of interesting things
131  * System.out.println("ERA: " + calendar.get(Calendar.ERA));
132  * System.out.println("YEAR: " + calendar.get(Calendar.YEAR));
133  * System.out.println("MONTH: " + calendar.get(Calendar.MONTH));
134  * System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR));
135  * System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH));
136  * System.out.println("DATE: " + calendar.get(Calendar.DATE));
137  * System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));
138  * System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR));
139  * System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK));
140  * System.out.println("DAY_OF_WEEK_IN_MONTH: "
141  *                    + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));
142  * System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));
143  * System.out.println("HOUR: " + calendar.get(Calendar.HOUR));
144  * System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));
145  * System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));
146  * System.out.println("SECOND: " + calendar.get(Calendar.SECOND));
147  * System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));
148  * System.out.println("ZONE_OFFSET: "
149  *                    + (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000)));
150  * System.out.println("DST_OFFSET: "
151  *                    + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000)));
152 
153  * System.out.println("Current Time, with hour reset to 3");
154  * calendar.clear(Calendar.HOUR_OF_DAY); // so doesn't override
155  * calendar.set(Calendar.HOUR, 3);
156  * System.out.println("ERA: " + calendar.get(Calendar.ERA));
157  * System.out.println("YEAR: " + calendar.get(Calendar.YEAR));
158  * System.out.println("MONTH: " + calendar.get(Calendar.MONTH));
159  * System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR));
160  * System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH));
161  * System.out.println("DATE: " + calendar.get(Calendar.DATE));
162  * System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));
163  * System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR));
164  * System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK));
165  * System.out.println("DAY_OF_WEEK_IN_MONTH: "
166  *                    + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));
167  * System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));
168  * System.out.println("HOUR: " + calendar.get(Calendar.HOUR));
169  * System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));
170  * System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));
171  * System.out.println("SECOND: " + calendar.get(Calendar.SECOND));
172  * System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));
173  * System.out.println("ZONE_OFFSET: "
174  *        + (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000))); // in hours
175  * System.out.println("DST_OFFSET: "
176  *        + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000))); // in hours
177  * </pre>
178  *
179  * </blockquote>
180  *
181  * @see Calendar
182  * @see TimeZone
183  */
184 public class GregorianCalendar extends Calendar {
185 
186     private static final long serialVersionUID = -8125100834729963327L;
187 
188     /**
189      * Value for the BC era.
190      */
191     public static final int BC = 0;
192 
193     /**
194      * Value for the AD era.
195      */
196     public static final int AD = 1;
197 
198     private static final long defaultGregorianCutover = -12219292800000l;
199 
200     private long gregorianCutover = defaultGregorianCutover;
201 
202     private transient int changeYear = 1582;
203 
204     private transient int julianSkew = ((changeYear - 2000) / 400)
205             + julianError() - ((changeYear - 2000) / 100);
206 
207     static byte[] DaysInMonth = new byte[] { 31, 28, 31, 30, 31, 30, 31, 31,
208             30, 31, 30, 31 };
209 
210     private static int[] DaysInYear = new int[] { 0, 31, 59, 90, 120, 151, 181,
211             212, 243, 273, 304, 334 };
212 
213     private static int[] maximums = new int[] { 1, 292278994, 11, 53, 6, 31,
214             366, 7, 6, 1, 11, 23, 59, 59, 999, 14 * 3600 * 1000, 7200000 };
215 
216     private static int[] minimums = new int[] { 0, 1, 0, 1, 0, 1, 1, 1, 1, 0,
217             0, 0, 0, 0, 0, -13 * 3600 * 1000, 0 };
218 
219     private static int[] leastMaximums = new int[] { 1, 292269054, 11, 50, 3,
220             28, 355, 7, 3, 1, 11, 23, 59, 59, 999, 50400000, 1200000 };
221 
222     private int currentYearSkew = 10;
223 
224     private int lastYearSkew = 0;
225 
226     /**
227      * Constructs a new {@code GregorianCalendar} initialized to the current date and
228      * time with the default {@code Locale} and {@code TimeZone}.
229      */
GregorianCalendar()230     public GregorianCalendar() {
231         this(TimeZone.getDefault(), Locale.getDefault());
232     }
233 
234     /**
235      * Constructs a new {@code GregorianCalendar} initialized to midnight in the default
236      * {@code TimeZone} and {@code Locale} on the specified date.
237      *
238      * @param year
239      *            the year.
240      * @param month
241      *            the month.
242      * @param day
243      *            the day of the month.
244      */
GregorianCalendar(int year, int month, int day)245     public GregorianCalendar(int year, int month, int day) {
246         super(TimeZone.getDefault(), Locale.getDefault());
247         set(year, month, day);
248     }
249 
250     /**
251      * Constructs a new {@code GregorianCalendar} initialized to the specified date and
252      * time in the default {@code TimeZone} and {@code Locale}.
253      *
254      * @param year
255      *            the year.
256      * @param month
257      *            the month.
258      * @param day
259      *            the day of the month.
260      * @param hour
261      *            the hour.
262      * @param minute
263      *            the minute.
264      */
GregorianCalendar(int year, int month, int day, int hour, int minute)265     public GregorianCalendar(int year, int month, int day, int hour, int minute) {
266         super(TimeZone.getDefault(), Locale.getDefault());
267         set(year, month, day, hour, minute);
268     }
269 
270     /**
271      * Constructs a new {@code GregorianCalendar} initialized to the specified date and
272      * time in the default {@code TimeZone} and {@code Locale}.
273      *
274      * @param year
275      *            the year.
276      * @param month
277      *            the month.
278      * @param day
279      *            the day of the month.
280      * @param hour
281      *            the hour.
282      * @param minute
283      *            the minute.
284      * @param second
285      *            the second.
286      */
GregorianCalendar(int year, int month, int day, int hour, int minute, int second)287     public GregorianCalendar(int year, int month, int day, int hour,
288             int minute, int second) {
289         super(TimeZone.getDefault(), Locale.getDefault());
290         set(year, month, day, hour, minute, second);
291     }
292 
GregorianCalendar(long milliseconds)293     GregorianCalendar(long milliseconds) {
294         this(false);
295         setTimeInMillis(milliseconds);
296     }
297 
298     /**
299      * Constructs a new {@code GregorianCalendar} initialized to the current date and
300      * time and using the specified {@code Locale} and the default {@code TimeZone}.
301      *
302      * @param locale
303      *            the {@code Locale}.
304      */
GregorianCalendar(Locale locale)305     public GregorianCalendar(Locale locale) {
306         this(TimeZone.getDefault(), locale);
307     }
308 
309     /**
310      * Constructs a new {@code GregorianCalendar} initialized to the current date and
311      * time and using the specified {@code TimeZone} and the default {@code Locale}.
312      *
313      * @param timezone
314      *            the {@code TimeZone}.
315      */
GregorianCalendar(TimeZone timezone)316     public GregorianCalendar(TimeZone timezone) {
317         this(timezone, Locale.getDefault());
318     }
319 
320     /**
321      * Constructs a new {@code GregorianCalendar} initialized to the current date and
322      * time and using the specified {@code TimeZone} and {@code Locale}.
323      *
324      * @param timezone
325      *            the {@code TimeZone}.
326      * @param locale
327      *            the {@code Locale}.
328      */
GregorianCalendar(TimeZone timezone, Locale locale)329     public GregorianCalendar(TimeZone timezone, Locale locale) {
330         super(timezone, locale);
331         setTimeInMillis(System.currentTimeMillis());
332     }
333 
334     /**
335      * A minimum-cost constructor that does not initialize the current time or perform any date
336      * calculations. For use internally when the time will be set later. Other constructors, such as
337      * {@link GregorianCalendar#GregorianCalendar()}, set the time to the current system clock
338      * and recalculate the fields incurring unnecessary cost when the time or fields will be set
339      * later.
340      *
341      * @hide used internally
342      */
GregorianCalendar(boolean ignored)343     public GregorianCalendar(boolean ignored) {
344         super(TimeZone.getDefault());
345         setFirstDayOfWeek(SUNDAY);
346         setMinimalDaysInFirstWeek(1);
347     }
348 
349     /**
350      * Adds the specified amount to a {@code Calendar} field.
351      *
352      * @param field
353      *            the {@code Calendar} field to modify.
354      * @param value
355      *            the amount to add to the field.
356      *
357      * @throws IllegalArgumentException
358      *                if the specified field is DST_OFFSET or ZONE_OFFSET.
359      */
360     @Override
add(int field, int value)361     public void add(int field, int value) {
362         if (value == 0) {
363             return;
364         }
365         if (field < 0 || field >= ZONE_OFFSET) {
366             throw new IllegalArgumentException();
367         }
368 
369         if (field == ERA) {
370             complete();
371             if (fields[ERA] == AD) {
372                 if (value >= 0) {
373                     return;
374                 }
375                 set(ERA, BC);
376             } else {
377                 if (value <= 0) {
378                     return;
379                 }
380                 set(ERA, AD);
381             }
382             complete();
383             return;
384         }
385 
386         if (field == YEAR || field == MONTH) {
387             complete();
388             if (field == MONTH) {
389                 int month = fields[MONTH] + value;
390                 if (month < 0) {
391                     value = (month - 11) / 12;
392                     month = 12 + (month % 12);
393                 } else {
394                     value = month / 12;
395                 }
396                 set(MONTH, month % 12);
397             }
398             set(YEAR, fields[YEAR] + value);
399             int days = daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]);
400             if (fields[DATE] > days) {
401                 set(DATE, days);
402             }
403             complete();
404             return;
405         }
406 
407         long multiplier = 0;
408         getTimeInMillis(); // Update the time
409         switch (field) {
410             case MILLISECOND:
411                 time += value;
412                 break;
413             case SECOND:
414                 time += value * 1000L;
415                 break;
416             case MINUTE:
417                 time += value * 60000L;
418                 break;
419             case HOUR:
420             case HOUR_OF_DAY:
421                 time += value * 3600000L;
422                 break;
423             case AM_PM:
424                 multiplier = 43200000L;
425                 break;
426             case DATE:
427             case DAY_OF_YEAR:
428             case DAY_OF_WEEK:
429                 multiplier = 86400000L;
430                 break;
431             case WEEK_OF_YEAR:
432             case WEEK_OF_MONTH:
433             case DAY_OF_WEEK_IN_MONTH:
434                 multiplier = 604800000L;
435                 break;
436         }
437 
438         if (multiplier == 0) {
439             areFieldsSet = false;
440             complete();
441             return;
442         }
443 
444         long delta = value * multiplier;
445 
446         /*
447          * Attempt to keep the hour and minute constant when we've crossed a DST
448          * boundary and the user's units are AM_PM or larger. The typical
449          * consequence is that calls to add(DATE, 1) will add 23, 24 or 25 hours
450          * depending on whether the DST goes forward, constant, or backward.
451          *
452          * We know we've crossed a DST boundary if the new time will have a
453          * different timezone offset. Adjust by adding the difference of the two
454          * offsets. We don't adjust when doing so prevents the change from
455          * crossing the boundary.
456          */
457         int zoneOffset = getTimeZone().getRawOffset();
458         int offsetBefore = getOffset(time + zoneOffset);
459         int offsetAfter = getOffset(time + zoneOffset + delta);
460         int dstDelta = offsetBefore - offsetAfter;
461         if (getOffset(time + zoneOffset + delta + dstDelta) == offsetAfter) {
462             delta += dstDelta;
463         }
464 
465         time += delta;
466         areFieldsSet = false;
467         complete();
468     }
469 
fullFieldsCalc()470     private void fullFieldsCalc() {
471         int millis = (int) (time % 86400000);
472         long days = time / 86400000;
473 
474         if (millis < 0) {
475             millis += 86400000;
476             days--;
477         }
478         // Adding fields[ZONE_OFFSET] to time might make it overflow, so we add
479         // it to millis (the number of milliseconds in the current day) instead.
480         millis += fields[ZONE_OFFSET];
481         while (millis < 0) {
482             millis += 86400000;
483             days--;
484         }
485         while (millis >= 86400000) {
486             millis -= 86400000;
487             days++;
488         }
489 
490         int dayOfYear = computeYearAndDay(days, time + fields[ZONE_OFFSET]);
491         fields[DAY_OF_YEAR] = dayOfYear;
492         if (fields[YEAR] == changeYear && gregorianCutover <= time + fields[ZONE_OFFSET]){
493             dayOfYear += currentYearSkew;
494         }
495         int month = dayOfYear / 32;
496         boolean leapYear = isLeapYear(fields[YEAR]);
497         int date = dayOfYear - daysInYear(leapYear, month);
498         if (date > daysInMonth(leapYear, month)) {
499             date -= daysInMonth(leapYear, month);
500             month++;
501         }
502         fields[DAY_OF_WEEK] = mod7(days - 3) + 1;
503         int dstOffset = fields[YEAR] <= 0 ? 0 : getTimeZone().getOffset(AD,
504                 fields[YEAR], month, date, fields[DAY_OF_WEEK], millis);
505         if (fields[YEAR] > 0) {
506             dstOffset -= fields[ZONE_OFFSET];
507         }
508         fields[DST_OFFSET] = dstOffset;
509         if (dstOffset != 0) {
510             long oldDays = days;
511             millis += dstOffset;
512             if (millis < 0) {
513                 millis += 86400000;
514                 days--;
515             } else if (millis >= 86400000) {
516                 millis -= 86400000;
517                 days++;
518             }
519             if (oldDays != days) {
520                 dayOfYear = computeYearAndDay(days, time - fields[ZONE_OFFSET]
521                         + dstOffset);
522                 fields[DAY_OF_YEAR] = dayOfYear;
523                 if(fields[YEAR] == changeYear && gregorianCutover <= time - fields[ZONE_OFFSET] + dstOffset){
524                     dayOfYear += currentYearSkew;
525                 }
526                 month = dayOfYear / 32;
527                 leapYear = isLeapYear(fields[YEAR]);
528                 date = dayOfYear - daysInYear(leapYear, month);
529                 if (date > daysInMonth(leapYear, month)) {
530                     date -= daysInMonth(leapYear, month);
531                     month++;
532                 }
533                 fields[DAY_OF_WEEK] = mod7(days - 3) + 1;
534             }
535         }
536 
537         fields[MILLISECOND] = (millis % 1000);
538         millis /= 1000;
539         fields[SECOND] = (millis % 60);
540         millis /= 60;
541         fields[MINUTE] = (millis % 60);
542         millis /= 60;
543         fields[HOUR_OF_DAY] = (millis % 24);
544         fields[AM_PM] = fields[HOUR_OF_DAY] > 11 ? 1 : 0;
545         fields[HOUR] = fields[HOUR_OF_DAY] % 12;
546 
547         if (fields[YEAR] <= 0) {
548             fields[ERA] = BC;
549             fields[YEAR] = -fields[YEAR] + 1;
550         } else {
551             fields[ERA] = AD;
552         }
553         fields[MONTH] = month;
554         fields[DATE] = date;
555         fields[DAY_OF_WEEK_IN_MONTH] = (date - 1) / 7 + 1;
556         fields[WEEK_OF_MONTH] = (date - 1 + mod7(days - date - 2
557                 - (getFirstDayOfWeek() - 1))) / 7 + 1;
558         int daysFromStart = mod7(days - 3 - (fields[DAY_OF_YEAR] - 1)
559                 - (getFirstDayOfWeek() - 1));
560         int week = (fields[DAY_OF_YEAR] - 1 + daysFromStart) / 7
561                 + (7 - daysFromStart >= getMinimalDaysInFirstWeek() ? 1 : 0);
562         if (week == 0) {
563             fields[WEEK_OF_YEAR] = 7 - mod7(daysFromStart
564                     - (isLeapYear(fields[YEAR] - 1) ? 2 : 1)) >= getMinimalDaysInFirstWeek() ? 53
565                     : 52;
566         } else if (fields[DAY_OF_YEAR] >= (leapYear ? 367 : 366)
567                 - mod7(daysFromStart + (leapYear ? 2 : 1))) {
568             fields[WEEK_OF_YEAR] = 7 - mod7(daysFromStart + (leapYear ? 2 : 1)) >= getMinimalDaysInFirstWeek() ? 1
569                     : week;
570         } else {
571             fields[WEEK_OF_YEAR] = week;
572         }
573     }
574 
575     @Override
computeFields()576     protected void computeFields() {
577         TimeZone timeZone = getTimeZone();
578         int dstOffset = timeZone.inDaylightTime(new Date(time)) ? timeZone.getDSTSavings() : 0;
579         int zoneOffset = timeZone.getRawOffset();
580 
581         // We unconditionally overwrite DST_OFFSET and ZONE_OFFSET with
582         // values from the timezone that's currently in use. This gives us
583         // much more consistent behavior, and matches ICU4J behavior (though
584         // it is inconsistent with the RI).
585         //
586         // Anything callers can do with ZONE_OFFSET they can do by constructing
587         // a SimpleTimeZone with the required offset.
588         //
589         // DST_OFFSET is a bit of a WTF, given that it's dependent on the rest
590         // of the fields. There's no sensible reason we'd want to allow it to
591         // be set, nor can we implement consistent full-fields calculation after
592         // this field is set without maintaining a large deal of additional state.
593         //
594         // At the very least, we will need isSet to differentiate between fields
595         // set by the user and fields set by our internal field calculation.
596         fields[DST_OFFSET] = dstOffset;
597         fields[ZONE_OFFSET] = zoneOffset;
598 
599         fullFieldsCalc();
600 
601         for (int i = 0; i < FIELD_COUNT; i++) {
602             isSet[i] = true;
603         }
604     }
605 
606     @Override
computeTime()607     protected void computeTime() {
608         if (!isLenient()) {
609             if (isSet[HOUR_OF_DAY]) {
610                 if (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23) {
611                     throw new IllegalArgumentException();
612                 }
613             } else if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) {
614                 throw new IllegalArgumentException();
615             }
616             if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59)) {
617                 throw new IllegalArgumentException();
618             }
619             if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59)) {
620                 throw new IllegalArgumentException();
621             }
622             if (isSet[MILLISECOND]
623                     && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999)) {
624                 throw new IllegalArgumentException();
625             }
626             if (isSet[WEEK_OF_YEAR]
627                     && (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > 53)) {
628                 throw new IllegalArgumentException();
629             }
630             if (isSet[DAY_OF_WEEK]
631                     && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7)) {
632                 throw new IllegalArgumentException();
633             }
634             if (isSet[DAY_OF_WEEK_IN_MONTH]
635                     && (fields[DAY_OF_WEEK_IN_MONTH] < 1 || fields[DAY_OF_WEEK_IN_MONTH] > 6)) {
636                 throw new IllegalArgumentException();
637             }
638             if (isSet[WEEK_OF_MONTH]
639                     && (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > 6)) {
640                 throw new IllegalArgumentException();
641             }
642             if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM) {
643                 throw new IllegalArgumentException();
644             }
645             if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) {
646                 throw new IllegalArgumentException();
647             }
648             if (isSet[YEAR]) {
649                 if (isSet[ERA] && fields[ERA] == BC
650                         && (fields[YEAR] < 1 || fields[YEAR] > 292269054)) {
651                     throw new IllegalArgumentException();
652                 } else if (fields[YEAR] < 1 || fields[YEAR] > 292278994) {
653                     throw new IllegalArgumentException();
654                 }
655             }
656             if (isSet[MONTH] && (fields[MONTH] < 0 || fields[MONTH] > 11)) {
657                 throw new IllegalArgumentException();
658             }
659         }
660 
661         long timeVal;
662         long hour = 0;
663         if (isSet[HOUR_OF_DAY] && lastTimeFieldSet != HOUR) {
664             hour = fields[HOUR_OF_DAY];
665         } else if (isSet[HOUR]) {
666             hour = (fields[AM_PM] * 12) + fields[HOUR];
667         }
668         timeVal = hour * 3600000;
669 
670         if (isSet[MINUTE]) {
671             timeVal += ((long) fields[MINUTE]) * 60000;
672         }
673         if (isSet[SECOND]) {
674             timeVal += ((long) fields[SECOND]) * 1000;
675         }
676         if (isSet[MILLISECOND]) {
677             timeVal += fields[MILLISECOND];
678         }
679 
680         long days;
681         int year = isSet[YEAR] ? fields[YEAR] : 1970;
682         if (isSet[ERA]) {
683             // Always test for valid ERA, even if the Calendar is lenient
684             if (fields[ERA] != BC && fields[ERA] != AD) {
685                 throw new IllegalArgumentException();
686             }
687             if (fields[ERA] == BC) {
688                 year = 1 - year;
689             }
690         }
691 
692         boolean weekMonthSet = isSet[WEEK_OF_MONTH]
693                 || isSet[DAY_OF_WEEK_IN_MONTH];
694         boolean useMonth = (isSet[DATE] || isSet[MONTH] || weekMonthSet)
695                 && lastDateFieldSet != DAY_OF_YEAR;
696         if (useMonth
697                 && (lastDateFieldSet == DAY_OF_WEEK || lastDateFieldSet == WEEK_OF_YEAR)) {
698             if (isSet[WEEK_OF_YEAR] && isSet[DAY_OF_WEEK]) {
699                 if (lastDateFieldSet == WEEK_OF_YEAR) {
700                     useMonth = false;
701                 } else if (lastDateFieldSet == DAY_OF_WEEK) {
702                     // DAY_OF_WEEK belongs to both the Month + Week + Day and the
703                     // WeekOfYear + Day combinations. We're supposed to use the most
704                     // recent combination, as specified by the single set field. We can't
705                     // know for sure in this case, so we always prefer the week-month-day
706                     // combination if week-month is already set.
707                     useMonth = weekMonthSet;
708                 }
709             } else if (isSet[DAY_OF_YEAR]) {
710                 useMonth = isSet[DATE] && isSet[MONTH];
711             }
712         }
713 
714         if (useMonth) {
715             int month = fields[MONTH];
716             year += month / 12;
717             month %= 12;
718             if (month < 0) {
719                 year--;
720                 month += 12;
721             }
722             boolean leapYear = isLeapYear(year);
723             days = daysFromBaseYear(year) + daysInYear(leapYear, month);
724             boolean useDate = isSet[DATE];
725             if (useDate
726                     && (lastDateFieldSet == DAY_OF_WEEK
727                             || lastDateFieldSet == WEEK_OF_MONTH || lastDateFieldSet == DAY_OF_WEEK_IN_MONTH)) {
728                 useDate = !(isSet[DAY_OF_WEEK] && weekMonthSet);
729             }
730             if (useDate) {
731                 if (!isLenient()
732                         && (fields[DATE] < 1 || fields[DATE] > daysInMonth(
733                                 leapYear, month))) {
734                     throw new IllegalArgumentException();
735                 }
736                 days += fields[DATE] - 1;
737             } else {
738                 int dayOfWeek;
739                 if (isSet[DAY_OF_WEEK]) {
740                     dayOfWeek = fields[DAY_OF_WEEK] - 1;
741                 } else {
742                     dayOfWeek = getFirstDayOfWeek() - 1;
743                 }
744                 if (isSet[WEEK_OF_MONTH]
745                         && lastDateFieldSet != DAY_OF_WEEK_IN_MONTH) {
746                     int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1));
747                     days += (fields[WEEK_OF_MONTH] - 1) * 7
748                             + mod7(skew + dayOfWeek - (days - 3)) - skew;
749                 } else if (isSet[DAY_OF_WEEK_IN_MONTH]) {
750                     if (fields[DAY_OF_WEEK_IN_MONTH] >= 0) {
751                         days += mod7(dayOfWeek - (days - 3))
752                                 + (fields[DAY_OF_WEEK_IN_MONTH] - 1) * 7;
753                     } else {
754                         days += daysInMonth(leapYear, month)
755                                 + mod7(dayOfWeek
756                                         - (days + daysInMonth(leapYear, month) - 3))
757                                 + fields[DAY_OF_WEEK_IN_MONTH] * 7;
758                     }
759                 } else if (isSet[DAY_OF_WEEK]) {
760                     int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1));
761                     days += mod7(mod7(skew + dayOfWeek - (days - 3)) - skew);
762                 }
763             }
764         } else {
765             boolean useWeekYear = isSet[WEEK_OF_YEAR]
766                     && lastDateFieldSet != DAY_OF_YEAR;
767             if (useWeekYear && isSet[DAY_OF_YEAR]) {
768                 useWeekYear = isSet[DAY_OF_WEEK];
769             }
770             days = daysFromBaseYear(year);
771             if (useWeekYear) {
772                 int dayOfWeek;
773                 if (isSet[DAY_OF_WEEK]) {
774                     dayOfWeek = fields[DAY_OF_WEEK] - 1;
775                 } else {
776                     dayOfWeek = getFirstDayOfWeek() - 1;
777                 }
778                 int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1));
779                 days += (fields[WEEK_OF_YEAR] - 1) * 7
780                         + mod7(skew + dayOfWeek - (days - 3)) - skew;
781                 if (7 - skew < getMinimalDaysInFirstWeek()) {
782                     days += 7;
783                 }
784             } else if (isSet[DAY_OF_YEAR]) {
785                 if (!isLenient()
786                         && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > (365 + (isLeapYear(year) ? 1
787                                 : 0)))) {
788                     throw new IllegalArgumentException();
789                 }
790                 days += fields[DAY_OF_YEAR] - 1;
791             } else if (isSet[DAY_OF_WEEK]) {
792                 days += mod7(fields[DAY_OF_WEEK] - 1 - (days - 3));
793             }
794         }
795         lastDateFieldSet = 0;
796 
797         timeVal += days * 86400000;
798         // Use local time to compare with the gregorian change
799         if (year == changeYear
800                 && timeVal >= gregorianCutover + julianError() * 86400000L) {
801             timeVal -= julianError() * 86400000L;
802         }
803 
804         // It is not possible to simply subtract getOffset(timeVal) from timeVal
805         // to get UTC.
806         // The trick is needed for the moment when DST transition occurs,
807         // say 1:00 is a transition time when DST offset becomes +1 hour,
808         // then wall time in the interval 1:00 - 2:00 is invalid and is
809         // treated as UTC time.
810         long timeValWithoutDST = timeVal - getOffset(timeVal)
811                 + getTimeZone().getRawOffset();
812         timeVal -= getOffset(timeValWithoutDST);
813         // Need to update wall time in fields, since it was invalid due to DST
814         // transition
815         this.time = timeVal;
816         if (timeValWithoutDST != timeVal) {
817             computeFields();
818             areFieldsSet = true;
819         }
820     }
821 
computeYearAndDay(long dayCount, long localTime)822     private int computeYearAndDay(long dayCount, long localTime) {
823         int year = 1970;
824         long days = dayCount;
825         if (localTime < gregorianCutover) {
826             days -= julianSkew;
827         }
828         int approxYears;
829 
830         while ((approxYears = (int) (days / 365)) != 0) {
831             year = year + approxYears;
832             days = dayCount - daysFromBaseYear(year);
833         }
834         if (days < 0) {
835             year = year - 1;
836             days = days + daysInYear(year);
837         }
838         fields[YEAR] = year;
839         return (int) days + 1;
840     }
841 
daysFromBaseYear(long year)842     private long daysFromBaseYear(long year) {
843         if (year >= 1970) {
844             long days = (year - 1970) * 365 + ((year - 1969) / 4);
845             if (year > changeYear) {
846                 days -= ((year - 1901) / 100) - ((year - 1601) / 400);
847             } else {
848                 if (year == changeYear) {
849                     days += currentYearSkew;
850                 } else if (year == changeYear - 1) {
851                     days += lastYearSkew;
852                 } else {
853                     days += julianSkew;
854                 }
855             }
856             return days;
857         } else if (year <= changeYear) {
858             return (year - 1970) * 365 + ((year - 1972) / 4) + julianSkew;
859         }
860         return (year - 1970) * 365 + ((year - 1972) / 4)
861                 - ((year - 2000) / 100) + ((year - 2000) / 400);
862     }
863 
daysInMonth()864     private int daysInMonth() {
865         return daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]);
866     }
867 
daysInMonth(boolean leapYear, int month)868     private int daysInMonth(boolean leapYear, int month) {
869         if (leapYear && month == FEBRUARY) {
870             return DaysInMonth[month] + 1;
871         }
872 
873         return DaysInMonth[month];
874     }
875 
daysInYear(int year)876     private int daysInYear(int year) {
877         int daysInYear = isLeapYear(year) ? 366 : 365;
878         if (year == changeYear) {
879             daysInYear -= currentYearSkew;
880         }
881         if (year == changeYear - 1) {
882             daysInYear -= lastYearSkew;
883         }
884         return daysInYear;
885     }
886 
daysInYear(boolean leapYear, int month)887     private int daysInYear(boolean leapYear, int month) {
888         if (leapYear && month > FEBRUARY) {
889             return DaysInYear[month] + 1;
890         }
891 
892         return DaysInYear[month];
893     }
894 
895     /**
896      * Returns true if {@code object} is a GregorianCalendar with the same
897      * properties.
898      */
equals(Object object)899     @Override public boolean equals(Object object) {
900         if (!(object instanceof GregorianCalendar)) {
901             return false;
902         }
903         if (object == this) {
904             return true;
905         }
906         return super.equals(object)
907                 && gregorianCutover == ((GregorianCalendar) object).gregorianCutover;
908     }
909 
getActualMaximum(int field)910     @Override public int getActualMaximum(int field) {
911         int value;
912         if ((value = maximums[field]) == leastMaximums[field]) {
913             return value;
914         }
915 
916         complete();
917         long orgTime = time;
918         int result = 0;
919         switch (field) {
920             case WEEK_OF_YEAR:
921                 set(DATE, 31);
922                 set(MONTH, DECEMBER);
923                 result = get(WEEK_OF_YEAR);
924                 if (result == 1) {
925                     set(DATE, 31 - 7);
926                     result = get(WEEK_OF_YEAR);
927                 }
928                 areFieldsSet = false;
929                 break;
930             case WEEK_OF_MONTH:
931                 set(DATE, daysInMonth());
932                 result = get(WEEK_OF_MONTH);
933                 areFieldsSet = false;
934                 break;
935             case DATE:
936                 return daysInMonth();
937             case DAY_OF_YEAR:
938                 return daysInYear(fields[YEAR]);
939             case DAY_OF_WEEK_IN_MONTH:
940                 result = get(DAY_OF_WEEK_IN_MONTH)
941                         + ((daysInMonth() - get(DATE)) / 7);
942                 break;
943             case YEAR:
944                 GregorianCalendar clone = (GregorianCalendar) clone();
945                 if (get(ERA) == AD) {
946                     clone.setTimeInMillis(Long.MAX_VALUE);
947                 } else {
948                     clone.setTimeInMillis(Long.MIN_VALUE);
949                 }
950                 result = clone.get(YEAR);
951                 clone.set(YEAR, get(YEAR));
952                 if (clone.before(this)) {
953                     result--;
954                 }
955                 break;
956             case DST_OFFSET:
957                 result = getMaximum(DST_OFFSET);
958                 break;
959         }
960         time = orgTime;
961         return result;
962     }
963 
964     /**
965      * Gets the minimum value of the specified field for the current date. For
966      * the gregorian calendar, this value is the same as
967      * {@code getMinimum()}.
968      *
969      * @param field
970      *            the field.
971      * @return the minimum value of the specified field.
972      */
973     @Override
getActualMinimum(int field)974     public int getActualMinimum(int field) {
975         return getMinimum(field);
976     }
977 
978     /**
979      * Gets the greatest minimum value of the specified field. For the gregorian
980      * calendar, this value is the same as {@code getMinimum()}.
981      *
982      * @param field
983      *            the field.
984      * @return the greatest minimum value of the specified field.
985      */
986     @Override
getGreatestMinimum(int field)987     public int getGreatestMinimum(int field) {
988         return minimums[field];
989     }
990 
991     /**
992      * Returns the gregorian change date of this calendar. This is the date on
993      * which the gregorian calendar came into effect.
994      *
995      * @return a {@code Date} which represents the gregorian change date.
996      */
getGregorianChange()997     public final Date getGregorianChange() {
998         return new Date(gregorianCutover);
999     }
1000 
1001     /**
1002      * Gets the smallest maximum value of the specified field. For example, 28
1003      * for the day of month field.
1004      *
1005      * @param field
1006      *            the field.
1007      * @return the smallest maximum value of the specified field.
1008      */
1009     @Override
getLeastMaximum(int field)1010     public int getLeastMaximum(int field) {
1011         // return value for WEEK_OF_YEAR should make corresponding changes when
1012         // the gregorian change date have been reset.
1013         if (gregorianCutover != defaultGregorianCutover
1014                 && field == WEEK_OF_YEAR) {
1015             long currentTimeInMillis = time;
1016             setTimeInMillis(gregorianCutover);
1017             int actual = getActualMaximum(field);
1018             setTimeInMillis(currentTimeInMillis);
1019             return actual;
1020         }
1021         return leastMaximums[field];
1022     }
1023 
1024     /**
1025      * Gets the greatest maximum value of the specified field. For example, 31
1026      * for the day of month field.
1027      *
1028      * @param field
1029      *            the field.
1030      * @return the greatest maximum value of the specified field.
1031      */
1032     @Override
getMaximum(int field)1033     public int getMaximum(int field) {
1034         return maximums[field];
1035     }
1036 
1037     /**
1038      * Gets the smallest minimum value of the specified field.
1039      *
1040      * @param field
1041      *            the field.
1042      * @return the smallest minimum value of the specified field.
1043      */
1044     @Override
getMinimum(int field)1045     public int getMinimum(int field) {
1046         return minimums[field];
1047     }
1048 
getOffset(long localTime)1049     private int getOffset(long localTime) {
1050         TimeZone timeZone = getTimeZone();
1051 
1052         long dayCount = localTime / 86400000;
1053         int millis = (int) (localTime % 86400000);
1054         if (millis < 0) {
1055             millis += 86400000;
1056             dayCount--;
1057         }
1058 
1059         int year = 1970;
1060         long days = dayCount;
1061         if (localTime < gregorianCutover) {
1062             days -= julianSkew;
1063         }
1064         int approxYears;
1065 
1066         while ((approxYears = (int) (days / 365)) != 0) {
1067             year = year + approxYears;
1068             days = dayCount - daysFromBaseYear(year);
1069         }
1070         if (days < 0) {
1071             year = year - 1;
1072             days = days + 365 + (isLeapYear(year) ? 1 : 0);
1073             if (year == changeYear && localTime < gregorianCutover) {
1074                 days -= julianError();
1075             }
1076         }
1077         if (year <= 0) {
1078             return timeZone.getRawOffset();
1079         }
1080         int dayOfYear = (int) days + 1;
1081 
1082         int month = dayOfYear / 32;
1083         boolean leapYear = isLeapYear(year);
1084         int date = dayOfYear - daysInYear(leapYear, month);
1085         if (date > daysInMonth(leapYear, month)) {
1086             date -= daysInMonth(leapYear, month);
1087             month++;
1088         }
1089         int dayOfWeek = mod7(dayCount - 3) + 1;
1090         return timeZone.getOffset(AD, year, month, date, dayOfWeek, millis);
1091     }
1092 
hashCode()1093     @Override public int hashCode() {
1094         return super.hashCode()
1095                 + ((int) (gregorianCutover >>> 32) ^ (int) gregorianCutover);
1096     }
1097 
1098     /**
1099      * Returns true if {@code year} is a leap year.
1100      */
isLeapYear(int year)1101     public boolean isLeapYear(int year) {
1102         if (year > changeYear) {
1103             return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
1104         }
1105 
1106         return year % 4 == 0;
1107     }
1108 
julianError()1109     private int julianError() {
1110         return changeYear / 100 - changeYear / 400 - 2;
1111     }
1112 
mod(int value, int mod)1113     private int mod(int value, int mod) {
1114         int rem = value % mod;
1115         if (value < 0 && rem < 0) {
1116             return rem + mod;
1117         }
1118         return rem;
1119     }
1120 
mod7(long num1)1121     private int mod7(long num1) {
1122         int rem = (int) (num1 % 7);
1123         if (num1 < 0 && rem < 0) {
1124             return rem + 7;
1125         }
1126         return rem;
1127     }
1128 
1129     /**
1130      * Adds the specified amount the specified field and wraps the value of the
1131      * field when it goes beyond the maximum or minimum value for the current
1132      * date. Other fields will be adjusted as required to maintain a consistent
1133      * date.
1134      *
1135      * @param field
1136      *            the field to roll.
1137      * @param value
1138      *            the amount to add.
1139      *
1140      * @throws IllegalArgumentException
1141      *                if an invalid field is specified.
1142      */
1143     @Override
roll(int field, int value)1144     public void roll(int field, int value) {
1145         if (value == 0) {
1146             return;
1147         }
1148         if (field < 0 || field >= ZONE_OFFSET) {
1149             throw new IllegalArgumentException();
1150         }
1151 
1152         complete();
1153         int days, day, mod, maxWeeks, newWeek;
1154         int max = -1;
1155         switch (field) {
1156         case YEAR:
1157             max = maximums[field];
1158             break;
1159         case WEEK_OF_YEAR:
1160             days = daysInYear(fields[YEAR]);
1161             day = DAY_OF_YEAR;
1162             mod = mod7(fields[DAY_OF_WEEK] - fields[day]
1163                     - (getFirstDayOfWeek() - 1));
1164             maxWeeks = (days - 1 + mod) / 7 + 1;
1165             newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1;
1166             if (newWeek == maxWeeks) {
1167                 int addDays = (newWeek - fields[field]) * 7;
1168                 if (fields[day] > addDays && fields[day] + addDays > days) {
1169                     set(field, 1);
1170                 } else {
1171                     set(field, newWeek - 1);
1172                 }
1173             } else if (newWeek == 1) {
1174                 int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1;
1175                 if (week > 1) {
1176                     set(field, 1);
1177                 } else {
1178                     set(field, newWeek);
1179                 }
1180             } else {
1181                 set(field, newWeek);
1182             }
1183             break;
1184         case WEEK_OF_MONTH:
1185             days = daysInMonth();
1186             day = DATE;
1187             mod = mod7(fields[DAY_OF_WEEK] - fields[day]
1188                     - (getFirstDayOfWeek() - 1));
1189             maxWeeks = (days - 1 + mod) / 7 + 1;
1190             newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1;
1191             if (newWeek == maxWeeks) {
1192                 if (fields[day] + (newWeek - fields[field]) * 7 > days) {
1193                     set(day, days);
1194                 } else {
1195                     set(field, newWeek);
1196                 }
1197             } else if (newWeek == 1) {
1198                 int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1;
1199                 if (week > 1) {
1200                     set(day, 1);
1201                 } else {
1202                     set(field, newWeek);
1203                 }
1204             } else {
1205                 set(field, newWeek);
1206             }
1207             break;
1208         case DATE:
1209             max = daysInMonth();
1210             break;
1211         case DAY_OF_YEAR:
1212             max = daysInYear(fields[YEAR]);
1213             break;
1214         case DAY_OF_WEEK:
1215             max = maximums[field];
1216             lastDateFieldSet = WEEK_OF_MONTH;
1217             break;
1218         case DAY_OF_WEEK_IN_MONTH:
1219             max = (fields[DATE] + ((daysInMonth() - fields[DATE]) / 7 * 7) - 1) / 7 + 1;
1220             break;
1221 
1222         case ERA:
1223         case MONTH:
1224         case AM_PM:
1225         case HOUR:
1226         case HOUR_OF_DAY:
1227         case MINUTE:
1228         case SECOND:
1229         case MILLISECOND:
1230             set(field, mod(fields[field] + value, maximums[field] + 1));
1231             if (field == MONTH && fields[DATE] > daysInMonth()) {
1232                 set(DATE, daysInMonth());
1233             } else if (field == AM_PM) {
1234                 lastTimeFieldSet = HOUR;
1235             }
1236             break;
1237         }
1238         if (max != -1) {
1239             set(field, mod(fields[field] - 1 + value, max) + 1);
1240         }
1241         complete();
1242     }
1243 
1244     /**
1245      * Increments or decrements the specified field and wraps the value of the
1246      * field when it goes beyond the maximum or minimum value for the current
1247      * date. Other fields will be adjusted as required to maintain a consistent
1248      * date. For example, March 31 will roll to April 30 when rolling the month
1249      * field.
1250      *
1251      * @param field
1252      *            the field to roll.
1253      * @param increment
1254      *            {@code true} to increment the field, {@code false} to
1255      *            decrement.
1256      * @throws IllegalArgumentException
1257      *                if an invalid field is specified.
1258      */
1259     @Override
roll(int field, boolean increment)1260     public void roll(int field, boolean increment) {
1261         roll(field, increment ? 1 : -1);
1262     }
1263 
1264     /**
1265      * Sets the gregorian change date of this calendar.
1266      */
setGregorianChange(Date date)1267     public void setGregorianChange(Date date) {
1268         gregorianCutover = date.getTime();
1269         GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
1270         cal.setTime(date);
1271         changeYear = cal.get(YEAR);
1272         if (cal.get(ERA) == BC) {
1273             changeYear = 1 - changeYear;
1274         }
1275         julianSkew = ((changeYear - 2000) / 400) + julianError()
1276                 - ((changeYear - 2000) / 100);
1277         int dayOfYear = cal.get(DAY_OF_YEAR);
1278         if (dayOfYear < julianSkew) {
1279             currentYearSkew = dayOfYear-1;
1280             lastYearSkew = julianSkew - dayOfYear + 1;
1281         } else {
1282             lastYearSkew = 0;
1283             currentYearSkew = julianSkew;
1284         }
1285     }
1286 
writeObject(ObjectOutputStream stream)1287     private void writeObject(ObjectOutputStream stream) throws IOException {
1288         stream.defaultWriteObject();
1289     }
1290 
readObject(ObjectInputStream stream)1291     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
1292         stream.defaultReadObject();
1293         setGregorianChange(new Date(gregorianCutover));
1294     }
1295 }
1296