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     /**
    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      */
    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
    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 
    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
    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
    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 
    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 
    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 
    864     private int daysInMonth() {
    865         return daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]);
    866     }
    867 
    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 
    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 
    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      */
    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 
    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
    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
    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      */
    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
   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
   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
   1045     public int getMinimum(int field) {
   1046         return minimums[field];
   1047     }
   1048 
   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 
   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      */
   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 
   1109     private int julianError() {
   1110         return changeYear / 100 - changeYear / 400 - 2;
   1111     }
   1112 
   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 
   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
   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
   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      */
   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 
   1287     private void writeObject(ObjectOutputStream stream) throws IOException {
   1288         stream.defaultWriteObject();
   1289     }
   1290 
   1291     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
   1292         stream.defaultReadObject();
   1293         setGregorianChange(new Date(gregorianCutover));
   1294     }
   1295 }
   1296