Home | History | Annotate | Download | only in util
      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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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      */
    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      */
    329     public GregorianCalendar(TimeZone timezone, Locale locale) {
    330         super(timezone, locale);
    331         setTimeInMillis(System.currentTimeMillis());
    332     }
    333 
    334     GregorianCalendar(boolean ignored) {
    335         super(TimeZone.getDefault());
    336         setFirstDayOfWeek(SUNDAY);
    337         setMinimalDaysInFirstWeek(1);
    338     }
    339 
    340     /**
    341      * Adds the specified amount to a {@code Calendar} field.
    342      *
    343      * @param field
    344      *            the {@code Calendar} field to modify.
    345      * @param value
    346      *            the amount to add to the field.
    347      *
    348      * @throws IllegalArgumentException
    349      *                if the specified field is DST_OFFSET or ZONE_OFFSET.
    350      */
    351     @Override
    352     public void add(int field, int value) {
    353         if (value == 0) {
    354             return;
    355         }
    356         if (field < 0 || field >= ZONE_OFFSET) {
    357             throw new IllegalArgumentException();
    358         }
    359 
    360         if (field == ERA) {
    361             complete();
    362             if (fields[ERA] == AD) {
    363                 if (value >= 0) {
    364                     return;
    365                 }
    366                 set(ERA, BC);
    367             } else {
    368                 if (value <= 0) {
    369                     return;
    370                 }
    371                 set(ERA, AD);
    372             }
    373             complete();
    374             return;
    375         }
    376 
    377         if (field == YEAR || field == MONTH) {
    378             complete();
    379             if (field == MONTH) {
    380                 int month = fields[MONTH] + value;
    381                 if (month < 0) {
    382                     value = (month - 11) / 12;
    383                     month = 12 + (month % 12);
    384                 } else {
    385                     value = month / 12;
    386                 }
    387                 set(MONTH, month % 12);
    388             }
    389             set(YEAR, fields[YEAR] + value);
    390             int days = daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]);
    391             if (fields[DATE] > days) {
    392                 set(DATE, days);
    393             }
    394             complete();
    395             return;
    396         }
    397 
    398         long multiplier = 0;
    399         getTimeInMillis(); // Update the time
    400         switch (field) {
    401             case MILLISECOND:
    402                 time += value;
    403                 break;
    404             case SECOND:
    405                 time += value * 1000L;
    406                 break;
    407             case MINUTE:
    408                 time += value * 60000L;
    409                 break;
    410             case HOUR:
    411             case HOUR_OF_DAY:
    412                 time += value * 3600000L;
    413                 break;
    414             case AM_PM:
    415                 multiplier = 43200000L;
    416                 break;
    417             case DATE:
    418             case DAY_OF_YEAR:
    419             case DAY_OF_WEEK:
    420                 multiplier = 86400000L;
    421                 break;
    422             case WEEK_OF_YEAR:
    423             case WEEK_OF_MONTH:
    424             case DAY_OF_WEEK_IN_MONTH:
    425                 multiplier = 604800000L;
    426                 break;
    427         }
    428 
    429         if (multiplier == 0) {
    430             areFieldsSet = false;
    431             complete();
    432             return;
    433         }
    434 
    435         long delta = value * multiplier;
    436 
    437         /*
    438          * Attempt to keep the hour and minute constant when we've crossed a DST
    439          * boundary and the user's units are AM_PM or larger. The typical
    440          * consequence is that calls to add(DATE, 1) will add 23, 24 or 25 hours
    441          * depending on whether the DST goes forward, constant, or backward.
    442          *
    443          * We know we've crossed a DST boundary if the new time will have a
    444          * different timezone offset. Adjust by adding the difference of the two
    445          * offsets. We don't adjust when doing so prevents the change from
    446          * crossing the boundary.
    447          */
    448         int zoneOffset = getTimeZone().getRawOffset();
    449         int offsetBefore = getOffset(time + zoneOffset);
    450         int offsetAfter = getOffset(time + zoneOffset + delta);
    451         int dstDelta = offsetBefore - offsetAfter;
    452         if (getOffset(time + zoneOffset + delta + dstDelta) == offsetAfter) {
    453             delta += dstDelta;
    454         }
    455 
    456         time += delta;
    457         areFieldsSet = false;
    458         complete();
    459     }
    460 
    461     private void fullFieldsCalc(long timeVal, int zoneOffset) {
    462         int millis = (int) (time % 86400000);
    463         long days = timeVal / 86400000;
    464 
    465         if (millis < 0) {
    466             millis += 86400000;
    467             days--;
    468         }
    469         // Cannot add ZONE_OFFSET to time as it might overflow
    470         millis += zoneOffset;
    471         while (millis < 0) {
    472             millis += 86400000;
    473             days--;
    474         }
    475         while (millis >= 86400000) {
    476             millis -= 86400000;
    477             days++;
    478         }
    479 
    480         int dayOfYear = computeYearAndDay(days, timeVal + zoneOffset);
    481         fields[DAY_OF_YEAR] = dayOfYear;
    482         if(fields[YEAR] == changeYear && gregorianCutover <= timeVal + zoneOffset){
    483             dayOfYear += currentYearSkew;
    484         }
    485         int month = dayOfYear / 32;
    486         boolean leapYear = isLeapYear(fields[YEAR]);
    487         int date = dayOfYear - daysInYear(leapYear, month);
    488         if (date > daysInMonth(leapYear, month)) {
    489             date -= daysInMonth(leapYear, month);
    490             month++;
    491         }
    492         fields[DAY_OF_WEEK] = mod7(days - 3) + 1;
    493         int dstOffset = fields[YEAR] <= 0 ? 0 : getTimeZone().getOffset(AD,
    494                 fields[YEAR], month, date, fields[DAY_OF_WEEK], millis);
    495         if (fields[YEAR] > 0) {
    496             dstOffset -= zoneOffset;
    497         }
    498         fields[DST_OFFSET] = dstOffset;
    499         if (dstOffset != 0) {
    500             long oldDays = days;
    501             millis += dstOffset;
    502             if (millis < 0) {
    503                 millis += 86400000;
    504                 days--;
    505             } else if (millis >= 86400000) {
    506                 millis -= 86400000;
    507                 days++;
    508             }
    509             if (oldDays != days) {
    510                 dayOfYear = computeYearAndDay(days, timeVal - zoneOffset
    511                         + dstOffset);
    512                 fields[DAY_OF_YEAR] = dayOfYear;
    513                 if(fields[YEAR] == changeYear && gregorianCutover <= timeVal - zoneOffset + dstOffset){
    514                     dayOfYear += currentYearSkew;
    515                 }
    516                 month = dayOfYear / 32;
    517                 leapYear = isLeapYear(fields[YEAR]);
    518                 date = dayOfYear - daysInYear(leapYear, month);
    519                 if (date > daysInMonth(leapYear, month)) {
    520                     date -= daysInMonth(leapYear, month);
    521                     month++;
    522                 }
    523                 fields[DAY_OF_WEEK] = mod7(days - 3) + 1;
    524             }
    525         }
    526 
    527         fields[MILLISECOND] = (millis % 1000);
    528         millis /= 1000;
    529         fields[SECOND] = (millis % 60);
    530         millis /= 60;
    531         fields[MINUTE] = (millis % 60);
    532         millis /= 60;
    533         fields[HOUR_OF_DAY] = (millis % 24);
    534         fields[AM_PM] = fields[HOUR_OF_DAY] > 11 ? 1 : 0;
    535         fields[HOUR] = fields[HOUR_OF_DAY] % 12;
    536 
    537         if (fields[YEAR] <= 0) {
    538             fields[ERA] = BC;
    539             fields[YEAR] = -fields[YEAR] + 1;
    540         } else {
    541             fields[ERA] = AD;
    542         }
    543         fields[MONTH] = month;
    544         fields[DATE] = date;
    545         fields[DAY_OF_WEEK_IN_MONTH] = (date - 1) / 7 + 1;
    546         fields[WEEK_OF_MONTH] = (date - 1 + mod7(days - date - 2
    547                 - (getFirstDayOfWeek() - 1))) / 7 + 1;
    548         int daysFromStart = mod7(days - 3 - (fields[DAY_OF_YEAR] - 1)
    549                 - (getFirstDayOfWeek() - 1));
    550         int week = (fields[DAY_OF_YEAR] - 1 + daysFromStart) / 7
    551                 + (7 - daysFromStart >= getMinimalDaysInFirstWeek() ? 1 : 0);
    552         if (week == 0) {
    553             fields[WEEK_OF_YEAR] = 7 - mod7(daysFromStart
    554                     - (isLeapYear(fields[YEAR] - 1) ? 2 : 1)) >= getMinimalDaysInFirstWeek() ? 53
    555                     : 52;
    556         } else if (fields[DAY_OF_YEAR] >= (leapYear ? 367 : 366)
    557                 - mod7(daysFromStart + (leapYear ? 2 : 1))) {
    558             fields[WEEK_OF_YEAR] = 7 - mod7(daysFromStart + (leapYear ? 2 : 1)) >= getMinimalDaysInFirstWeek() ? 1
    559                     : week;
    560         } else {
    561             fields[WEEK_OF_YEAR] = week;
    562         }
    563     }
    564 
    565     @Override
    566     protected void computeFields() {
    567         TimeZone timeZone = getTimeZone();
    568         int dstOffset = timeZone.inDaylightTime(new Date(time)) ? timeZone.getDSTSavings() : 0;
    569         int zoneOffset = timeZone.getRawOffset();
    570         fields[DST_OFFSET] = dstOffset;
    571         fields[ZONE_OFFSET] = zoneOffset;
    572 
    573         fullFieldsCalc(time, zoneOffset);
    574 
    575         for (int i = 0; i < FIELD_COUNT; i++) {
    576             isSet[i] = true;
    577         }
    578     }
    579 
    580     @Override
    581     protected void computeTime() {
    582         if (!isLenient()) {
    583             if (isSet[HOUR_OF_DAY]) {
    584                 if (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23) {
    585                     throw new IllegalArgumentException();
    586                 }
    587             } else if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) {
    588                 throw new IllegalArgumentException();
    589             }
    590             if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59)) {
    591                 throw new IllegalArgumentException();
    592             }
    593             if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59)) {
    594                 throw new IllegalArgumentException();
    595             }
    596             if (isSet[MILLISECOND]
    597                     && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999)) {
    598                 throw new IllegalArgumentException();
    599             }
    600             if (isSet[WEEK_OF_YEAR]
    601                     && (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > 53)) {
    602                 throw new IllegalArgumentException();
    603             }
    604             if (isSet[DAY_OF_WEEK]
    605                     && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7)) {
    606                 throw new IllegalArgumentException();
    607             }
    608             if (isSet[DAY_OF_WEEK_IN_MONTH]
    609                     && (fields[DAY_OF_WEEK_IN_MONTH] < 1 || fields[DAY_OF_WEEK_IN_MONTH] > 6)) {
    610                 throw new IllegalArgumentException();
    611             }
    612             if (isSet[WEEK_OF_MONTH]
    613                     && (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > 6)) {
    614                 throw new IllegalArgumentException();
    615             }
    616             if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM) {
    617                 throw new IllegalArgumentException();
    618             }
    619             if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) {
    620                 throw new IllegalArgumentException();
    621             }
    622             if (isSet[YEAR]) {
    623                 if (isSet[ERA] && fields[ERA] == BC
    624                         && (fields[YEAR] < 1 || fields[YEAR] > 292269054)) {
    625                     throw new IllegalArgumentException();
    626                 } else if (fields[YEAR] < 1 || fields[YEAR] > 292278994) {
    627                     throw new IllegalArgumentException();
    628                 }
    629             }
    630             if (isSet[MONTH] && (fields[MONTH] < 0 || fields[MONTH] > 11)) {
    631                 throw new IllegalArgumentException();
    632             }
    633         }
    634 
    635         long timeVal;
    636         long hour = 0;
    637         if (isSet[HOUR_OF_DAY] && lastTimeFieldSet != HOUR) {
    638             hour = fields[HOUR_OF_DAY];
    639         } else if (isSet[HOUR]) {
    640             hour = (fields[AM_PM] * 12) + fields[HOUR];
    641         }
    642         timeVal = hour * 3600000;
    643 
    644         if (isSet[MINUTE]) {
    645             timeVal += ((long) fields[MINUTE]) * 60000;
    646         }
    647         if (isSet[SECOND]) {
    648             timeVal += ((long) fields[SECOND]) * 1000;
    649         }
    650         if (isSet[MILLISECOND]) {
    651             timeVal += fields[MILLISECOND];
    652         }
    653 
    654         long days;
    655         int year = isSet[YEAR] ? fields[YEAR] : 1970;
    656         if (isSet[ERA]) {
    657             // Always test for valid ERA, even if the Calendar is lenient
    658             if (fields[ERA] != BC && fields[ERA] != AD) {
    659                 throw new IllegalArgumentException();
    660             }
    661             if (fields[ERA] == BC) {
    662                 year = 1 - year;
    663             }
    664         }
    665 
    666         boolean weekMonthSet = isSet[WEEK_OF_MONTH]
    667                 || isSet[DAY_OF_WEEK_IN_MONTH];
    668         boolean useMonth = (isSet[DATE] || isSet[MONTH] || weekMonthSet)
    669                 && lastDateFieldSet != DAY_OF_YEAR;
    670         if (useMonth
    671                 && (lastDateFieldSet == DAY_OF_WEEK || lastDateFieldSet == WEEK_OF_YEAR)) {
    672             if (isSet[WEEK_OF_YEAR] && isSet[DAY_OF_WEEK]) {
    673                 useMonth = lastDateFieldSet != WEEK_OF_YEAR && weekMonthSet
    674                         && isSet[DAY_OF_WEEK];
    675             } else if (isSet[DAY_OF_YEAR]) {
    676                 useMonth = isSet[DATE] && isSet[MONTH];
    677             }
    678         }
    679 
    680         if (useMonth) {
    681             int month = fields[MONTH];
    682             year += month / 12;
    683             month %= 12;
    684             if (month < 0) {
    685                 year--;
    686                 month += 12;
    687             }
    688             boolean leapYear = isLeapYear(year);
    689             days = daysFromBaseYear(year) + daysInYear(leapYear, month);
    690             boolean useDate = isSet[DATE];
    691             if (useDate
    692                     && (lastDateFieldSet == DAY_OF_WEEK
    693                             || lastDateFieldSet == WEEK_OF_MONTH || lastDateFieldSet == DAY_OF_WEEK_IN_MONTH)) {
    694                 useDate = !(isSet[DAY_OF_WEEK] && weekMonthSet);
    695             }
    696             if (useDate) {
    697                 if (!isLenient()
    698                         && (fields[DATE] < 1 || fields[DATE] > daysInMonth(
    699                                 leapYear, month))) {
    700                     throw new IllegalArgumentException();
    701                 }
    702                 days += fields[DATE] - 1;
    703             } else {
    704                 int dayOfWeek;
    705                 if (isSet[DAY_OF_WEEK]) {
    706                     dayOfWeek = fields[DAY_OF_WEEK] - 1;
    707                 } else {
    708                     dayOfWeek = getFirstDayOfWeek() - 1;
    709                 }
    710                 if (isSet[WEEK_OF_MONTH]
    711                         && lastDateFieldSet != DAY_OF_WEEK_IN_MONTH) {
    712                     int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1));
    713                     days += (fields[WEEK_OF_MONTH] - 1) * 7
    714                             + mod7(skew + dayOfWeek - (days - 3)) - skew;
    715                 } else if (isSet[DAY_OF_WEEK_IN_MONTH]) {
    716                     if (fields[DAY_OF_WEEK_IN_MONTH] >= 0) {
    717                         days += mod7(dayOfWeek - (days - 3))
    718                                 + (fields[DAY_OF_WEEK_IN_MONTH] - 1) * 7;
    719                     } else {
    720                         days += daysInMonth(leapYear, month)
    721                                 + mod7(dayOfWeek
    722                                         - (days + daysInMonth(leapYear, month) - 3))
    723                                 + fields[DAY_OF_WEEK_IN_MONTH] * 7;
    724                     }
    725                 } else if (isSet[DAY_OF_WEEK]) {
    726                     int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1));
    727                     days += mod7(mod7(skew + dayOfWeek - (days - 3)) - skew);
    728                 }
    729             }
    730         } else {
    731             boolean useWeekYear = isSet[WEEK_OF_YEAR]
    732                     && lastDateFieldSet != DAY_OF_YEAR;
    733             if (useWeekYear && isSet[DAY_OF_YEAR]) {
    734                 useWeekYear = isSet[DAY_OF_WEEK];
    735             }
    736             days = daysFromBaseYear(year);
    737             if (useWeekYear) {
    738                 int dayOfWeek;
    739                 if (isSet[DAY_OF_WEEK]) {
    740                     dayOfWeek = fields[DAY_OF_WEEK] - 1;
    741                 } else {
    742                     dayOfWeek = getFirstDayOfWeek() - 1;
    743                 }
    744                 int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1));
    745                 days += (fields[WEEK_OF_YEAR] - 1) * 7
    746                         + mod7(skew + dayOfWeek - (days - 3)) - skew;
    747                 if (7 - skew < getMinimalDaysInFirstWeek()) {
    748                     days += 7;
    749                 }
    750             } else if (isSet[DAY_OF_YEAR]) {
    751                 if (!isLenient()
    752                         && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > (365 + (isLeapYear(year) ? 1
    753                                 : 0)))) {
    754                     throw new IllegalArgumentException();
    755                 }
    756                 days += fields[DAY_OF_YEAR] - 1;
    757             } else if (isSet[DAY_OF_WEEK]) {
    758                 days += mod7(fields[DAY_OF_WEEK] - 1 - (days - 3));
    759             }
    760         }
    761         lastDateFieldSet = 0;
    762 
    763         timeVal += days * 86400000;
    764         // Use local time to compare with the gregorian change
    765         if (year == changeYear
    766                 && timeVal >= gregorianCutover + julianError() * 86400000L) {
    767             timeVal -= julianError() * 86400000L;
    768         }
    769 
    770         // It is not possible to simply subtract getOffset(timeVal) from timeVal
    771         // to get UTC.
    772         // The trick is needed for the moment when DST transition occurs,
    773         // say 1:00 is a transition time when DST offset becomes +1 hour,
    774         // then wall time in the interval 1:00 - 2:00 is invalid and is
    775         // treated as UTC time.
    776         long timeValWithoutDST = timeVal - getOffset(timeVal)
    777                 + getTimeZone().getRawOffset();
    778         timeVal -= getOffset(timeValWithoutDST);
    779         // Need to update wall time in fields, since it was invalid due to DST
    780         // transition
    781         this.time = timeVal;
    782         if (timeValWithoutDST != timeVal) {
    783             computeFields();
    784             areFieldsSet = true;
    785         }
    786     }
    787 
    788     private int computeYearAndDay(long dayCount, long localTime) {
    789         int year = 1970;
    790         long days = dayCount;
    791         if (localTime < gregorianCutover) {
    792             days -= julianSkew;
    793         }
    794         int approxYears;
    795 
    796         while ((approxYears = (int) (days / 365)) != 0) {
    797             year = year + approxYears;
    798             days = dayCount - daysFromBaseYear(year);
    799         }
    800         if (days < 0) {
    801             year = year - 1;
    802             days = days + daysInYear(year);
    803         }
    804         fields[YEAR] = year;
    805         return (int) days + 1;
    806     }
    807 
    808     private long daysFromBaseYear(long year) {
    809         if (year >= 1970) {
    810             long days = (year - 1970) * 365 + ((year - 1969) / 4);
    811             if (year > changeYear) {
    812                 days -= ((year - 1901) / 100) - ((year - 1601) / 400);
    813             } else {
    814                 if (year == changeYear) {
    815                     days += currentYearSkew;
    816                 } else if (year == changeYear - 1) {
    817                     days += lastYearSkew;
    818                 } else {
    819                     days += julianSkew;
    820                 }
    821             }
    822             return days;
    823         } else if (year <= changeYear) {
    824             return (year - 1970) * 365 + ((year - 1972) / 4) + julianSkew;
    825         }
    826         return (year - 1970) * 365 + ((year - 1972) / 4)
    827                 - ((year - 2000) / 100) + ((year - 2000) / 400);
    828     }
    829 
    830     private int daysInMonth() {
    831         return daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]);
    832     }
    833 
    834     private int daysInMonth(boolean leapYear, int month) {
    835         if (leapYear && month == FEBRUARY) {
    836             return DaysInMonth[month] + 1;
    837         }
    838 
    839         return DaysInMonth[month];
    840     }
    841 
    842     private int daysInYear(int year) {
    843         int daysInYear = isLeapYear(year) ? 366 : 365;
    844         if (year == changeYear) {
    845             daysInYear -= currentYearSkew;
    846         }
    847         if (year == changeYear - 1) {
    848             daysInYear -= lastYearSkew;
    849         }
    850         return daysInYear;
    851     }
    852 
    853     private int daysInYear(boolean leapYear, int month) {
    854         if (leapYear && month > FEBRUARY) {
    855             return DaysInYear[month] + 1;
    856         }
    857 
    858         return DaysInYear[month];
    859     }
    860 
    861     /**
    862      * Returns true if {@code object} is a GregorianCalendar with the same
    863      * properties.
    864      */
    865     @Override public boolean equals(Object object) {
    866         if (!(object instanceof GregorianCalendar)) {
    867             return false;
    868         }
    869         if (object == this) {
    870             return true;
    871         }
    872         return super.equals(object)
    873                 && gregorianCutover == ((GregorianCalendar) object).gregorianCutover;
    874     }
    875 
    876     @Override public int getActualMaximum(int field) {
    877         int value;
    878         if ((value = maximums[field]) == leastMaximums[field]) {
    879             return value;
    880         }
    881 
    882         complete();
    883         long orgTime = time;
    884         int result = 0;
    885         switch (field) {
    886             case WEEK_OF_YEAR:
    887                 set(DATE, 31);
    888                 set(MONTH, DECEMBER);
    889                 result = get(WEEK_OF_YEAR);
    890                 if (result == 1) {
    891                     set(DATE, 31 - 7);
    892                     result = get(WEEK_OF_YEAR);
    893                 }
    894                 areFieldsSet = false;
    895                 break;
    896             case WEEK_OF_MONTH:
    897                 set(DATE, daysInMonth());
    898                 result = get(WEEK_OF_MONTH);
    899                 areFieldsSet = false;
    900                 break;
    901             case DATE:
    902                 return daysInMonth();
    903             case DAY_OF_YEAR:
    904                 return daysInYear(fields[YEAR]);
    905             case DAY_OF_WEEK_IN_MONTH:
    906                 result = get(DAY_OF_WEEK_IN_MONTH)
    907                         + ((daysInMonth() - get(DATE)) / 7);
    908                 break;
    909             case YEAR:
    910                 GregorianCalendar clone = (GregorianCalendar) clone();
    911                 if (get(ERA) == AD) {
    912                     clone.setTimeInMillis(Long.MAX_VALUE);
    913                 } else {
    914                     clone.setTimeInMillis(Long.MIN_VALUE);
    915                 }
    916                 result = clone.get(YEAR);
    917                 clone.set(YEAR, get(YEAR));
    918                 if (clone.before(this)) {
    919                     result--;
    920                 }
    921                 break;
    922             case DST_OFFSET:
    923                 result = getMaximum(DST_OFFSET);
    924                 break;
    925         }
    926         time = orgTime;
    927         return result;
    928     }
    929 
    930     /**
    931      * Gets the minimum value of the specified field for the current date. For
    932      * the gregorian calendar, this value is the same as
    933      * {@code getMinimum()}.
    934      *
    935      * @param field
    936      *            the field.
    937      * @return the minimum value of the specified field.
    938      */
    939     @Override
    940     public int getActualMinimum(int field) {
    941         return getMinimum(field);
    942     }
    943 
    944     /**
    945      * Gets the greatest minimum value of the specified field. For the gregorian
    946      * calendar, this value is the same as {@code getMinimum()}.
    947      *
    948      * @param field
    949      *            the field.
    950      * @return the greatest minimum value of the specified field.
    951      */
    952     @Override
    953     public int getGreatestMinimum(int field) {
    954         return minimums[field];
    955     }
    956 
    957     /**
    958      * Returns the gregorian change date of this calendar. This is the date on
    959      * which the gregorian calendar came into effect.
    960      *
    961      * @return a {@code Date} which represents the gregorian change date.
    962      */
    963     public final Date getGregorianChange() {
    964         return new Date(gregorianCutover);
    965     }
    966 
    967     /**
    968      * Gets the smallest maximum value of the specified field. For example, 28
    969      * for the day of month field.
    970      *
    971      * @param field
    972      *            the field.
    973      * @return the smallest maximum value of the specified field.
    974      */
    975     @Override
    976     public int getLeastMaximum(int field) {
    977         // return value for WEEK_OF_YEAR should make corresponding changes when
    978         // the gregorian change date have been reset.
    979         if (gregorianCutover != defaultGregorianCutover
    980                 && field == WEEK_OF_YEAR) {
    981             long currentTimeInMillis = time;
    982             setTimeInMillis(gregorianCutover);
    983             int actual = getActualMaximum(field);
    984             setTimeInMillis(currentTimeInMillis);
    985             return actual;
    986         }
    987         return leastMaximums[field];
    988     }
    989 
    990     /**
    991      * Gets the greatest maximum value of the specified field. For example, 31
    992      * for the day of month field.
    993      *
    994      * @param field
    995      *            the field.
    996      * @return the greatest maximum value of the specified field.
    997      */
    998     @Override
    999     public int getMaximum(int field) {
   1000         return maximums[field];
   1001     }
   1002 
   1003     /**
   1004      * Gets the smallest minimum value of the specified field.
   1005      *
   1006      * @param field
   1007      *            the field.
   1008      * @return the smallest minimum value of the specified field.
   1009      */
   1010     @Override
   1011     public int getMinimum(int field) {
   1012         return minimums[field];
   1013     }
   1014 
   1015     private int getOffset(long localTime) {
   1016         TimeZone timeZone = getTimeZone();
   1017 
   1018         long dayCount = localTime / 86400000;
   1019         int millis = (int) (localTime % 86400000);
   1020         if (millis < 0) {
   1021             millis += 86400000;
   1022             dayCount--;
   1023         }
   1024 
   1025         int year = 1970;
   1026         long days = dayCount;
   1027         if (localTime < gregorianCutover) {
   1028             days -= julianSkew;
   1029         }
   1030         int approxYears;
   1031 
   1032         while ((approxYears = (int) (days / 365)) != 0) {
   1033             year = year + approxYears;
   1034             days = dayCount - daysFromBaseYear(year);
   1035         }
   1036         if (days < 0) {
   1037             year = year - 1;
   1038             days = days + 365 + (isLeapYear(year) ? 1 : 0);
   1039             if (year == changeYear && localTime < gregorianCutover) {
   1040                 days -= julianError();
   1041             }
   1042         }
   1043         if (year <= 0) {
   1044             return timeZone.getRawOffset();
   1045         }
   1046         int dayOfYear = (int) days + 1;
   1047 
   1048         int month = dayOfYear / 32;
   1049         boolean leapYear = isLeapYear(year);
   1050         int date = dayOfYear - daysInYear(leapYear, month);
   1051         if (date > daysInMonth(leapYear, month)) {
   1052             date -= daysInMonth(leapYear, month);
   1053             month++;
   1054         }
   1055         int dayOfWeek = mod7(dayCount - 3) + 1;
   1056         return timeZone.getOffset(AD, year, month, date, dayOfWeek, millis);
   1057     }
   1058 
   1059     @Override public int hashCode() {
   1060         return super.hashCode()
   1061                 + ((int) (gregorianCutover >>> 32) ^ (int) gregorianCutover);
   1062     }
   1063 
   1064     /**
   1065      * Returns true if {@code year} is a leap year.
   1066      */
   1067     public boolean isLeapYear(int year) {
   1068         if (year > changeYear) {
   1069             return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
   1070         }
   1071 
   1072         return year % 4 == 0;
   1073     }
   1074 
   1075     private int julianError() {
   1076         return changeYear / 100 - changeYear / 400 - 2;
   1077     }
   1078 
   1079     private int mod(int value, int mod) {
   1080         int rem = value % mod;
   1081         if (value < 0 && rem < 0) {
   1082             return rem + mod;
   1083         }
   1084         return rem;
   1085     }
   1086 
   1087     private int mod7(long num1) {
   1088         int rem = (int) (num1 % 7);
   1089         if (num1 < 0 && rem < 0) {
   1090             return rem + 7;
   1091         }
   1092         return rem;
   1093     }
   1094 
   1095     /**
   1096      * Adds the specified amount the specified field and wraps the value of the
   1097      * field when it goes beyond the maximum or minimum value for the current
   1098      * date. Other fields will be adjusted as required to maintain a consistent
   1099      * date.
   1100      *
   1101      * @param field
   1102      *            the field to roll.
   1103      * @param value
   1104      *            the amount to add.
   1105      *
   1106      * @throws IllegalArgumentException
   1107      *                if an invalid field is specified.
   1108      */
   1109     @Override
   1110     public void roll(int field, int value) {
   1111         if (value == 0) {
   1112             return;
   1113         }
   1114         if (field < 0 || field >= ZONE_OFFSET) {
   1115             throw new IllegalArgumentException();
   1116         }
   1117 
   1118         complete();
   1119         int days, day, mod, maxWeeks, newWeek;
   1120         int max = -1;
   1121         switch (field) {
   1122         case YEAR:
   1123             max = maximums[field];
   1124             break;
   1125         case WEEK_OF_YEAR:
   1126             days = daysInYear(fields[YEAR]);
   1127             day = DAY_OF_YEAR;
   1128             mod = mod7(fields[DAY_OF_WEEK] - fields[day]
   1129                     - (getFirstDayOfWeek() - 1));
   1130             maxWeeks = (days - 1 + mod) / 7 + 1;
   1131             newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1;
   1132             if (newWeek == maxWeeks) {
   1133                 int addDays = (newWeek - fields[field]) * 7;
   1134                 if (fields[day] > addDays && fields[day] + addDays > days) {
   1135                     set(field, 1);
   1136                 } else {
   1137                     set(field, newWeek - 1);
   1138                 }
   1139             } else if (newWeek == 1) {
   1140                 int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1;
   1141                 if (week > 1) {
   1142                     set(field, 1);
   1143                 } else {
   1144                     set(field, newWeek);
   1145                 }
   1146             } else {
   1147                 set(field, newWeek);
   1148             }
   1149             break;
   1150         case WEEK_OF_MONTH:
   1151             days = daysInMonth();
   1152             day = DATE;
   1153             mod = mod7(fields[DAY_OF_WEEK] - fields[day]
   1154                     - (getFirstDayOfWeek() - 1));
   1155             maxWeeks = (days - 1 + mod) / 7 + 1;
   1156             newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1;
   1157             if (newWeek == maxWeeks) {
   1158                 if (fields[day] + (newWeek - fields[field]) * 7 > days) {
   1159                     set(day, days);
   1160                 } else {
   1161                     set(field, newWeek);
   1162                 }
   1163             } else if (newWeek == 1) {
   1164                 int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1;
   1165                 if (week > 1) {
   1166                     set(day, 1);
   1167                 } else {
   1168                     set(field, newWeek);
   1169                 }
   1170             } else {
   1171                 set(field, newWeek);
   1172             }
   1173             break;
   1174         case DATE:
   1175             max = daysInMonth();
   1176             break;
   1177         case DAY_OF_YEAR:
   1178             max = daysInYear(fields[YEAR]);
   1179             break;
   1180         case DAY_OF_WEEK:
   1181             max = maximums[field];
   1182             lastDateFieldSet = WEEK_OF_MONTH;
   1183             break;
   1184         case DAY_OF_WEEK_IN_MONTH:
   1185             max = (fields[DATE] + ((daysInMonth() - fields[DATE]) / 7 * 7) - 1) / 7 + 1;
   1186             break;
   1187 
   1188         case ERA:
   1189         case MONTH:
   1190         case AM_PM:
   1191         case HOUR:
   1192         case HOUR_OF_DAY:
   1193         case MINUTE:
   1194         case SECOND:
   1195         case MILLISECOND:
   1196             set(field, mod(fields[field] + value, maximums[field] + 1));
   1197             if (field == MONTH && fields[DATE] > daysInMonth()) {
   1198                 set(DATE, daysInMonth());
   1199             } else if (field == AM_PM) {
   1200                 lastTimeFieldSet = HOUR;
   1201             }
   1202             break;
   1203         }
   1204         if (max != -1) {
   1205             set(field, mod(fields[field] - 1 + value, max) + 1);
   1206         }
   1207         complete();
   1208     }
   1209 
   1210     /**
   1211      * Increments or decrements the specified field and wraps the value of the
   1212      * field when it goes beyond the maximum or minimum value for the current
   1213      * date. Other fields will be adjusted as required to maintain a consistent
   1214      * date. For example, March 31 will roll to April 30 when rolling the month
   1215      * field.
   1216      *
   1217      * @param field
   1218      *            the field to roll.
   1219      * @param increment
   1220      *            {@code true} to increment the field, {@code false} to
   1221      *            decrement.
   1222      * @throws IllegalArgumentException
   1223      *                if an invalid field is specified.
   1224      */
   1225     @Override
   1226     public void roll(int field, boolean increment) {
   1227         roll(field, increment ? 1 : -1);
   1228     }
   1229 
   1230     /**
   1231      * Sets the gregorian change date of this calendar.
   1232      */
   1233     public void setGregorianChange(Date date) {
   1234         gregorianCutover = date.getTime();
   1235         GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
   1236         cal.setTime(date);
   1237         changeYear = cal.get(YEAR);
   1238         if (cal.get(ERA) == BC) {
   1239             changeYear = 1 - changeYear;
   1240         }
   1241         julianSkew = ((changeYear - 2000) / 400) + julianError()
   1242                 - ((changeYear - 2000) / 100);
   1243         int dayOfYear = cal.get(DAY_OF_YEAR);
   1244         if (dayOfYear < julianSkew) {
   1245             currentYearSkew = dayOfYear-1;
   1246             lastYearSkew = julianSkew - dayOfYear + 1;
   1247         } else {
   1248             lastYearSkew = 0;
   1249             currentYearSkew = julianSkew;
   1250         }
   1251     }
   1252 
   1253     private void writeObject(ObjectOutputStream stream) throws IOException {
   1254         stream.defaultWriteObject();
   1255     }
   1256 
   1257     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
   1258         stream.defaultReadObject();
   1259         setGregorianChange(new Date(gregorianCutover));
   1260     }
   1261 }
   1262