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