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