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

Save This Page
Home » openjdk-7 » java » util » [javadoc | source]