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