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