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