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