Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package java.util;
     27 
     28 import java.io.IOException;
     29 import java.io.ObjectInputStream;
     30 import sun.util.locale.provider.CalendarDataUtility;
     31 import sun.util.calendar.BaseCalendar;
     32 import sun.util.calendar.CalendarDate;
     33 import sun.util.calendar.CalendarSystem;
     34 import sun.util.calendar.CalendarUtils;
     35 import sun.util.calendar.Era;
     36 import sun.util.calendar.Gregorian;
     37 import sun.util.calendar.LocalGregorianCalendar;
     38 
     39 /**
     40  * <code>JapaneseImperialCalendar</code> implements a Japanese
     41  * calendar system in which the imperial era-based year numbering is
     42  * supported from the Meiji era. The following are the eras supported
     43  * by this calendar system.
     44  * <pre><tt>
     45  * ERA value   Era name    Since (in Gregorian)
     46  * ------------------------------------------------------
     47  *     0       N/A         N/A
     48  *     1       Meiji       1868-01-01 midnight local time
     49  *     2       Taisho      1912-07-30 midnight local time
     50  *     3       Showa       1926-12-25 midnight local time
     51  *     4       Heisei      1989-01-08 midnight local time
     52  * ------------------------------------------------------
     53  * </tt></pre>
     54  *
     55  * <p><code>ERA</code> value 0 specifies the years before Meiji and
     56  * the Gregorian year values are used. Unlike {@link
     57  * GregorianCalendar}, the Julian to Gregorian transition is not
     58  * supported because it doesn't make any sense to the Japanese
     59  * calendar systems used before Meiji. To represent the years before
     60  * Gregorian year 1, 0 and negative values are used. The Japanese
     61  * Imperial rescripts and government decrees don't specify how to deal
     62  * with time differences for applying the era transitions. This
     63  * calendar implementation assumes local time for all transitions.
     64  *
     65  * @author Masayoshi Okutsu
     66  * @since 1.6
     67  */
     68 class JapaneseImperialCalendar extends Calendar {
     69     /*
     70      * Implementation Notes
     71      *
     72      * This implementation uses
     73      * sun.util.calendar.LocalGregorianCalendar to perform most of the
     74      * calendar calculations. LocalGregorianCalendar is configurable
     75      * and reads <JRE_HOME>/lib/calendars.properties at the start-up.
     76      */
     77 
     78     /**
     79      * The ERA constant designating the era before Meiji.
     80      */
     81     public static final int BEFORE_MEIJI = 0;
     82 
     83     /**
     84      * The ERA constant designating the Meiji era.
     85      */
     86     public static final int MEIJI = 1;
     87 
     88     /**
     89      * The ERA constant designating the Taisho era.
     90      */
     91     public static final int TAISHO = 2;
     92 
     93     /**
     94      * The ERA constant designating the Showa era.
     95      */
     96     public static final int SHOWA = 3;
     97 
     98     /**
     99      * The ERA constant designating the Heisei era.
    100      */
    101     public static final int HEISEI = 4;
    102 
    103     private static final int EPOCH_OFFSET   = 719163; // Fixed date of January 1, 1970 (Gregorian)
    104     private static final int EPOCH_YEAR     = 1970;
    105 
    106     // Useful millisecond constants.  Although ONE_DAY and ONE_WEEK can fit
    107     // into ints, they must be longs in order to prevent arithmetic overflow
    108     // when performing (bug 4173516).
    109     private static final int  ONE_SECOND = 1000;
    110     private static final int  ONE_MINUTE = 60*ONE_SECOND;
    111     private static final int  ONE_HOUR   = 60*ONE_MINUTE;
    112     private static final long ONE_DAY    = 24*ONE_HOUR;
    113     private static final long ONE_WEEK   = 7*ONE_DAY;
    114 
    115     // Reference to the sun.util.calendar.LocalGregorianCalendar instance (singleton).
    116     private static final LocalGregorianCalendar jcal
    117         = (LocalGregorianCalendar) CalendarSystem.forName("japanese");
    118 
    119     // Gregorian calendar instance. This is required because era
    120     // transition dates are given in Gregorian dates.
    121     private static final Gregorian gcal = CalendarSystem.getGregorianCalendar();
    122 
    123     // The Era instance representing "before Meiji".
    124     private static final Era BEFORE_MEIJI_ERA = new Era("BeforeMeiji", "BM", Long.MIN_VALUE, false);
    125 
    126     // Imperial eras. The sun.util.calendar.LocalGregorianCalendar
    127     // doesn't have an Era representing before Meiji, which is
    128     // inconvenient for a Calendar. So, era[0] is a reference to
    129     // BEFORE_MEIJI_ERA.
    130     private static final Era[] eras;
    131 
    132     // Fixed date of the first date of each era.
    133     private static final long[] sinceFixedDates;
    134 
    135     /*
    136      * <pre>
    137      *                                 Greatest       Least
    138      * Field name             Minimum   Minimum     Maximum     Maximum
    139      * ----------             -------   -------     -------     -------
    140      * ERA                          0         0           1           1
    141      * YEAR                -292275055         1           ?           ?
    142      * MONTH                        0         0          11          11
    143      * WEEK_OF_YEAR                 1         1          52*         53
    144      * WEEK_OF_MONTH                0         0           4*          6
    145      * DAY_OF_MONTH                 1         1          28*         31
    146      * DAY_OF_YEAR                  1         1         365*        366
    147      * DAY_OF_WEEK                  1         1           7           7
    148      * DAY_OF_WEEK_IN_MONTH        -1        -1           4*          6
    149      * AM_PM                        0         0           1           1
    150      * HOUR                         0         0          11          11
    151      * HOUR_OF_DAY                  0         0          23          23
    152      * MINUTE                       0         0          59          59
    153      * SECOND                       0         0          59          59
    154      * MILLISECOND                  0         0         999         999
    155      * ZONE_OFFSET             -13:00    -13:00       14:00       14:00
    156      * DST_OFFSET                0:00      0:00        0:20        2:00
    157      * </pre>
    158      * *: depends on eras
    159      */
    160     static final int MIN_VALUES[] = {
    161         0,              // ERA
    162         -292275055,     // YEAR
    163         JANUARY,        // MONTH
    164         1,              // WEEK_OF_YEAR
    165         0,              // WEEK_OF_MONTH
    166         1,              // DAY_OF_MONTH
    167         1,              // DAY_OF_YEAR
    168         SUNDAY,         // DAY_OF_WEEK
    169         1,              // DAY_OF_WEEK_IN_MONTH
    170         AM,             // AM_PM
    171         0,              // HOUR
    172         0,              // HOUR_OF_DAY
    173         0,              // MINUTE
    174         0,              // SECOND
    175         0,              // MILLISECOND
    176         -13*ONE_HOUR,   // ZONE_OFFSET (UNIX compatibility)
    177         0               // DST_OFFSET
    178     };
    179     static final int LEAST_MAX_VALUES[] = {
    180         0,              // ERA (initialized later)
    181         0,              // YEAR (initialized later)
    182         JANUARY,        // MONTH (Showa 64 ended in January.)
    183         0,              // WEEK_OF_YEAR (Showa 1 has only 6 days which could be 0 weeks.)
    184         4,              // WEEK_OF_MONTH
    185         28,             // DAY_OF_MONTH
    186         0,              // DAY_OF_YEAR (initialized later)
    187         SATURDAY,       // DAY_OF_WEEK
    188         4,              // DAY_OF_WEEK_IN
    189         PM,             // AM_PM
    190         11,             // HOUR
    191         23,             // HOUR_OF_DAY
    192         59,             // MINUTE
    193         59,             // SECOND
    194         999,            // MILLISECOND
    195         14*ONE_HOUR,    // ZONE_OFFSET
    196         20*ONE_MINUTE   // DST_OFFSET (historical least maximum)
    197     };
    198     static final int MAX_VALUES[] = {
    199         0,              // ERA
    200         292278994,      // YEAR
    201         DECEMBER,       // MONTH
    202         53,             // WEEK_OF_YEAR
    203         6,              // WEEK_OF_MONTH
    204         31,             // DAY_OF_MONTH
    205         366,            // DAY_OF_YEAR
    206         SATURDAY,       // DAY_OF_WEEK
    207         6,              // DAY_OF_WEEK_IN
    208         PM,             // AM_PM
    209         11,             // HOUR
    210         23,             // HOUR_OF_DAY
    211         59,             // MINUTE
    212         59,             // SECOND
    213         999,            // MILLISECOND
    214         14*ONE_HOUR,    // ZONE_OFFSET
    215         2*ONE_HOUR      // DST_OFFSET (double summer time)
    216     };
    217 
    218     // Proclaim serialization compatibility with JDK 1.6
    219     private static final long serialVersionUID = -3364572813905467929L;
    220 
    221     static {
    222         Era[] es = jcal.getEras();
    223         int length = es.length + 1;
    224         eras = new Era[length];
    225         sinceFixedDates = new long[length];
    226 
    227         // eras[BEFORE_MEIJI] and sinceFixedDate[BEFORE_MEIJI] are the
    228         // same as Gregorian.
    229         int index = BEFORE_MEIJI;
    230         sinceFixedDates[index] = gcal.getFixedDate(BEFORE_MEIJI_ERA.getSinceDate());
    231         eras[index++] = BEFORE_MEIJI_ERA;
    232         for (Era e : es) {
    233             CalendarDate d = e.getSinceDate();
    234             sinceFixedDates[index] = gcal.getFixedDate(d);
    235             eras[index++] = e;
    236         }
    237 
    238         LEAST_MAX_VALUES[ERA] = MAX_VALUES[ERA] = eras.length - 1;
    239 
    240         // Calculate the least maximum year and least day of Year
    241         // values. The following code assumes that there's at most one
    242         // era transition in a Gregorian year.
    243         int year = Integer.MAX_VALUE;
    244         int dayOfYear = Integer.MAX_VALUE;
    245         CalendarDate date = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
    246         for (int i = 1; i < eras.length; i++) {
    247             long fd = sinceFixedDates[i];
    248             CalendarDate transitionDate = eras[i].getSinceDate();
    249             date.setDate(transitionDate.getYear(), BaseCalendar.JANUARY, 1);
    250             long fdd = gcal.getFixedDate(date);
    251             if (fd != fdd) {
    252                 dayOfYear = Math.min((int)(fd - fdd) + 1, dayOfYear);
    253             }
    254             date.setDate(transitionDate.getYear(), BaseCalendar.DECEMBER, 31);
    255             fdd = gcal.getFixedDate(date);
    256             if (fd != fdd) {
    257                 dayOfYear = Math.min((int)(fdd - fd) + 1, dayOfYear);
    258             }
    259             LocalGregorianCalendar.Date lgd = getCalendarDate(fd - 1);
    260             int y = lgd.getYear();
    261             // Unless the first year starts from January 1, the actual
    262             // max value could be one year short. For example, if it's
    263             // Showa 63 January 8, 63 is the actual max value since
    264             // Showa 64 January 8 doesn't exist.
    265             if (!(lgd.getMonth() == BaseCalendar.JANUARY && lgd.getDayOfMonth() == 1)) {
    266                 y--;
    267             }
    268             year = Math.min(y, year);
    269         }
    270         LEAST_MAX_VALUES[YEAR] = year; // Max year could be smaller than this value.
    271         LEAST_MAX_VALUES[DAY_OF_YEAR] = dayOfYear;
    272     }
    273 
    274     /**
    275      * jdate always has a sun.util.calendar.LocalGregorianCalendar.Date instance to
    276      * avoid overhead of creating it for each calculation.
    277      */
    278     private transient LocalGregorianCalendar.Date jdate;
    279 
    280     /**
    281      * Temporary int[2] to get time zone offsets. zoneOffsets[0] gets
    282      * the GMT offset value and zoneOffsets[1] gets the daylight saving
    283      * value.
    284      */
    285     private transient int[] zoneOffsets;
    286 
    287     /**
    288      * Temporary storage for saving original fields[] values in
    289      * non-lenient mode.
    290      */
    291     private transient int[] originalFields;
    292 
    293     /**
    294      * Constructs a <code>JapaneseImperialCalendar</code> based on the current time
    295      * in the given time zone with the given locale.
    296      *
    297      * @param zone the given time zone.
    298      * @param aLocale the given locale.
    299      */
    300     JapaneseImperialCalendar(TimeZone zone, Locale aLocale) {
    301         super(zone, aLocale);
    302         jdate = jcal.newCalendarDate(zone);
    303         setTimeInMillis(System.currentTimeMillis());
    304     }
    305 
    306     /**
    307      * Constructs an "empty" {@code JapaneseImperialCalendar}.
    308      *
    309      * @param zone    the given time zone
    310      * @param aLocale the given locale
    311      * @param flag    the flag requesting an empty instance
    312      */
    313     JapaneseImperialCalendar(TimeZone zone, Locale aLocale, boolean flag) {
    314         super(zone, aLocale);
    315         jdate = jcal.newCalendarDate(zone);
    316     }
    317 
    318     /**
    319      * Returns {@code "japanese"} as the calendar type of this {@code
    320      * JapaneseImperialCalendar}.
    321      *
    322      * @return {@code "japanese"}
    323      */
    324     @Override
    325     public String getCalendarType() {
    326         return "japanese";
    327     }
    328 
    329     /**
    330      * Compares this <code>JapaneseImperialCalendar</code> to the specified
    331      * <code>Object</code>. The result is <code>true</code> if and
    332      * only if the argument is a <code>JapaneseImperialCalendar</code> object
    333      * that represents the same time value (millisecond offset from
    334      * the <a href="Calendar.html#Epoch">Epoch</a>) under the same
    335      * <code>Calendar</code> parameters.
    336      *
    337      * @param obj the object to compare with.
    338      * @return <code>true</code> if this object is equal to <code>obj</code>;
    339      * <code>false</code> otherwise.
    340      * @see Calendar#compareTo(Calendar)
    341      */
    342     public boolean equals(Object obj) {
    343         return obj instanceof JapaneseImperialCalendar &&
    344             super.equals(obj);
    345     }
    346 
    347     /**
    348      * Generates the hash code for this
    349      * <code>JapaneseImperialCalendar</code> object.
    350      */
    351     public int hashCode() {
    352         return super.hashCode() ^ jdate.hashCode();
    353     }
    354 
    355     /**
    356      * Adds the specified (signed) amount of time to the given calendar field,
    357      * based on the calendar's rules.
    358      *
    359      * <p><em>Add rule 1</em>. The value of <code>field</code>
    360      * after the call minus the value of <code>field</code> before the
    361      * call is <code>amount</code>, modulo any overflow that has occurred in
    362      * <code>field</code>. Overflow occurs when a field value exceeds its
    363      * range and, as a result, the next larger field is incremented or
    364      * decremented and the field value is adjusted back into its range.</p>
    365      *
    366      * <p><em>Add rule 2</em>. If a smaller field is expected to be
    367      * invariant, but it is impossible for it to be equal to its
    368      * prior value because of changes in its minimum or maximum after
    369      * <code>field</code> is changed, then its value is adjusted to be as close
    370      * as possible to its expected value. A smaller field represents a
    371      * smaller unit of time. <code>HOUR</code> is a smaller field than
    372      * <code>DAY_OF_MONTH</code>. No adjustment is made to smaller fields
    373      * that are not expected to be invariant. The calendar system
    374      * determines what fields are expected to be invariant.</p>
    375      *
    376      * @param field the calendar field.
    377      * @param amount the amount of date or time to be added to the field.
    378      * @exception IllegalArgumentException if <code>field</code> is
    379      * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
    380      * or if any calendar fields have out-of-range values in
    381      * non-lenient mode.
    382      */
    383     public void add(int field, int amount) {
    384         // If amount == 0, do nothing even the given field is out of
    385         // range. This is tested by JCK.
    386         if (amount == 0) {
    387             return;   // Do nothing!
    388         }
    389 
    390         if (field < 0 || field >= ZONE_OFFSET) {
    391             throw new IllegalArgumentException();
    392         }
    393 
    394         // Sync the time and calendar fields.
    395         complete();
    396 
    397         if (field == YEAR) {
    398             LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
    399             d.addYear(amount);
    400             pinDayOfMonth(d);
    401             set(ERA, getEraIndex(d));
    402             set(YEAR, d.getYear());
    403             set(MONTH, d.getMonth() - 1);
    404             set(DAY_OF_MONTH, d.getDayOfMonth());
    405         } else if (field == MONTH) {
    406             LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
    407             d.addMonth(amount);
    408             pinDayOfMonth(d);
    409             set(ERA, getEraIndex(d));
    410             set(YEAR, d.getYear());
    411             set(MONTH, d.getMonth() - 1);
    412             set(DAY_OF_MONTH, d.getDayOfMonth());
    413         } else if (field == ERA) {
    414             int era = internalGet(ERA) + amount;
    415             if (era < 0) {
    416                 era = 0;
    417             } else if (era > eras.length - 1) {
    418                 era = eras.length - 1;
    419             }
    420             set(ERA, era);
    421         } else {
    422             long delta = amount;
    423             long timeOfDay = 0;
    424             switch (field) {
    425             // Handle the time fields here. Convert the given
    426             // amount to milliseconds and call setTimeInMillis.
    427             case HOUR:
    428             case HOUR_OF_DAY:
    429                 delta *= 60 * 60 * 1000;        // hours to milliseconds
    430                 break;
    431 
    432             case MINUTE:
    433                 delta *= 60 * 1000;             // minutes to milliseconds
    434                 break;
    435 
    436             case SECOND:
    437                 delta *= 1000;                  // seconds to milliseconds
    438                 break;
    439 
    440             case MILLISECOND:
    441                 break;
    442 
    443             // Handle week, day and AM_PM fields which involves
    444             // time zone offset change adjustment. Convert the
    445             // given amount to the number of days.
    446             case WEEK_OF_YEAR:
    447             case WEEK_OF_MONTH:
    448             case DAY_OF_WEEK_IN_MONTH:
    449                 delta *= 7;
    450                 break;
    451 
    452             case DAY_OF_MONTH: // synonym of DATE
    453             case DAY_OF_YEAR:
    454             case DAY_OF_WEEK:
    455                 break;
    456 
    457             case AM_PM:
    458                 // Convert the amount to the number of days (delta)
    459                 // and +12 or -12 hours (timeOfDay).
    460                 delta = amount / 2;
    461                 timeOfDay = 12 * (amount % 2);
    462                 break;
    463             }
    464 
    465             // The time fields don't require time zone offset change
    466             // adjustment.
    467             if (field >= HOUR) {
    468                 setTimeInMillis(time + delta);
    469                 return;
    470             }
    471 
    472             // The rest of the fields (week, day or AM_PM fields)
    473             // require time zone offset (both GMT and DST) change
    474             // adjustment.
    475 
    476             // Translate the current time to the fixed date and time
    477             // of the day.
    478             long fd = cachedFixedDate;
    479             timeOfDay += internalGet(HOUR_OF_DAY);
    480             timeOfDay *= 60;
    481             timeOfDay += internalGet(MINUTE);
    482             timeOfDay *= 60;
    483             timeOfDay += internalGet(SECOND);
    484             timeOfDay *= 1000;
    485             timeOfDay += internalGet(MILLISECOND);
    486             if (timeOfDay >= ONE_DAY) {
    487                 fd++;
    488                 timeOfDay -= ONE_DAY;
    489             } else if (timeOfDay < 0) {
    490                 fd--;
    491                 timeOfDay += ONE_DAY;
    492             }
    493 
    494             fd += delta; // fd is the expected fixed date after the calculation
    495             int zoneOffset = internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET);
    496             setTimeInMillis((fd - EPOCH_OFFSET) * ONE_DAY + timeOfDay - zoneOffset);
    497             zoneOffset -= internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET);
    498             // If the time zone offset has changed, then adjust the difference.
    499             if (zoneOffset != 0) {
    500                 setTimeInMillis(time + zoneOffset);
    501                 long fd2 = cachedFixedDate;
    502                 // If the adjustment has changed the date, then take
    503                 // the previous one.
    504                 if (fd2 != fd) {
    505                     setTimeInMillis(time - zoneOffset);
    506                 }
    507             }
    508         }
    509     }
    510 
    511     public void roll(int field, boolean up) {
    512         roll(field, up ? +1 : -1);
    513     }
    514 
    515     /**
    516      * Adds a signed amount to the specified calendar field without changing larger fields.
    517      * A negative roll amount means to subtract from field without changing
    518      * larger fields. If the specified amount is 0, this method performs nothing.
    519      *
    520      * <p>This method calls {@link #complete()} before adding the
    521      * amount so that all the calendar fields are normalized. If there
    522      * is any calendar field having an out-of-range value in non-lenient mode, then an
    523      * <code>IllegalArgumentException</code> is thrown.
    524      *
    525      * @param field the calendar field.
    526      * @param amount the signed amount to add to <code>field</code>.
    527      * @exception IllegalArgumentException if <code>field</code> is
    528      * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
    529      * or if any calendar fields have out-of-range values in
    530      * non-lenient mode.
    531      * @see #roll(int,boolean)
    532      * @see #add(int,int)
    533      * @see #set(int,int)
    534      */
    535     public void roll(int field, int amount) {
    536         // If amount == 0, do nothing even the given field is out of
    537         // range. This is tested by JCK.
    538         if (amount == 0) {
    539             return;
    540         }
    541 
    542         if (field < 0 || field >= ZONE_OFFSET) {
    543             throw new IllegalArgumentException();
    544         }
    545 
    546         // Sync the time and calendar fields.
    547         complete();
    548 
    549         int min = getMinimum(field);
    550         int max = getMaximum(field);
    551 
    552         switch (field) {
    553         case ERA:
    554         case AM_PM:
    555         case MINUTE:
    556         case SECOND:
    557         case MILLISECOND:
    558             // These fields are handled simply, since they have fixed
    559             // minima and maxima. Other fields are complicated, since
    560             // the range within they must roll varies depending on the
    561             // date, a time zone and the era transitions.
    562             break;
    563 
    564         case HOUR:
    565         case HOUR_OF_DAY:
    566             {
    567                 int unit = max + 1; // 12 or 24 hours
    568                 int h = internalGet(field);
    569                 int nh = (h + amount) % unit;
    570                 if (nh < 0) {
    571                     nh += unit;
    572                 }
    573                 time += ONE_HOUR * (nh - h);
    574 
    575                 // The day might have changed, which could happen if
    576                 // the daylight saving time transition brings it to
    577                 // the next day, although it's very unlikely. But we
    578                 // have to make sure not to change the larger fields.
    579                 CalendarDate d = jcal.getCalendarDate(time, getZone());
    580                 if (internalGet(DAY_OF_MONTH) != d.getDayOfMonth()) {
    581                     d.setEra(jdate.getEra());
    582                     d.setDate(internalGet(YEAR),
    583                               internalGet(MONTH) + 1,
    584                               internalGet(DAY_OF_MONTH));
    585                     if (field == HOUR) {
    586                         assert (internalGet(AM_PM) == PM);
    587                         d.addHours(+12); // restore PM
    588                     }
    589                     time = jcal.getTime(d);
    590                 }
    591                 int hourOfDay = d.getHours();
    592                 internalSet(field, hourOfDay % unit);
    593                 if (field == HOUR) {
    594                     internalSet(HOUR_OF_DAY, hourOfDay);
    595                 } else {
    596                     internalSet(AM_PM, hourOfDay / 12);
    597                     internalSet(HOUR, hourOfDay % 12);
    598                 }
    599 
    600                 // Time zone offset and/or daylight saving might have changed.
    601                 int zoneOffset = d.getZoneOffset();
    602                 int saving = d.getDaylightSaving();
    603                 internalSet(ZONE_OFFSET, zoneOffset - saving);
    604                 internalSet(DST_OFFSET, saving);
    605                 return;
    606             }
    607 
    608         case YEAR:
    609             min = getActualMinimum(field);
    610             max = getActualMaximum(field);
    611             break;
    612 
    613         case MONTH:
    614             // Rolling the month involves both pinning the final value to [0, 11]
    615             // and adjusting the DAY_OF_MONTH if necessary.  We only adjust the
    616             // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
    617             // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
    618             {
    619                 if (!isTransitionYear(jdate.getNormalizedYear())) {
    620                     int year = jdate.getYear();
    621                     if (year == getMaximum(YEAR)) {
    622                         CalendarDate jd = jcal.getCalendarDate(time, getZone());
    623                         CalendarDate d = jcal.getCalendarDate(Long.MAX_VALUE, getZone());
    624                         max = d.getMonth() - 1;
    625                         int n = getRolledValue(internalGet(field), amount, min, max);
    626                         if (n == max) {
    627                             // To avoid overflow, use an equivalent year.
    628                             jd.addYear(-400);
    629                             jd.setMonth(n + 1);
    630                             if (jd.getDayOfMonth() > d.getDayOfMonth()) {
    631                                 jd.setDayOfMonth(d.getDayOfMonth());
    632                                 jcal.normalize(jd);
    633                             }
    634                             if (jd.getDayOfMonth() == d.getDayOfMonth()
    635                                 && jd.getTimeOfDay() > d.getTimeOfDay()) {
    636                                 jd.setMonth(n + 1);
    637                                 jd.setDayOfMonth(d.getDayOfMonth() - 1);
    638                                 jcal.normalize(jd);
    639                                 // Month may have changed by the normalization.
    640                                 n = jd.getMonth() - 1;
    641                             }
    642                             set(DAY_OF_MONTH, jd.getDayOfMonth());
    643                         }
    644                         set(MONTH, n);
    645                     } else if (year == getMinimum(YEAR)) {
    646                         CalendarDate jd = jcal.getCalendarDate(time, getZone());
    647                         CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
    648                         min = d.getMonth() - 1;
    649                         int n = getRolledValue(internalGet(field), amount, min, max);
    650                         if (n == min) {
    651                             // To avoid underflow, use an equivalent year.
    652                             jd.addYear(+400);
    653                             jd.setMonth(n + 1);
    654                             if (jd.getDayOfMonth() < d.getDayOfMonth()) {
    655                                 jd.setDayOfMonth(d.getDayOfMonth());
    656                                 jcal.normalize(jd);
    657                             }
    658                             if (jd.getDayOfMonth() == d.getDayOfMonth()
    659                                 && jd.getTimeOfDay() < d.getTimeOfDay()) {
    660                                 jd.setMonth(n + 1);
    661                                 jd.setDayOfMonth(d.getDayOfMonth() + 1);
    662                                 jcal.normalize(jd);
    663                                 // Month may have changed by the normalization.
    664                                 n = jd.getMonth() - 1;
    665                             }
    666                             set(DAY_OF_MONTH, jd.getDayOfMonth());
    667                         }
    668                         set(MONTH, n);
    669                     } else {
    670                         int mon = (internalGet(MONTH) + amount) % 12;
    671                         if (mon < 0) {
    672                             mon += 12;
    673                         }
    674                         set(MONTH, mon);
    675 
    676                         // Keep the day of month in the range.  We
    677                         // don't want to spill over into the next
    678                         // month; e.g., we don't want jan31 + 1 mo ->
    679                         // feb31 -> mar3.
    680                         int monthLen = monthLength(mon);
    681                         if (internalGet(DAY_OF_MONTH) > monthLen) {
    682                             set(DAY_OF_MONTH, monthLen);
    683                         }
    684                     }
    685                 } else {
    686                     int eraIndex = getEraIndex(jdate);
    687                     CalendarDate transition = null;
    688                     if (jdate.getYear() == 1) {
    689                         transition = eras[eraIndex].getSinceDate();
    690                         min = transition.getMonth() - 1;
    691                     } else {
    692                         if (eraIndex < eras.length - 1) {
    693                             transition = eras[eraIndex + 1].getSinceDate();
    694                             if (transition.getYear() == jdate.getNormalizedYear()) {
    695                                 max = transition.getMonth() - 1;
    696                                 if (transition.getDayOfMonth() == 1) {
    697                                     max--;
    698                                 }
    699                             }
    700                         }
    701                     }
    702 
    703                     if (min == max) {
    704                         // The year has only one month. No need to
    705                         // process further. (Showa Gan-nen (year 1)
    706                         // and the last year have only one month.)
    707                         return;
    708                     }
    709                     int n = getRolledValue(internalGet(field), amount, min, max);
    710                     set(MONTH, n);
    711                     if (n == min) {
    712                         if (!(transition.getMonth() == BaseCalendar.JANUARY
    713                               && transition.getDayOfMonth() == 1)) {
    714                             if (jdate.getDayOfMonth() < transition.getDayOfMonth()) {
    715                                 set(DAY_OF_MONTH, transition.getDayOfMonth());
    716                             }
    717                         }
    718                     } else if (n == max && (transition.getMonth() - 1 == n)) {
    719                         int dom = transition.getDayOfMonth();
    720                         if (jdate.getDayOfMonth() >= dom) {
    721                             set(DAY_OF_MONTH, dom - 1);
    722                         }
    723                     }
    724                 }
    725                 return;
    726             }
    727 
    728         case WEEK_OF_YEAR:
    729             {
    730                 int y = jdate.getNormalizedYear();
    731                 max = getActualMaximum(WEEK_OF_YEAR);
    732                 set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK)); // update stamp[field]
    733                 int woy = internalGet(WEEK_OF_YEAR);
    734                 int value = woy + amount;
    735                 if (!isTransitionYear(jdate.getNormalizedYear())) {
    736                     int year = jdate.getYear();
    737                     if (year == getMaximum(YEAR)) {
    738                         max = getActualMaximum(WEEK_OF_YEAR);
    739                     } else if (year == getMinimum(YEAR)) {
    740                         min = getActualMinimum(WEEK_OF_YEAR);
    741                         max = getActualMaximum(WEEK_OF_YEAR);
    742                         if (value > min && value < max) {
    743                             set(WEEK_OF_YEAR, value);
    744                             return;
    745                         }
    746 
    747                     }
    748                     // If the new value is in between min and max
    749                     // (exclusive), then we can use the value.
    750                     if (value > min && value < max) {
    751                         set(WEEK_OF_YEAR, value);
    752                         return;
    753                     }
    754                     long fd = cachedFixedDate;
    755                     // Make sure that the min week has the current DAY_OF_WEEK
    756                     long day1 = fd - (7 * (woy - min));
    757                     if (year != getMinimum(YEAR)) {
    758                         if (gcal.getYearFromFixedDate(day1) != y) {
    759                             min++;
    760                         }
    761                     } else {
    762                         CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
    763                         if (day1 < jcal.getFixedDate(d)) {
    764                             min++;
    765                         }
    766                     }
    767 
    768                     // Make sure the same thing for the max week
    769                     fd += 7 * (max - internalGet(WEEK_OF_YEAR));
    770                     if (gcal.getYearFromFixedDate(fd) != y) {
    771                         max--;
    772                     }
    773                     break;
    774                 }
    775 
    776                 // Handle transition here.
    777                 long fd = cachedFixedDate;
    778                 long day1 = fd - (7 * (woy - min));
    779                 // Make sure that the min week has the current DAY_OF_WEEK
    780                 LocalGregorianCalendar.Date d = getCalendarDate(day1);
    781                 if (!(d.getEra() == jdate.getEra() && d.getYear() == jdate.getYear())) {
    782                     min++;
    783                 }
    784 
    785                 // Make sure the same thing for the max week
    786                 fd += 7 * (max - woy);
    787                 jcal.getCalendarDateFromFixedDate(d, fd);
    788                 if (!(d.getEra() == jdate.getEra() && d.getYear() == jdate.getYear())) {
    789                     max--;
    790                 }
    791                 // value: the new WEEK_OF_YEAR which must be converted
    792                 // to month and day of month.
    793                 value = getRolledValue(woy, amount, min, max) - 1;
    794                 d = getCalendarDate(day1 + value * 7);
    795                 set(MONTH, d.getMonth() - 1);
    796                 set(DAY_OF_MONTH, d.getDayOfMonth());
    797                 return;
    798             }
    799 
    800         case WEEK_OF_MONTH:
    801             {
    802                 boolean isTransitionYear = isTransitionYear(jdate.getNormalizedYear());
    803                 // dow: relative day of week from the first day of week
    804                 int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
    805                 if (dow < 0) {
    806                     dow += 7;
    807                 }
    808 
    809                 long fd = cachedFixedDate;
    810                 long month1;     // fixed date of the first day (usually 1) of the month
    811                 int monthLength; // actual month length
    812                 if (isTransitionYear) {
    813                     month1 = getFixedDateMonth1(jdate, fd);
    814                     monthLength = actualMonthLength();
    815                 } else {
    816                     month1 = fd - internalGet(DAY_OF_MONTH) + 1;
    817                     monthLength = jcal.getMonthLength(jdate);
    818                 }
    819 
    820                 // the first day of week of the month.
    821                 long monthDay1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(month1 + 6,
    822                                                                                      getFirstDayOfWeek());
    823                 // if the week has enough days to form a week, the
    824                 // week starts from the previous month.
    825                 if ((int)(monthDay1st - month1) >= getMinimalDaysInFirstWeek()) {
    826                     monthDay1st -= 7;
    827                 }
    828                 max = getActualMaximum(field);
    829 
    830                 // value: the new WEEK_OF_MONTH value
    831                 int value = getRolledValue(internalGet(field), amount, 1, max) - 1;
    832 
    833                 // nfd: fixed date of the rolled date
    834                 long nfd = monthDay1st + value * 7 + dow;
    835 
    836                 // Unlike WEEK_OF_YEAR, we need to change day of week if the
    837                 // nfd is out of the month.
    838                 if (nfd < month1) {
    839                     nfd = month1;
    840                 } else if (nfd >= (month1 + monthLength)) {
    841                     nfd = month1 + monthLength - 1;
    842                 }
    843                 set(DAY_OF_MONTH, (int)(nfd - month1) + 1);
    844                 return;
    845             }
    846 
    847         case DAY_OF_MONTH:
    848             {
    849                 if (!isTransitionYear(jdate.getNormalizedYear())) {
    850                     max = jcal.getMonthLength(jdate);
    851                     break;
    852                 }
    853 
    854                 // TODO: Need to change the spec to be usable DAY_OF_MONTH rolling...
    855 
    856                 // Transition handling. We can't change year and era
    857                 // values here due to the Calendar roll spec!
    858                 long month1 = getFixedDateMonth1(jdate, cachedFixedDate);
    859 
    860                 // It may not be a regular month. Convert the date and range to
    861                 // the relative values, perform the roll, and
    862                 // convert the result back to the rolled date.
    863                 int value = getRolledValue((int)(cachedFixedDate - month1), amount,
    864                                            0, actualMonthLength() - 1);
    865                 LocalGregorianCalendar.Date d = getCalendarDate(month1 + value);
    866                 assert getEraIndex(d) == internalGetEra()
    867                     && d.getYear() == internalGet(YEAR) && d.getMonth()-1 == internalGet(MONTH);
    868                 set(DAY_OF_MONTH, d.getDayOfMonth());
    869                 return;
    870             }
    871 
    872         case DAY_OF_YEAR:
    873             {
    874                 max = getActualMaximum(field);
    875                 if (!isTransitionYear(jdate.getNormalizedYear())) {
    876                     break;
    877                 }
    878 
    879                 // Handle transition. We can't change year and era values
    880                 // here due to the Calendar roll spec.
    881                 int value = getRolledValue(internalGet(DAY_OF_YEAR), amount, min, max);
    882                 long jan0 = cachedFixedDate - internalGet(DAY_OF_YEAR);
    883                 LocalGregorianCalendar.Date d = getCalendarDate(jan0 + value);
    884                 assert getEraIndex(d) == internalGetEra() && d.getYear() == internalGet(YEAR);
    885                 set(MONTH, d.getMonth() - 1);
    886                 set(DAY_OF_MONTH, d.getDayOfMonth());
    887                 return;
    888             }
    889 
    890         case DAY_OF_WEEK:
    891             {
    892                 int normalizedYear = jdate.getNormalizedYear();
    893                 if (!isTransitionYear(normalizedYear) && !isTransitionYear(normalizedYear - 1)) {
    894                     // If the week of year is in the same year, we can
    895                     // just change DAY_OF_WEEK.
    896                     int weekOfYear = internalGet(WEEK_OF_YEAR);
    897                     if (weekOfYear > 1 && weekOfYear < 52) {
    898                         set(WEEK_OF_YEAR, internalGet(WEEK_OF_YEAR));
    899                         max = SATURDAY;
    900                         break;
    901                     }
    902                 }
    903 
    904                 // We need to handle it in a different way around year
    905                 // boundaries and in the transition year. Note that
    906                 // changing era and year values violates the roll
    907                 // rule: not changing larger calendar fields...
    908                 amount %= 7;
    909                 if (amount == 0) {
    910                     return;
    911                 }
    912                 long fd = cachedFixedDate;
    913                 long dowFirst = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fd, getFirstDayOfWeek());
    914                 fd += amount;
    915                 if (fd < dowFirst) {
    916                     fd += 7;
    917                 } else if (fd >= dowFirst + 7) {
    918                     fd -= 7;
    919                 }
    920                 LocalGregorianCalendar.Date d = getCalendarDate(fd);
    921                 set(ERA, getEraIndex(d));
    922                 set(d.getYear(), d.getMonth() - 1, d.getDayOfMonth());
    923                 return;
    924             }
    925 
    926         case DAY_OF_WEEK_IN_MONTH:
    927             {
    928                 min = 1; // after having normalized, min should be 1.
    929                 if (!isTransitionYear(jdate.getNormalizedYear())) {
    930                     int dom = internalGet(DAY_OF_MONTH);
    931                     int monthLength = jcal.getMonthLength(jdate);
    932                     int lastDays = monthLength % 7;
    933                     max = monthLength / 7;
    934                     int x = (dom - 1) % 7;
    935                     if (x < lastDays) {
    936                         max++;
    937                     }
    938                     set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK));
    939                     break;
    940                 }
    941 
    942                 // Transition year handling.
    943                 long fd = cachedFixedDate;
    944                 long month1 = getFixedDateMonth1(jdate, fd);
    945                 int monthLength = actualMonthLength();
    946                 int lastDays = monthLength % 7;
    947                 max = monthLength / 7;
    948                 int x = (int)(fd - month1) % 7;
    949                 if (x < lastDays) {
    950                     max++;
    951                 }
    952                 int value = getRolledValue(internalGet(field), amount, min, max) - 1;
    953                 fd = month1 + value * 7 + x;
    954                 LocalGregorianCalendar.Date d = getCalendarDate(fd);
    955                 set(DAY_OF_MONTH, d.getDayOfMonth());
    956                 return;
    957             }
    958         }
    959 
    960         set(field, getRolledValue(internalGet(field), amount, min, max));
    961     }
    962 
    963     @Override
    964     public String getDisplayName(int field, int style, Locale locale) {
    965         if (!checkDisplayNameParams(field, style, SHORT, NARROW_FORMAT, locale,
    966                                     ERA_MASK|YEAR_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
    967             return null;
    968         }
    969 
    970         int fieldValue = get(field);
    971 
    972         // "GanNen" is supported only in the LONG style.
    973         if (field == YEAR
    974             && (getBaseStyle(style) != LONG || fieldValue != 1 || get(ERA) == 0)) {
    975             return null;
    976         }
    977 
    978         String name = CalendarDataUtility.retrieveFieldValueName(getCalendarType(), field,
    979                                                                  fieldValue, style, locale);
    980         // If the ERA value is null, then
    981         // try to get its name or abbreviation from the Era instance.
    982         if (name == null && field == ERA && fieldValue < eras.length) {
    983             Era era = eras[fieldValue];
    984             name = (style == SHORT) ? era.getAbbreviation() : era.getName();
    985         }
    986         return name;
    987     }
    988 
    989     @Override
    990     public Map<String,Integer> getDisplayNames(int field, int style, Locale locale) {
    991         if (!checkDisplayNameParams(field, style, ALL_STYLES, NARROW_FORMAT, locale,
    992                                     ERA_MASK|YEAR_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
    993             return null;
    994         }
    995         Map<String, Integer> names;
    996         names = CalendarDataUtility.retrieveFieldValueNames(getCalendarType(), field, style, locale);
    997         // If strings[] has fewer than eras[], get more names from eras[].
    998         if (names != null) {
    999             if (field == ERA) {
   1000                 int size = names.size();
   1001                 if (style == ALL_STYLES) {
   1002                     Set<Integer> values = new HashSet<>();
   1003                     // count unique era values
   1004                     for (String key : names.keySet()) {
   1005                         values.add(names.get(key));
   1006                     }
   1007                     size = values.size();
   1008                 }
   1009                 if (size < eras.length) {
   1010                     int baseStyle = getBaseStyle(style);
   1011                     for (int i = size; i < eras.length; i++) {
   1012                         Era era = eras[i];
   1013                         if (baseStyle == ALL_STYLES || baseStyle == SHORT
   1014                                 || baseStyle == NARROW_FORMAT) {
   1015                             names.put(era.getAbbreviation(), i);
   1016                         }
   1017                         if (baseStyle == ALL_STYLES || baseStyle == LONG) {
   1018                             names.put(era.getName(), i);
   1019                         }
   1020                     }
   1021                 }
   1022             }
   1023         }
   1024         return names;
   1025     }
   1026 
   1027     /**
   1028      * Returns the minimum value for the given calendar field of this
   1029      * <code>Calendar</code> instance. The minimum value is
   1030      * defined as the smallest value returned by the {@link
   1031      * Calendar#get(int) get} method for any possible time value,
   1032      * taking into consideration the current values of the
   1033      * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
   1034      * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
   1035      * and {@link Calendar#getTimeZone() getTimeZone} methods.
   1036      *
   1037      * @param field the calendar field.
   1038      * @return the minimum value for the given calendar field.
   1039      * @see #getMaximum(int)
   1040      * @see #getGreatestMinimum(int)
   1041      * @see #getLeastMaximum(int)
   1042      * @see #getActualMinimum(int)
   1043      * @see #getActualMaximum(int)
   1044      */
   1045     public int getMinimum(int field) {
   1046         return MIN_VALUES[field];
   1047     }
   1048 
   1049     /**
   1050      * Returns the maximum value for the given calendar field of this
   1051      * <code>GregorianCalendar</code> instance. The maximum value is
   1052      * defined as the largest value returned by the {@link
   1053      * Calendar#get(int) get} method for any possible time value,
   1054      * taking into consideration the current values of the
   1055      * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
   1056      * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
   1057      * and {@link Calendar#getTimeZone() getTimeZone} methods.
   1058      *
   1059      * @param field the calendar field.
   1060      * @return the maximum value for the given calendar field.
   1061      * @see #getMinimum(int)
   1062      * @see #getGreatestMinimum(int)
   1063      * @see #getLeastMaximum(int)
   1064      * @see #getActualMinimum(int)
   1065      * @see #getActualMaximum(int)
   1066      */
   1067     public int getMaximum(int field) {
   1068         switch (field) {
   1069         case YEAR:
   1070             {
   1071                 // The value should depend on the time zone of this calendar.
   1072                 LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
   1073                                                                      getZone());
   1074                 return Math.max(LEAST_MAX_VALUES[YEAR], d.getYear());
   1075             }
   1076         }
   1077         return MAX_VALUES[field];
   1078     }
   1079 
   1080     /**
   1081      * Returns the highest minimum value for the given calendar field
   1082      * of this <code>GregorianCalendar</code> instance. The highest
   1083      * minimum value is defined as the largest value returned by
   1084      * {@link #getActualMinimum(int)} for any possible time value,
   1085      * taking into consideration the current values of the
   1086      * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
   1087      * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
   1088      * and {@link Calendar#getTimeZone() getTimeZone} methods.
   1089      *
   1090      * @param field the calendar field.
   1091      * @return the highest minimum value for the given calendar field.
   1092      * @see #getMinimum(int)
   1093      * @see #getMaximum(int)
   1094      * @see #getLeastMaximum(int)
   1095      * @see #getActualMinimum(int)
   1096      * @see #getActualMaximum(int)
   1097      */
   1098     public int getGreatestMinimum(int field) {
   1099         return field == YEAR ? 1 : MIN_VALUES[field];
   1100     }
   1101 
   1102     /**
   1103      * Returns the lowest maximum value for the given calendar field
   1104      * of this <code>GregorianCalendar</code> instance. The lowest
   1105      * maximum value is defined as the smallest value returned by
   1106      * {@link #getActualMaximum(int)} for any possible time value,
   1107      * taking into consideration the current values of the
   1108      * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
   1109      * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
   1110      * and {@link Calendar#getTimeZone() getTimeZone} methods.
   1111      *
   1112      * @param field the calendar field
   1113      * @return the lowest maximum value for the given calendar field.
   1114      * @see #getMinimum(int)
   1115      * @see #getMaximum(int)
   1116      * @see #getGreatestMinimum(int)
   1117      * @see #getActualMinimum(int)
   1118      * @see #getActualMaximum(int)
   1119      */
   1120     public int getLeastMaximum(int field) {
   1121         switch (field) {
   1122         case YEAR:
   1123             {
   1124                 return Math.min(LEAST_MAX_VALUES[YEAR], getMaximum(YEAR));
   1125             }
   1126         }
   1127         return LEAST_MAX_VALUES[field];
   1128     }
   1129 
   1130     /**
   1131      * Returns the minimum value that this calendar field could have,
   1132      * taking into consideration the given time value and the current
   1133      * values of the
   1134      * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
   1135      * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
   1136      * and {@link Calendar#getTimeZone() getTimeZone} methods.
   1137      *
   1138      * @param field the calendar field
   1139      * @return the minimum of the given field for the time value of
   1140      * this <code>JapaneseImperialCalendar</code>
   1141      * @see #getMinimum(int)
   1142      * @see #getMaximum(int)
   1143      * @see #getGreatestMinimum(int)
   1144      * @see #getLeastMaximum(int)
   1145      * @see #getActualMaximum(int)
   1146      */
   1147     public int getActualMinimum(int field) {
   1148         if (!isFieldSet(YEAR_MASK|MONTH_MASK|WEEK_OF_YEAR_MASK, field)) {
   1149             return getMinimum(field);
   1150         }
   1151 
   1152         int value = 0;
   1153         JapaneseImperialCalendar jc = getNormalizedCalendar();
   1154         // Get a local date which includes time of day and time zone,
   1155         // which are missing in jc.jdate.
   1156         LocalGregorianCalendar.Date jd = jcal.getCalendarDate(jc.getTimeInMillis(),
   1157                                                               getZone());
   1158         int eraIndex = getEraIndex(jd);
   1159         switch (field) {
   1160         case YEAR:
   1161             {
   1162                 if (eraIndex > BEFORE_MEIJI) {
   1163                     value = 1;
   1164                     long since = eras[eraIndex].getSince(getZone());
   1165                     CalendarDate d = jcal.getCalendarDate(since, getZone());
   1166                     // Use the same year in jd to take care of leap
   1167                     // years. i.e., both jd and d must agree on leap
   1168                     // or common years.
   1169                     jd.setYear(d.getYear());
   1170                     jcal.normalize(jd);
   1171                     assert jd.isLeapYear() == d.isLeapYear();
   1172                     if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
   1173                         value++;
   1174                     }
   1175                 } else {
   1176                     value = getMinimum(field);
   1177                     CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
   1178                     // Use an equvalent year of d.getYear() if
   1179                     // possible. Otherwise, ignore the leap year and
   1180                     // common year difference.
   1181                     int y = d.getYear();
   1182                     if (y > 400) {
   1183                         y -= 400;
   1184                     }
   1185                     jd.setYear(y);
   1186                     jcal.normalize(jd);
   1187                     if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
   1188                         value++;
   1189                     }
   1190                 }
   1191             }
   1192             break;
   1193 
   1194         case MONTH:
   1195             {
   1196                 // In Before Meiji and Meiji, January is the first month.
   1197                 if (eraIndex > MEIJI && jd.getYear() == 1) {
   1198                     long since = eras[eraIndex].getSince(getZone());
   1199                     CalendarDate d = jcal.getCalendarDate(since, getZone());
   1200                     value = d.getMonth() - 1;
   1201                     if (jd.getDayOfMonth() < d.getDayOfMonth()) {
   1202                         value++;
   1203                     }
   1204                 }
   1205             }
   1206             break;
   1207 
   1208         case WEEK_OF_YEAR:
   1209             {
   1210                 value = 1;
   1211                 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
   1212                 // shift 400 years to avoid underflow
   1213                 d.addYear(+400);
   1214                 jcal.normalize(d);
   1215                 jd.setEra(d.getEra());
   1216                 jd.setYear(d.getYear());
   1217                 jcal.normalize(jd);
   1218 
   1219                 long jan1 = jcal.getFixedDate(d);
   1220                 long fd = jcal.getFixedDate(jd);
   1221                 int woy = getWeekNumber(jan1, fd);
   1222                 long day1 = fd - (7 * (woy - 1));
   1223                 if ((day1 < jan1) ||
   1224                     (day1 == jan1 &&
   1225                      jd.getTimeOfDay() < d.getTimeOfDay())) {
   1226                     value++;
   1227                 }
   1228             }
   1229             break;
   1230         }
   1231         return value;
   1232     }
   1233 
   1234     /**
   1235      * Returns the maximum value that this calendar field could have,
   1236      * taking into consideration the given time value and the current
   1237      * values of the
   1238      * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
   1239      * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
   1240      * and
   1241      * {@link Calendar#getTimeZone() getTimeZone} methods.
   1242      * For example, if the date of this instance is Heisei 16February 1,
   1243      * the actual maximum value of the <code>DAY_OF_MONTH</code> field
   1244      * is 29 because Heisei 16 is a leap year, and if the date of this
   1245      * instance is Heisei 17 February 1, it's 28.
   1246      *
   1247      * @param field the calendar field
   1248      * @return the maximum of the given field for the time value of
   1249      * this <code>JapaneseImperialCalendar</code>
   1250      * @see #getMinimum(int)
   1251      * @see #getMaximum(int)
   1252      * @see #getGreatestMinimum(int)
   1253      * @see #getLeastMaximum(int)
   1254      * @see #getActualMinimum(int)
   1255      */
   1256     public int getActualMaximum(int field) {
   1257         final int fieldsForFixedMax = ERA_MASK|DAY_OF_WEEK_MASK|HOUR_MASK|AM_PM_MASK|
   1258             HOUR_OF_DAY_MASK|MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK|
   1259             ZONE_OFFSET_MASK|DST_OFFSET_MASK;
   1260         if ((fieldsForFixedMax & (1<<field)) != 0) {
   1261             return getMaximum(field);
   1262         }
   1263 
   1264         JapaneseImperialCalendar jc = getNormalizedCalendar();
   1265         LocalGregorianCalendar.Date date = jc.jdate;
   1266         int normalizedYear = date.getNormalizedYear();
   1267 
   1268         int value = -1;
   1269         switch (field) {
   1270         case MONTH:
   1271             {
   1272                 value = DECEMBER;
   1273                 if (isTransitionYear(date.getNormalizedYear())) {
   1274                     // TODO: there may be multiple transitions in a year.
   1275                     int eraIndex = getEraIndex(date);
   1276                     if (date.getYear() != 1) {
   1277                         eraIndex++;
   1278                         assert eraIndex < eras.length;
   1279                     }
   1280                     long transition = sinceFixedDates[eraIndex];
   1281                     long fd = jc.cachedFixedDate;
   1282                     if (fd < transition) {
   1283                         LocalGregorianCalendar.Date ldate
   1284                             = (LocalGregorianCalendar.Date) date.clone();
   1285                         jcal.getCalendarDateFromFixedDate(ldate, transition - 1);
   1286                         value = ldate.getMonth() - 1;
   1287                     }
   1288                 } else {
   1289                     LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
   1290                                                                          getZone());
   1291                     if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) {
   1292                         value = d.getMonth() - 1;
   1293                     }
   1294                 }
   1295             }
   1296             break;
   1297 
   1298         case DAY_OF_MONTH:
   1299             value = jcal.getMonthLength(date);
   1300             break;
   1301 
   1302         case DAY_OF_YEAR:
   1303             {
   1304                 if (isTransitionYear(date.getNormalizedYear())) {
   1305                     // Handle transition year.
   1306                     // TODO: there may be multiple transitions in a year.
   1307                     int eraIndex = getEraIndex(date);
   1308                     if (date.getYear() != 1) {
   1309                         eraIndex++;
   1310                         assert eraIndex < eras.length;
   1311                     }
   1312                     long transition = sinceFixedDates[eraIndex];
   1313                     long fd = jc.cachedFixedDate;
   1314                     CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
   1315                     d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1);
   1316                     if (fd < transition) {
   1317                         value = (int)(transition - gcal.getFixedDate(d));
   1318                     } else {
   1319                         d.addYear(+1);
   1320                         value = (int)(gcal.getFixedDate(d) - transition);
   1321                     }
   1322                 } else {
   1323                     LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
   1324                                                                          getZone());
   1325                     if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) {
   1326                         long fd = jcal.getFixedDate(d);
   1327                         long jan1 = getFixedDateJan1(d, fd);
   1328                         value = (int)(fd - jan1) + 1;
   1329                     } else if (date.getYear() == getMinimum(YEAR)) {
   1330                         CalendarDate d1 = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
   1331                         long fd1 = jcal.getFixedDate(d1);
   1332                         d1.addYear(1);
   1333                         d1.setMonth(BaseCalendar.JANUARY).setDayOfMonth(1);
   1334                         jcal.normalize(d1);
   1335                         long fd2 = jcal.getFixedDate(d1);
   1336                         value = (int)(fd2 - fd1);
   1337                     } else {
   1338                         value = jcal.getYearLength(date);
   1339                     }
   1340                 }
   1341             }
   1342             break;
   1343 
   1344         case WEEK_OF_YEAR:
   1345             {
   1346                 if (!isTransitionYear(date.getNormalizedYear())) {
   1347                     LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE,
   1348                                                                           getZone());
   1349                     if (date.getEra() == jd.getEra() && date.getYear() == jd.getYear()) {
   1350                         long fd = jcal.getFixedDate(jd);
   1351                         long jan1 = getFixedDateJan1(jd, fd);
   1352                         value = getWeekNumber(jan1, fd);
   1353                     } else if (date.getEra() == null && date.getYear() == getMinimum(YEAR)) {
   1354                         CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
   1355                         // shift 400 years to avoid underflow
   1356                         d.addYear(+400);
   1357                         jcal.normalize(d);
   1358                         jd.setEra(d.getEra());
   1359                         jd.setDate(d.getYear() + 1, BaseCalendar.JANUARY, 1);
   1360                         jcal.normalize(jd);
   1361                         long jan1 = jcal.getFixedDate(d);
   1362                         long nextJan1 = jcal.getFixedDate(jd);
   1363                         long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
   1364                                                                                             getFirstDayOfWeek());
   1365                         int ndays = (int)(nextJan1st - nextJan1);
   1366                         if (ndays >= getMinimalDaysInFirstWeek()) {
   1367                             nextJan1st -= 7;
   1368                         }
   1369                         value = getWeekNumber(jan1, nextJan1st);
   1370                     } else {
   1371                         // Get the day of week of January 1 of the year
   1372                         CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
   1373                         d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1);
   1374                         int dayOfWeek = gcal.getDayOfWeek(d);
   1375                         // Normalize the day of week with the firstDayOfWeek value
   1376                         dayOfWeek -= getFirstDayOfWeek();
   1377                         if (dayOfWeek < 0) {
   1378                             dayOfWeek += 7;
   1379                         }
   1380                         value = 52;
   1381                         int magic = dayOfWeek + getMinimalDaysInFirstWeek() - 1;
   1382                         if ((magic == 6) ||
   1383                             (date.isLeapYear() && (magic == 5 || magic == 12))) {
   1384                             value++;
   1385                         }
   1386                     }
   1387                     break;
   1388                 }
   1389 
   1390                 if (jc == this) {
   1391                     jc = (JapaneseImperialCalendar) jc.clone();
   1392                 }
   1393                 int max = getActualMaximum(DAY_OF_YEAR);
   1394                 jc.set(DAY_OF_YEAR, max);
   1395                 value = jc.get(WEEK_OF_YEAR);
   1396                 if (value == 1 && max > 7) {
   1397                     jc.add(WEEK_OF_YEAR, -1);
   1398                     value = jc.get(WEEK_OF_YEAR);
   1399                 }
   1400             }
   1401             break;
   1402 
   1403         case WEEK_OF_MONTH:
   1404             {
   1405                 LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE,
   1406                                                                       getZone());
   1407                 if (!(date.getEra() == jd.getEra() && date.getYear() == jd.getYear())) {
   1408                     CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
   1409                     d.setDate(date.getNormalizedYear(), date.getMonth(), 1);
   1410                     int dayOfWeek = gcal.getDayOfWeek(d);
   1411                     int monthLength = gcal.getMonthLength(d);
   1412                     dayOfWeek -= getFirstDayOfWeek();
   1413                     if (dayOfWeek < 0) {
   1414                         dayOfWeek += 7;
   1415                     }
   1416                     int nDaysFirstWeek = 7 - dayOfWeek; // # of days in the first week
   1417                     value = 3;
   1418                     if (nDaysFirstWeek >= getMinimalDaysInFirstWeek()) {
   1419                         value++;
   1420                     }
   1421                     monthLength -= nDaysFirstWeek + 7 * 3;
   1422                     if (monthLength > 0) {
   1423                         value++;
   1424                         if (monthLength > 7) {
   1425                             value++;
   1426                         }
   1427                     }
   1428                 } else {
   1429                     long fd = jcal.getFixedDate(jd);
   1430                     long month1 = fd - jd.getDayOfMonth() + 1;
   1431                     value = getWeekNumber(month1, fd);
   1432                 }
   1433             }
   1434             break;
   1435 
   1436         case DAY_OF_WEEK_IN_MONTH:
   1437             {
   1438                 int ndays, dow1;
   1439                 int dow = date.getDayOfWeek();
   1440                 BaseCalendar.Date d = (BaseCalendar.Date) date.clone();
   1441                 ndays = jcal.getMonthLength(d);
   1442                 d.setDayOfMonth(1);
   1443                 jcal.normalize(d);
   1444                 dow1 = d.getDayOfWeek();
   1445                 int x = dow - dow1;
   1446                 if (x < 0) {
   1447                     x += 7;
   1448                 }
   1449                 ndays -= x;
   1450                 value = (ndays + 6) / 7;
   1451             }
   1452             break;
   1453 
   1454         case YEAR:
   1455             {
   1456                 CalendarDate jd = jcal.getCalendarDate(jc.getTimeInMillis(), getZone());
   1457                 CalendarDate d;
   1458                 int eraIndex = getEraIndex(date);
   1459                 if (eraIndex == eras.length - 1) {
   1460                     d = jcal.getCalendarDate(Long.MAX_VALUE, getZone());
   1461                     value = d.getYear();
   1462                     // Use an equivalent year for the
   1463                     // getYearOffsetInMillis call to avoid overflow.
   1464                     if (value > 400) {
   1465                         jd.setYear(value - 400);
   1466                     }
   1467                 } else {
   1468                     d = jcal.getCalendarDate(eras[eraIndex + 1].getSince(getZone()) - 1,
   1469                                              getZone());
   1470                     value = d.getYear();
   1471                     // Use the same year as d.getYear() to be
   1472                     // consistent with leap and common years.
   1473                     jd.setYear(value);
   1474                 }
   1475                 jcal.normalize(jd);
   1476                 if (getYearOffsetInMillis(jd) > getYearOffsetInMillis(d)) {
   1477                     value--;
   1478                 }
   1479             }
   1480             break;
   1481 
   1482         default:
   1483             throw new ArrayIndexOutOfBoundsException(field);
   1484         }
   1485         return value;
   1486     }
   1487 
   1488     /**
   1489      * Returns the millisecond offset from the beginning of the
   1490      * year. In the year for Long.MIN_VALUE, it's a pseudo value
   1491      * beyond the limit. The given CalendarDate object must have been
   1492      * normalized before calling this method.
   1493      */
   1494     private long getYearOffsetInMillis(CalendarDate date) {
   1495         long t = (jcal.getDayOfYear(date) - 1) * ONE_DAY;
   1496         return t + date.getTimeOfDay() - date.getZoneOffset();
   1497     }
   1498 
   1499     public Object clone() {
   1500         JapaneseImperialCalendar other = (JapaneseImperialCalendar) super.clone();
   1501 
   1502         other.jdate = (LocalGregorianCalendar.Date) jdate.clone();
   1503         other.originalFields = null;
   1504         other.zoneOffsets = null;
   1505         return other;
   1506     }
   1507 
   1508     public TimeZone getTimeZone() {
   1509         TimeZone zone = super.getTimeZone();
   1510         // To share the zone by the CalendarDate
   1511         jdate.setZone(zone);
   1512         return zone;
   1513     }
   1514 
   1515     public void setTimeZone(TimeZone zone) {
   1516         super.setTimeZone(zone);
   1517         // To share the zone by the CalendarDate
   1518         jdate.setZone(zone);
   1519     }
   1520 
   1521     /**
   1522      * The fixed date corresponding to jdate. If the value is
   1523      * Long.MIN_VALUE, the fixed date value is unknown.
   1524      */
   1525     transient private long cachedFixedDate = Long.MIN_VALUE;
   1526 
   1527     /**
   1528      * Converts the time value (millisecond offset from the <a
   1529      * href="Calendar.html#Epoch">Epoch</a>) to calendar field values.
   1530      * The time is <em>not</em>
   1531      * recomputed first; to recompute the time, then the fields, call the
   1532      * <code>complete</code> method.
   1533      *
   1534      * @see Calendar#complete
   1535      */
   1536     protected void computeFields() {
   1537         int mask = 0;
   1538         if (isPartiallyNormalized()) {
   1539             // Determine which calendar fields need to be computed.
   1540             mask = getSetStateFields();
   1541             int fieldMask = ~mask & ALL_FIELDS;
   1542             if (fieldMask != 0 || cachedFixedDate == Long.MIN_VALUE) {
   1543                 mask |= computeFields(fieldMask,
   1544                                       mask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK));
   1545                 assert mask == ALL_FIELDS;
   1546             }
   1547         } else {
   1548             // Specify all fields
   1549             mask = ALL_FIELDS;
   1550             computeFields(mask, 0);
   1551         }
   1552         // After computing all the fields, set the field state to `COMPUTED'.
   1553         setFieldsComputed(mask);
   1554     }
   1555 
   1556     /**
   1557      * This computeFields implements the conversion from UTC
   1558      * (millisecond offset from the Epoch) to calendar
   1559      * field values. fieldMask specifies which fields to change the
   1560      * setting state to COMPUTED, although all fields are set to
   1561      * the correct values. This is required to fix 4685354.
   1562      *
   1563      * @param fieldMask a bit mask to specify which fields to change
   1564      * the setting state.
   1565      * @param tzMask a bit mask to specify which time zone offset
   1566      * fields to be used for time calculations
   1567      * @return a new field mask that indicates what field values have
   1568      * actually been set.
   1569      */
   1570     private int computeFields(int fieldMask, int tzMask) {
   1571         int zoneOffset = 0;
   1572         TimeZone tz = getZone();
   1573         if (zoneOffsets == null) {
   1574             zoneOffsets = new int[2];
   1575         }
   1576         if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
   1577             // Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
   1578             // if (tz instanceof ZoneInfo) {
   1579             //     zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
   1580             // } else {
   1581                 zoneOffset = tz.getOffset(time);
   1582                 zoneOffsets[0] = tz.getRawOffset();
   1583                 zoneOffsets[1] = zoneOffset - zoneOffsets[0];
   1584             // }
   1585         }
   1586         if (tzMask != 0) {
   1587             if (isFieldSet(tzMask, ZONE_OFFSET)) {
   1588                 zoneOffsets[0] = internalGet(ZONE_OFFSET);
   1589             }
   1590             if (isFieldSet(tzMask, DST_OFFSET)) {
   1591                 zoneOffsets[1] = internalGet(DST_OFFSET);
   1592             }
   1593             zoneOffset = zoneOffsets[0] + zoneOffsets[1];
   1594         }
   1595 
   1596         // By computing time and zoneOffset separately, we can take
   1597         // the wider range of time+zoneOffset than the previous
   1598         // implementation.
   1599         long fixedDate = zoneOffset / ONE_DAY;
   1600         int timeOfDay = zoneOffset % (int)ONE_DAY;
   1601         fixedDate += time / ONE_DAY;
   1602         timeOfDay += (int) (time % ONE_DAY);
   1603         if (timeOfDay >= ONE_DAY) {
   1604             timeOfDay -= ONE_DAY;
   1605             ++fixedDate;
   1606         } else {
   1607             while (timeOfDay < 0) {
   1608                 timeOfDay += ONE_DAY;
   1609                 --fixedDate;
   1610             }
   1611         }
   1612         fixedDate += EPOCH_OFFSET;
   1613 
   1614         // See if we can use jdate to avoid date calculation.
   1615         if (fixedDate != cachedFixedDate || fixedDate < 0) {
   1616             jcal.getCalendarDateFromFixedDate(jdate, fixedDate);
   1617             cachedFixedDate = fixedDate;
   1618         }
   1619         int era = getEraIndex(jdate);
   1620         int year = jdate.getYear();
   1621 
   1622         // Always set the ERA and YEAR values.
   1623         internalSet(ERA, era);
   1624         internalSet(YEAR, year);
   1625         int mask = fieldMask | (ERA_MASK|YEAR_MASK);
   1626 
   1627         int month =  jdate.getMonth() - 1; // 0-based
   1628         int dayOfMonth = jdate.getDayOfMonth();
   1629 
   1630         // Set the basic date fields.
   1631         if ((fieldMask & (MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK))
   1632             != 0) {
   1633             internalSet(MONTH, month);
   1634             internalSet(DAY_OF_MONTH, dayOfMonth);
   1635             internalSet(DAY_OF_WEEK, jdate.getDayOfWeek());
   1636             mask |= MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK;
   1637         }
   1638 
   1639         if ((fieldMask & (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
   1640                           |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK)) != 0) {
   1641             if (timeOfDay != 0) {
   1642                 int hours = timeOfDay / ONE_HOUR;
   1643                 internalSet(HOUR_OF_DAY, hours);
   1644                 internalSet(AM_PM, hours / 12); // Assume AM == 0
   1645                 internalSet(HOUR, hours % 12);
   1646                 int r = timeOfDay % ONE_HOUR;
   1647                 internalSet(MINUTE, r / ONE_MINUTE);
   1648                 r %= ONE_MINUTE;
   1649                 internalSet(SECOND, r / ONE_SECOND);
   1650                 internalSet(MILLISECOND, r % ONE_SECOND);
   1651             } else {
   1652                 internalSet(HOUR_OF_DAY, 0);
   1653                 internalSet(AM_PM, AM);
   1654                 internalSet(HOUR, 0);
   1655                 internalSet(MINUTE, 0);
   1656                 internalSet(SECOND, 0);
   1657                 internalSet(MILLISECOND, 0);
   1658             }
   1659             mask |= (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
   1660                      |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK);
   1661         }
   1662 
   1663         if ((fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) != 0) {
   1664             internalSet(ZONE_OFFSET, zoneOffsets[0]);
   1665             internalSet(DST_OFFSET, zoneOffsets[1]);
   1666             mask |= (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
   1667         }
   1668 
   1669         if ((fieldMask & (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK
   1670                           |WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK)) != 0) {
   1671             int normalizedYear = jdate.getNormalizedYear();
   1672             // If it's a year of an era transition, we need to handle
   1673             // irregular year boundaries.
   1674             boolean transitionYear = isTransitionYear(jdate.getNormalizedYear());
   1675             int dayOfYear;
   1676             long fixedDateJan1;
   1677             if (transitionYear) {
   1678                 fixedDateJan1 = getFixedDateJan1(jdate, fixedDate);
   1679                 dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
   1680             } else if (normalizedYear == MIN_VALUES[YEAR]) {
   1681                 CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
   1682                 fixedDateJan1 = jcal.getFixedDate(dx);
   1683                 dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
   1684             } else {
   1685                 dayOfYear = (int) jcal.getDayOfYear(jdate);
   1686                 fixedDateJan1 = fixedDate - dayOfYear + 1;
   1687             }
   1688             long fixedDateMonth1 = transitionYear ?
   1689                 getFixedDateMonth1(jdate, fixedDate) : fixedDate - dayOfMonth + 1;
   1690 
   1691             internalSet(DAY_OF_YEAR, dayOfYear);
   1692             internalSet(DAY_OF_WEEK_IN_MONTH, (dayOfMonth - 1) / 7 + 1);
   1693 
   1694             int weekOfYear = getWeekNumber(fixedDateJan1, fixedDate);
   1695 
   1696             // The spec is to calculate WEEK_OF_YEAR in the
   1697             // ISO8601-style. This creates problems, though.
   1698             if (weekOfYear == 0) {
   1699                 // If the date belongs to the last week of the
   1700                 // previous year, use the week number of "12/31" of
   1701                 // the "previous" year. Again, if the previous year is
   1702                 // a transition year, we need to take care of it.
   1703                 // Usually the previous day of the first day of a year
   1704                 // is December 31, which is not always true in the
   1705                 // Japanese imperial calendar system.
   1706                 long fixedDec31 = fixedDateJan1 - 1;
   1707                 long prevJan1;
   1708                 LocalGregorianCalendar.Date d = getCalendarDate(fixedDec31);
   1709                 if (!(transitionYear || isTransitionYear(d.getNormalizedYear()))) {
   1710                     prevJan1 = fixedDateJan1 - 365;
   1711                     if (d.isLeapYear()) {
   1712                         --prevJan1;
   1713                     }
   1714                 } else if (transitionYear) {
   1715                     if (jdate.getYear() == 1) {
   1716                         // As of Heisei (since Meiji) there's no case
   1717                         // that there are multiple transitions in a
   1718                         // year.  Historically there was such
   1719                         // case. There might be such case again in the
   1720                         // future.
   1721                         if (era > HEISEI) {
   1722                             CalendarDate pd = eras[era - 1].getSinceDate();
   1723                             if (normalizedYear == pd.getYear()) {
   1724                                 d.setMonth(pd.getMonth()).setDayOfMonth(pd.getDayOfMonth());
   1725                             }
   1726                         } else {
   1727                             d.setMonth(LocalGregorianCalendar.JANUARY).setDayOfMonth(1);
   1728                         }
   1729                         jcal.normalize(d);
   1730                         prevJan1 = jcal.getFixedDate(d);
   1731                     } else {
   1732                         prevJan1 = fixedDateJan1 - 365;
   1733                         if (d.isLeapYear()) {
   1734                             --prevJan1;
   1735                         }
   1736                     }
   1737                 } else {
   1738                     CalendarDate cd = eras[getEraIndex(jdate)].getSinceDate();
   1739                     d.setMonth(cd.getMonth()).setDayOfMonth(cd.getDayOfMonth());
   1740                     jcal.normalize(d);
   1741                     prevJan1 = jcal.getFixedDate(d);
   1742                 }
   1743                 weekOfYear = getWeekNumber(prevJan1, fixedDec31);
   1744             } else {
   1745                 if (!transitionYear) {
   1746                     // Regular years
   1747                     if (weekOfYear >= 52) {
   1748                         long nextJan1 = fixedDateJan1 + 365;
   1749                         if (jdate.isLeapYear()) {
   1750                             nextJan1++;
   1751                         }
   1752                         long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
   1753                                                                                             getFirstDayOfWeek());
   1754                         int ndays = (int)(nextJan1st - nextJan1);
   1755                         if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
   1756                             // The first days forms a week in which the date is included.
   1757                             weekOfYear = 1;
   1758                         }
   1759                     }
   1760                 } else {
   1761                     LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
   1762                     long nextJan1;
   1763                     if (jdate.getYear() == 1) {
   1764                         d.addYear(+1);
   1765                         d.setMonth(LocalGregorianCalendar.JANUARY).setDayOfMonth(1);
   1766                         nextJan1 = jcal.getFixedDate(d);
   1767                     } else {
   1768                         int nextEraIndex = getEraIndex(d) + 1;
   1769                         CalendarDate cd = eras[nextEraIndex].getSinceDate();
   1770                         d.setEra(eras[nextEraIndex]);
   1771                         d.setDate(1, cd.getMonth(), cd.getDayOfMonth());
   1772                         jcal.normalize(d);
   1773                         nextJan1 = jcal.getFixedDate(d);
   1774                     }
   1775                     long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
   1776                                                                                         getFirstDayOfWeek());
   1777                     int ndays = (int)(nextJan1st - nextJan1);
   1778                     if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
   1779                         // The first days forms a week in which the date is included.
   1780                         weekOfYear = 1;
   1781                     }
   1782                 }
   1783             }
   1784             internalSet(WEEK_OF_YEAR, weekOfYear);
   1785             internalSet(WEEK_OF_MONTH, getWeekNumber(fixedDateMonth1, fixedDate));
   1786             mask |= (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK|WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK);
   1787         }
   1788         return mask;
   1789     }
   1790 
   1791     /**
   1792      * Returns the number of weeks in a period between fixedDay1 and
   1793      * fixedDate. The getFirstDayOfWeek-getMinimalDaysInFirstWeek rule
   1794      * is applied to calculate the number of weeks.
   1795      *
   1796      * @param fixedDay1 the fixed date of the first day of the period
   1797      * @param fixedDate the fixed date of the last day of the period
   1798      * @return the number of weeks of the given period
   1799      */
   1800     private int getWeekNumber(long fixedDay1, long fixedDate) {
   1801         // We can always use `jcal' since Julian and Gregorian are the
   1802         // same thing for this calculation.
   1803         long fixedDay1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDay1 + 6,
   1804                                                                              getFirstDayOfWeek());
   1805         int ndays = (int)(fixedDay1st - fixedDay1);
   1806         assert ndays <= 7;
   1807         if (ndays >= getMinimalDaysInFirstWeek()) {
   1808             fixedDay1st -= 7;
   1809         }
   1810         int normalizedDayOfPeriod = (int)(fixedDate - fixedDay1st);
   1811         if (normalizedDayOfPeriod >= 0) {
   1812             return normalizedDayOfPeriod / 7 + 1;
   1813         }
   1814         return CalendarUtils.floorDivide(normalizedDayOfPeriod, 7) + 1;
   1815     }
   1816 
   1817     /**
   1818      * Converts calendar field values to the time value (millisecond
   1819      * offset from the <a href="Calendar.html#Epoch">Epoch</a>).
   1820      *
   1821      * @exception IllegalArgumentException if any calendar fields are invalid.
   1822      */
   1823     protected void computeTime() {
   1824         // In non-lenient mode, perform brief checking of calendar
   1825         // fields which have been set externally. Through this
   1826         // checking, the field values are stored in originalFields[]
   1827         // to see if any of them are normalized later.
   1828         if (!isLenient()) {
   1829             if (originalFields == null) {
   1830                 originalFields = new int[FIELD_COUNT];
   1831             }
   1832             for (int field = 0; field < FIELD_COUNT; field++) {
   1833                 int value = internalGet(field);
   1834                 if (isExternallySet(field)) {
   1835                     // Quick validation for any out of range values
   1836                     if (value < getMinimum(field) || value > getMaximum(field)) {
   1837                         throw new IllegalArgumentException(getFieldName(field));
   1838                     }
   1839                 }
   1840                 originalFields[field] = value;
   1841             }
   1842         }
   1843 
   1844         // Let the super class determine which calendar fields to be
   1845         // used to calculate the time.
   1846         int fieldMask = selectFields();
   1847 
   1848         int year;
   1849         int era;
   1850 
   1851         if (isSet(ERA)) {
   1852             era = internalGet(ERA);
   1853             year = isSet(YEAR) ? internalGet(YEAR) : 1;
   1854         } else {
   1855             if (isSet(YEAR)) {
   1856                 era = eras.length - 1;
   1857                 year = internalGet(YEAR);
   1858             } else {
   1859                 // Equivalent to 1970 (Gregorian)
   1860                 era = SHOWA;
   1861                 year = 45;
   1862             }
   1863         }
   1864 
   1865         // Calculate the time of day. We rely on the convention that
   1866         // an UNSET field has 0.
   1867         long timeOfDay = 0;
   1868         if (isFieldSet(fieldMask, HOUR_OF_DAY)) {
   1869             timeOfDay += (long) internalGet(HOUR_OF_DAY);
   1870         } else {
   1871             timeOfDay += internalGet(HOUR);
   1872             // The default value of AM_PM is 0 which designates AM.
   1873             if (isFieldSet(fieldMask, AM_PM)) {
   1874                 timeOfDay += 12 * internalGet(AM_PM);
   1875             }
   1876         }
   1877         timeOfDay *= 60;
   1878         timeOfDay += internalGet(MINUTE);
   1879         timeOfDay *= 60;
   1880         timeOfDay += internalGet(SECOND);
   1881         timeOfDay *= 1000;
   1882         timeOfDay += internalGet(MILLISECOND);
   1883 
   1884         // Convert the time of day to the number of days and the
   1885         // millisecond offset from midnight.
   1886         long fixedDate = timeOfDay / ONE_DAY;
   1887         timeOfDay %= ONE_DAY;
   1888         while (timeOfDay < 0) {
   1889             timeOfDay += ONE_DAY;
   1890             --fixedDate;
   1891         }
   1892 
   1893         // Calculate the fixed date since January 1, 1 (Gregorian).
   1894         fixedDate += getFixedDate(era, year, fieldMask);
   1895 
   1896         // millis represents local wall-clock time in milliseconds.
   1897         long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
   1898 
   1899         // Compute the time zone offset and DST offset.  There are two potential
   1900         // ambiguities here.  We'll assume a 2:00 am (wall time) switchover time
   1901         // for discussion purposes here.
   1902         // 1. The transition into DST.  Here, a designated time of 2:00 am - 2:59 am
   1903         //    can be in standard or in DST depending.  However, 2:00 am is an invalid
   1904         //    representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
   1905         //    We assume standard time.
   1906         // 2. The transition out of DST.  Here, a designated time of 1:00 am - 1:59 am
   1907         //    can be in standard or DST.  Both are valid representations (the rep
   1908         //    jumps from 1:59:59 DST to 1:00:00 Std).
   1909         //    Again, we assume standard time.
   1910         // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
   1911         // or DST_OFFSET fields; then we use those fields.
   1912         TimeZone zone = getZone();
   1913         if (zoneOffsets == null) {
   1914             zoneOffsets = new int[2];
   1915         }
   1916         int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
   1917         if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
   1918             // Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
   1919             // if (zone instanceof ZoneInfo) {
   1920             //     ((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets);
   1921             // } else {
   1922                 zone.getOffsets(millis - zone.getRawOffset(), zoneOffsets);
   1923             // }
   1924         }
   1925         if (tzMask != 0) {
   1926             if (isFieldSet(tzMask, ZONE_OFFSET)) {
   1927                 zoneOffsets[0] = internalGet(ZONE_OFFSET);
   1928             }
   1929             if (isFieldSet(tzMask, DST_OFFSET)) {
   1930                 zoneOffsets[1] = internalGet(DST_OFFSET);
   1931             }
   1932         }
   1933 
   1934         // Adjust the time zone offset values to get the UTC time.
   1935         millis -= zoneOffsets[0] + zoneOffsets[1];
   1936 
   1937         // Set this calendar's time in milliseconds
   1938         time = millis;
   1939 
   1940         int mask = computeFields(fieldMask | getSetStateFields(), tzMask);
   1941 
   1942         if (!isLenient()) {
   1943             for (int field = 0; field < FIELD_COUNT; field++) {
   1944                 if (!isExternallySet(field)) {
   1945                     continue;
   1946                 }
   1947                 if (originalFields[field] != internalGet(field)) {
   1948                     int wrongValue = internalGet(field);
   1949                     // Restore the original field values
   1950                     System.arraycopy(originalFields, 0, fields, 0, fields.length);
   1951                     throw new IllegalArgumentException(getFieldName(field) + "=" + wrongValue
   1952                                                        + ", expected " + originalFields[field]);
   1953                 }
   1954             }
   1955         }
   1956         setFieldsNormalized(mask);
   1957     }
   1958 
   1959     /**
   1960      * Computes the fixed date under either the Gregorian or the
   1961      * Julian calendar, using the given year and the specified calendar fields.
   1962      *
   1963      * @param era era index
   1964      * @param year the normalized year number, with 0 indicating the
   1965      * year 1 BCE, -1 indicating 2 BCE, etc.
   1966      * @param fieldMask the calendar fields to be used for the date calculation
   1967      * @return the fixed date
   1968      * @see Calendar#selectFields
   1969      */
   1970     private long getFixedDate(int era, int year, int fieldMask) {
   1971         int month = JANUARY;
   1972         int firstDayOfMonth = 1;
   1973         if (isFieldSet(fieldMask, MONTH)) {
   1974             // No need to check if MONTH has been set (no isSet(MONTH)
   1975             // call) since its unset value happens to be JANUARY (0).
   1976             month = internalGet(MONTH);
   1977 
   1978             // If the month is out of range, adjust it into range.
   1979             if (month > DECEMBER) {
   1980                 year += month / 12;
   1981                 month %= 12;
   1982             } else if (month < JANUARY) {
   1983                 int[] rem = new int[1];
   1984                 year += CalendarUtils.floorDivide(month, 12, rem);
   1985                 month = rem[0];
   1986             }
   1987         } else {
   1988             if (year == 1 && era != 0) {
   1989                 CalendarDate d = eras[era].getSinceDate();
   1990                 month = d.getMonth() - 1;
   1991                 firstDayOfMonth = d.getDayOfMonth();
   1992             }
   1993         }
   1994 
   1995         // Adjust the base date if year is the minimum value.
   1996         if (year == MIN_VALUES[YEAR]) {
   1997             CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
   1998             int m = dx.getMonth() - 1;
   1999             if (month < m) {
   2000                 month = m;
   2001             }
   2002             if (month == m) {
   2003                 firstDayOfMonth = dx.getDayOfMonth();
   2004             }
   2005         }
   2006 
   2007         LocalGregorianCalendar.Date date = jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
   2008         date.setEra(era > 0 ? eras[era] : null);
   2009         date.setDate(year, month + 1, firstDayOfMonth);
   2010         jcal.normalize(date);
   2011 
   2012         // Get the fixed date since Jan 1, 1 (Gregorian). We are on
   2013         // the first day of either `month' or January in 'year'.
   2014         long fixedDate = jcal.getFixedDate(date);
   2015 
   2016         if (isFieldSet(fieldMask, MONTH)) {
   2017             // Month-based calculations
   2018             if (isFieldSet(fieldMask, DAY_OF_MONTH)) {
   2019                 // We are on the "first day" of the month (which may
   2020                 // not be 1). Just add the offset if DAY_OF_MONTH is
   2021                 // set. If the isSet call returns false, that means
   2022                 // DAY_OF_MONTH has been selected just because of the
   2023                 // selected combination. We don't need to add any
   2024                 // since the default value is the "first day".
   2025                 if (isSet(DAY_OF_MONTH)) {
   2026                     // To avoid underflow with DAY_OF_MONTH-firstDayOfMonth, add
   2027                     // DAY_OF_MONTH, then subtract firstDayOfMonth.
   2028                     fixedDate += internalGet(DAY_OF_MONTH);
   2029                     fixedDate -= firstDayOfMonth;
   2030                 }
   2031             } else {
   2032                 if (isFieldSet(fieldMask, WEEK_OF_MONTH)) {
   2033                     long firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + 6,
   2034                                                                                             getFirstDayOfWeek());
   2035                     // If we have enough days in the first week, then
   2036                     // move to the previous week.
   2037                     if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
   2038                         firstDayOfWeek -= 7;
   2039                     }
   2040                     if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
   2041                         firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
   2042                                                                                            internalGet(DAY_OF_WEEK));
   2043                     }
   2044                     // In lenient mode, we treat days of the previous
   2045                     // months as a part of the specified
   2046                     // WEEK_OF_MONTH. See 4633646.
   2047                     fixedDate = firstDayOfWeek + 7 * (internalGet(WEEK_OF_MONTH) - 1);
   2048                 } else {
   2049                     int dayOfWeek;
   2050                     if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
   2051                         dayOfWeek = internalGet(DAY_OF_WEEK);
   2052                     } else {
   2053                         dayOfWeek = getFirstDayOfWeek();
   2054                     }
   2055                     // We are basing this on the day-of-week-in-month.  The only
   2056                     // trickiness occurs if the day-of-week-in-month is
   2057                     // negative.
   2058                     int dowim;
   2059                     if (isFieldSet(fieldMask, DAY_OF_WEEK_IN_MONTH)) {
   2060                         dowim = internalGet(DAY_OF_WEEK_IN_MONTH);
   2061                     } else {
   2062                         dowim = 1;
   2063                     }
   2064                     if (dowim >= 0) {
   2065                         fixedDate = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + (7 * dowim) - 1,
   2066                                                                                       dayOfWeek);
   2067                     } else {
   2068                         // Go to the first day of the next week of
   2069                         // the specified week boundary.
   2070                         int lastDate = monthLength(month, year) + (7 * (dowim + 1));
   2071                         // Then, get the day of week date on or before the last date.
   2072                         fixedDate = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + lastDate - 1,
   2073                                                                                       dayOfWeek);
   2074                     }
   2075                 }
   2076             }
   2077         } else {
   2078             // We are on the first day of the year.
   2079             if (isFieldSet(fieldMask, DAY_OF_YEAR)) {
   2080                 if (isTransitionYear(date.getNormalizedYear())) {
   2081                     fixedDate = getFixedDateJan1(date, fixedDate);
   2082                 }
   2083                 // Add the offset, then subtract 1. (Make sure to avoid underflow.)
   2084                 fixedDate += internalGet(DAY_OF_YEAR);
   2085                 fixedDate--;
   2086             } else {
   2087                 long firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + 6,
   2088                                                                                         getFirstDayOfWeek());
   2089                 // If we have enough days in the first week, then move
   2090                 // to the previous week.
   2091                 if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
   2092                     firstDayOfWeek -= 7;
   2093                 }
   2094                 if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
   2095                     int dayOfWeek = internalGet(DAY_OF_WEEK);
   2096                     if (dayOfWeek != getFirstDayOfWeek()) {
   2097                         firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
   2098                                                                                            dayOfWeek);
   2099                     }
   2100                 }
   2101                 fixedDate = firstDayOfWeek + 7 * ((long)internalGet(WEEK_OF_YEAR) - 1);
   2102             }
   2103         }
   2104         return fixedDate;
   2105     }
   2106 
   2107     /**
   2108      * Returns the fixed date of the first day of the year (usually
   2109      * January 1) before the specified date.
   2110      *
   2111      * @param date the date for which the first day of the year is
   2112      * calculated. The date has to be in the cut-over year.
   2113      * @param fixedDate the fixed date representation of the date
   2114      */
   2115     private long getFixedDateJan1(LocalGregorianCalendar.Date date, long fixedDate) {
   2116         Era era = date.getEra();
   2117         if (date.getEra() != null && date.getYear() == 1) {
   2118             for (int eraIndex = getEraIndex(date); eraIndex > 0; eraIndex--) {
   2119                 CalendarDate d = eras[eraIndex].getSinceDate();
   2120                 long fd = gcal.getFixedDate(d);
   2121                 // There might be multiple era transitions in a year.
   2122                 if (fd > fixedDate) {
   2123                     continue;
   2124                 }
   2125                 return fd;
   2126             }
   2127         }
   2128         CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
   2129         d.setDate(date.getNormalizedYear(), Gregorian.JANUARY, 1);
   2130         return gcal.getFixedDate(d);
   2131     }
   2132 
   2133     /**
   2134      * Returns the fixed date of the first date of the month (usually
   2135      * the 1st of the month) before the specified date.
   2136      *
   2137      * @param date the date for which the first day of the month is
   2138      * calculated. The date must be in the era transition year.
   2139      * @param fixedDate the fixed date representation of the date
   2140      */
   2141     private long getFixedDateMonth1(LocalGregorianCalendar.Date date,
   2142                                           long fixedDate) {
   2143         int eraIndex = getTransitionEraIndex(date);
   2144         if (eraIndex != -1) {
   2145             long transition = sinceFixedDates[eraIndex];
   2146             // If the given date is on or after the transition date, then
   2147             // return the transition date.
   2148             if (transition <= fixedDate) {
   2149                 return transition;
   2150             }
   2151         }
   2152 
   2153         // Otherwise, we can use the 1st day of the month.
   2154         return fixedDate - date.getDayOfMonth() + 1;
   2155     }
   2156 
   2157     /**
   2158      * Returns a LocalGregorianCalendar.Date produced from the specified fixed date.
   2159      *
   2160      * @param fd the fixed date
   2161      */
   2162     private static LocalGregorianCalendar.Date getCalendarDate(long fd) {
   2163         LocalGregorianCalendar.Date d = jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
   2164         jcal.getCalendarDateFromFixedDate(d, fd);
   2165         return d;
   2166     }
   2167 
   2168     /**
   2169      * Returns the length of the specified month in the specified
   2170      * Gregorian year. The year number must be normalized.
   2171      *
   2172      * @see GregorianCalendar#isLeapYear(int)
   2173      */
   2174     private int monthLength(int month, int gregorianYear) {
   2175         return CalendarUtils.isGregorianLeapYear(gregorianYear) ?
   2176             GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month];
   2177     }
   2178 
   2179     /**
   2180      * Returns the length of the specified month in the year provided
   2181      * by internalGet(YEAR).
   2182      *
   2183      * @see GregorianCalendar#isLeapYear(int)
   2184      */
   2185     private int monthLength(int month) {
   2186         assert jdate.isNormalized();
   2187         return jdate.isLeapYear() ?
   2188             GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month];
   2189     }
   2190 
   2191     private int actualMonthLength() {
   2192         int length = jcal.getMonthLength(jdate);
   2193         int eraIndex = getTransitionEraIndex(jdate);
   2194         if (eraIndex == -1) {
   2195             long transitionFixedDate = sinceFixedDates[eraIndex];
   2196             CalendarDate d = eras[eraIndex].getSinceDate();
   2197             if (transitionFixedDate <= cachedFixedDate) {
   2198                 length -= d.getDayOfMonth() - 1;
   2199             } else {
   2200                 length = d.getDayOfMonth() - 1;
   2201             }
   2202         }
   2203         return length;
   2204     }
   2205 
   2206     /**
   2207      * Returns the index to the new era if the given date is in a
   2208      * transition month.  For example, if the give date is Heisei 1
   2209      * (1989) January 20, then the era index for Heisei is
   2210      * returned. Likewise, if the given date is Showa 64 (1989)
   2211      * January 3, then the era index for Heisei is returned. If the
   2212      * given date is not in any transition month, then -1 is returned.
   2213      */
   2214     private static int getTransitionEraIndex(LocalGregorianCalendar.Date date) {
   2215         int eraIndex = getEraIndex(date);
   2216         CalendarDate transitionDate = eras[eraIndex].getSinceDate();
   2217         if (transitionDate.getYear() == date.getNormalizedYear() &&
   2218             transitionDate.getMonth() == date.getMonth()) {
   2219             return eraIndex;
   2220         }
   2221         if (eraIndex < eras.length - 1) {
   2222             transitionDate = eras[++eraIndex].getSinceDate();
   2223             if (transitionDate.getYear() == date.getNormalizedYear() &&
   2224                 transitionDate.getMonth() == date.getMonth()) {
   2225                 return eraIndex;
   2226             }
   2227         }
   2228         return -1;
   2229     }
   2230 
   2231     private boolean isTransitionYear(int normalizedYear) {
   2232         for (int i = eras.length - 1; i > 0; i--) {
   2233             int transitionYear = eras[i].getSinceDate().getYear();
   2234             if (normalizedYear == transitionYear) {
   2235                 return true;
   2236             }
   2237             if (normalizedYear > transitionYear) {
   2238                 break;
   2239             }
   2240         }
   2241         return false;
   2242     }
   2243 
   2244     private static int getEraIndex(LocalGregorianCalendar.Date date) {
   2245         Era era = date.getEra();
   2246         for (int i = eras.length - 1; i > 0; i--) {
   2247             if (eras[i] == era) {
   2248                 return i;
   2249             }
   2250         }
   2251         return 0;
   2252     }
   2253 
   2254     /**
   2255      * Returns this object if it's normalized (all fields and time are
   2256      * in sync). Otherwise, a cloned object is returned after calling
   2257      * complete() in lenient mode.
   2258      */
   2259     private JapaneseImperialCalendar getNormalizedCalendar() {
   2260         JapaneseImperialCalendar jc;
   2261         if (isFullyNormalized()) {
   2262             jc = this;
   2263         } else {
   2264             // Create a clone and normalize the calendar fields
   2265             jc = (JapaneseImperialCalendar) this.clone();
   2266             jc.setLenient(true);
   2267             jc.complete();
   2268         }
   2269         return jc;
   2270     }
   2271 
   2272     /**
   2273      * After adjustments such as add(MONTH), add(YEAR), we don't want the
   2274      * month to jump around.  E.g., we don't want Jan 31 + 1 month to go to Mar
   2275      * 3, we want it to go to Feb 28.  Adjustments which might run into this
   2276      * problem call this method to retain the proper month.
   2277      */
   2278     private void pinDayOfMonth(LocalGregorianCalendar.Date date) {
   2279         int year = date.getYear();
   2280         int dom = date.getDayOfMonth();
   2281         if (year != getMinimum(YEAR)) {
   2282             date.setDayOfMonth(1);
   2283             jcal.normalize(date);
   2284             int monthLength = jcal.getMonthLength(date);
   2285             if (dom > monthLength) {
   2286                 date.setDayOfMonth(monthLength);
   2287             } else {
   2288                 date.setDayOfMonth(dom);
   2289             }
   2290             jcal.normalize(date);
   2291         } else {
   2292             LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
   2293             LocalGregorianCalendar.Date realDate = jcal.getCalendarDate(time, getZone());
   2294             long tod = realDate.getTimeOfDay();
   2295             // Use an equivalent year.
   2296             realDate.addYear(+400);
   2297             realDate.setMonth(date.getMonth());
   2298             realDate.setDayOfMonth(1);
   2299             jcal.normalize(realDate);
   2300             int monthLength = jcal.getMonthLength(realDate);
   2301             if (dom > monthLength) {
   2302                 realDate.setDayOfMonth(monthLength);
   2303             } else {
   2304                 if (dom < d.getDayOfMonth()) {
   2305                     realDate.setDayOfMonth(d.getDayOfMonth());
   2306                 } else {
   2307                     realDate.setDayOfMonth(dom);
   2308                 }
   2309             }
   2310             if (realDate.getDayOfMonth() == d.getDayOfMonth() && tod < d.getTimeOfDay()) {
   2311                 realDate.setDayOfMonth(Math.min(dom + 1, monthLength));
   2312             }
   2313             // restore the year.
   2314             date.setDate(year, realDate.getMonth(), realDate.getDayOfMonth());
   2315             // Don't normalize date here so as not to cause underflow.
   2316         }
   2317     }
   2318 
   2319     /**
   2320      * Returns the new value after 'roll'ing the specified value and amount.
   2321      */
   2322     private static int getRolledValue(int value, int amount, int min, int max) {
   2323         assert value >= min && value <= max;
   2324         int range = max - min + 1;
   2325         amount %= range;
   2326         int n = value + amount;
   2327         if (n > max) {
   2328             n -= range;
   2329         } else if (n < min) {
   2330             n += range;
   2331         }
   2332         assert n >= min && n <= max;
   2333         return n;
   2334     }
   2335 
   2336     /**
   2337      * Returns the ERA.  We need a special method for this because the
   2338      * default ERA is the current era, but a zero (unset) ERA means before Meiji.
   2339      */
   2340     private int internalGetEra() {
   2341         return isSet(ERA) ? internalGet(ERA) : eras.length - 1;
   2342     }
   2343 
   2344     /**
   2345      * Updates internal state.
   2346      */
   2347     private void readObject(ObjectInputStream stream)
   2348             throws IOException, ClassNotFoundException {
   2349         stream.defaultReadObject();
   2350         if (jdate == null) {
   2351             jdate = jcal.newCalendarDate(getZone());
   2352             cachedFixedDate = Long.MIN_VALUE;
   2353         }
   2354     }
   2355 }
   2356