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