JapaneseImperialCalendar.java revision 12745:f068a4ffddd2
1/*
2 * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
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 = size; i < eras.length; i++) {
1041                        Era era = eras[i];
1042                        if (baseStyle == ALL_STYLES || baseStyle == SHORT
1043                                || baseStyle == NARROW_FORMAT) {
1044                            names.put(era.getAbbreviation(), i);
1045                        }
1046                        if (baseStyle == ALL_STYLES || baseStyle == LONG) {
1047                            names.put(era.getName(), i);
1048                        }
1049                    }
1050                }
1051            }
1052        }
1053        return names;
1054    }
1055
1056    /**
1057     * Returns the minimum value for the given calendar field of this
1058     * {@code Calendar} instance. The minimum value is
1059     * defined as the smallest value returned by the
1060     * {@link Calendar#get(int) get} method for any possible time value,
1061     * taking into consideration the current values of the
1062     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1063     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1064     * and {@link Calendar#getTimeZone() getTimeZone} methods.
1065     *
1066     * @param field the calendar field.
1067     * @return the minimum value for the given calendar field.
1068     * @see #getMaximum(int)
1069     * @see #getGreatestMinimum(int)
1070     * @see #getLeastMaximum(int)
1071     * @see #getActualMinimum(int)
1072     * @see #getActualMaximum(int)
1073     */
1074    public int getMinimum(int field) {
1075        return MIN_VALUES[field];
1076    }
1077
1078    /**
1079     * Returns the maximum value for the given calendar field of this
1080     * {@code GregorianCalendar} instance. The maximum value is
1081     * defined as the largest value returned by the
1082     * {@link Calendar#get(int) get} method for any possible time value,
1083     * taking into consideration the current values of the
1084     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1085     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1086     * and {@link Calendar#getTimeZone() getTimeZone} methods.
1087     *
1088     * @param field the calendar field.
1089     * @return the maximum value for the given calendar field.
1090     * @see #getMinimum(int)
1091     * @see #getGreatestMinimum(int)
1092     * @see #getLeastMaximum(int)
1093     * @see #getActualMinimum(int)
1094     * @see #getActualMaximum(int)
1095     */
1096    public int getMaximum(int field) {
1097        switch (field) {
1098        case YEAR:
1099            {
1100                // The value should depend on the time zone of this calendar.
1101                LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
1102                                                                     getZone());
1103                return Math.max(LEAST_MAX_VALUES[YEAR], d.getYear());
1104            }
1105        }
1106        return MAX_VALUES[field];
1107    }
1108
1109    /**
1110     * Returns the highest minimum value for the given calendar field
1111     * of this {@code GregorianCalendar} instance. The highest
1112     * minimum value is defined as the largest value returned by
1113     * {@link #getActualMinimum(int)} for any possible time value,
1114     * taking into consideration the current values of the
1115     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1116     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1117     * and {@link Calendar#getTimeZone() getTimeZone} methods.
1118     *
1119     * @param field the calendar field.
1120     * @return the highest minimum value for the given calendar field.
1121     * @see #getMinimum(int)
1122     * @see #getMaximum(int)
1123     * @see #getLeastMaximum(int)
1124     * @see #getActualMinimum(int)
1125     * @see #getActualMaximum(int)
1126     */
1127    public int getGreatestMinimum(int field) {
1128        return field == YEAR ? 1 : MIN_VALUES[field];
1129    }
1130
1131    /**
1132     * Returns the lowest maximum value for the given calendar field
1133     * of this {@code GregorianCalendar} instance. The lowest
1134     * maximum value is defined as the smallest value returned by
1135     * {@link #getActualMaximum(int)} for any possible time value,
1136     * taking into consideration the current values of the
1137     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1138     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1139     * and {@link Calendar#getTimeZone() getTimeZone} methods.
1140     *
1141     * @param field the calendar field
1142     * @return the lowest maximum value for the given calendar field.
1143     * @see #getMinimum(int)
1144     * @see #getMaximum(int)
1145     * @see #getGreatestMinimum(int)
1146     * @see #getActualMinimum(int)
1147     * @see #getActualMaximum(int)
1148     */
1149    public int getLeastMaximum(int field) {
1150        switch (field) {
1151        case YEAR:
1152            {
1153                return Math.min(LEAST_MAX_VALUES[YEAR], getMaximum(YEAR));
1154            }
1155        }
1156        return LEAST_MAX_VALUES[field];
1157    }
1158
1159    /**
1160     * Returns the minimum value that this calendar field could have,
1161     * taking into consideration the given time value and the current
1162     * values of the
1163     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1164     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1165     * and {@link Calendar#getTimeZone() getTimeZone} methods.
1166     *
1167     * @param field the calendar field
1168     * @return the minimum of the given field for the time value of
1169     * this {@code JapaneseImperialCalendar}
1170     * @see #getMinimum(int)
1171     * @see #getMaximum(int)
1172     * @see #getGreatestMinimum(int)
1173     * @see #getLeastMaximum(int)
1174     * @see #getActualMaximum(int)
1175     */
1176    public int getActualMinimum(int field) {
1177        if (!isFieldSet(YEAR_MASK|MONTH_MASK|WEEK_OF_YEAR_MASK, field)) {
1178            return getMinimum(field);
1179        }
1180
1181        int value = 0;
1182        JapaneseImperialCalendar jc = getNormalizedCalendar();
1183        // Get a local date which includes time of day and time zone,
1184        // which are missing in jc.jdate.
1185        LocalGregorianCalendar.Date jd = jcal.getCalendarDate(jc.getTimeInMillis(),
1186                                                              getZone());
1187        int eraIndex = getEraIndex(jd);
1188        switch (field) {
1189        case YEAR:
1190            {
1191                if (eraIndex > BEFORE_MEIJI) {
1192                    value = 1;
1193                    long since = eras[eraIndex].getSince(getZone());
1194                    CalendarDate d = jcal.getCalendarDate(since, getZone());
1195                    // Use the same year in jd to take care of leap
1196                    // years. i.e., both jd and d must agree on leap
1197                    // or common years.
1198                    jd.setYear(d.getYear());
1199                    jcal.normalize(jd);
1200                    assert jd.isLeapYear() == d.isLeapYear();
1201                    if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
1202                        value++;
1203                    }
1204                } else {
1205                    value = getMinimum(field);
1206                    CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1207                    // Use an equvalent year of d.getYear() if
1208                    // possible. Otherwise, ignore the leap year and
1209                    // common year difference.
1210                    int y = d.getYear();
1211                    if (y > 400) {
1212                        y -= 400;
1213                    }
1214                    jd.setYear(y);
1215                    jcal.normalize(jd);
1216                    if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
1217                        value++;
1218                    }
1219                }
1220            }
1221            break;
1222
1223        case MONTH:
1224            {
1225                // In Before Meiji and Meiji, January is the first month.
1226                if (eraIndex > MEIJI && jd.getYear() == 1) {
1227                    long since = eras[eraIndex].getSince(getZone());
1228                    CalendarDate d = jcal.getCalendarDate(since, getZone());
1229                    value = d.getMonth() - 1;
1230                    if (jd.getDayOfMonth() < d.getDayOfMonth()) {
1231                        value++;
1232                    }
1233                }
1234            }
1235            break;
1236
1237        case WEEK_OF_YEAR:
1238            {
1239                value = 1;
1240                CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1241                // shift 400 years to avoid underflow
1242                d.addYear(+400);
1243                jcal.normalize(d);
1244                jd.setEra(d.getEra());
1245                jd.setYear(d.getYear());
1246                jcal.normalize(jd);
1247
1248                long jan1 = jcal.getFixedDate(d);
1249                long fd = jcal.getFixedDate(jd);
1250                int woy = getWeekNumber(jan1, fd);
1251                long day1 = fd - (7 * (woy - 1));
1252                if ((day1 < jan1) ||
1253                    (day1 == jan1 &&
1254                     jd.getTimeOfDay() < d.getTimeOfDay())) {
1255                    value++;
1256                }
1257            }
1258            break;
1259        }
1260        return value;
1261    }
1262
1263    /**
1264     * Returns the maximum value that this calendar field could have,
1265     * taking into consideration the given time value and the current
1266     * values of the
1267     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1268     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1269     * and
1270     * {@link Calendar#getTimeZone() getTimeZone} methods.
1271     * For example, if the date of this instance is Heisei 16February 1,
1272     * the actual maximum value of the {@code DAY_OF_MONTH} field
1273     * is 29 because Heisei 16 is a leap year, and if the date of this
1274     * instance is Heisei 17 February 1, it's 28.
1275     *
1276     * @param field the calendar field
1277     * @return the maximum of the given field for the time value of
1278     * this {@code JapaneseImperialCalendar}
1279     * @see #getMinimum(int)
1280     * @see #getMaximum(int)
1281     * @see #getGreatestMinimum(int)
1282     * @see #getLeastMaximum(int)
1283     * @see #getActualMinimum(int)
1284     */
1285    public int getActualMaximum(int field) {
1286        final int fieldsForFixedMax = ERA_MASK|DAY_OF_WEEK_MASK|HOUR_MASK|AM_PM_MASK|
1287            HOUR_OF_DAY_MASK|MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK|
1288            ZONE_OFFSET_MASK|DST_OFFSET_MASK;
1289        if ((fieldsForFixedMax & (1<<field)) != 0) {
1290            return getMaximum(field);
1291        }
1292
1293        JapaneseImperialCalendar jc = getNormalizedCalendar();
1294        LocalGregorianCalendar.Date date = jc.jdate;
1295        int normalizedYear = date.getNormalizedYear();
1296
1297        int value = -1;
1298        switch (field) {
1299        case MONTH:
1300            {
1301                value = DECEMBER;
1302                if (isTransitionYear(date.getNormalizedYear())) {
1303                    // TODO: there may be multiple transitions in a year.
1304                    int eraIndex = getEraIndex(date);
1305                    if (date.getYear() != 1) {
1306                        eraIndex++;
1307                        assert eraIndex < eras.length;
1308                    }
1309                    long transition = sinceFixedDates[eraIndex];
1310                    long fd = jc.cachedFixedDate;
1311                    if (fd < transition) {
1312                        LocalGregorianCalendar.Date ldate
1313                            = (LocalGregorianCalendar.Date) date.clone();
1314                        jcal.getCalendarDateFromFixedDate(ldate, transition - 1);
1315                        value = ldate.getMonth() - 1;
1316                    }
1317                } else {
1318                    LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
1319                                                                         getZone());
1320                    if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) {
1321                        value = d.getMonth() - 1;
1322                    }
1323                }
1324            }
1325            break;
1326
1327        case DAY_OF_MONTH:
1328            value = jcal.getMonthLength(date);
1329            break;
1330
1331        case DAY_OF_YEAR:
1332            {
1333                if (isTransitionYear(date.getNormalizedYear())) {
1334                    // Handle transition year.
1335                    // TODO: there may be multiple transitions in a year.
1336                    int eraIndex = getEraIndex(date);
1337                    if (date.getYear() != 1) {
1338                        eraIndex++;
1339                        assert eraIndex < eras.length;
1340                    }
1341                    long transition = sinceFixedDates[eraIndex];
1342                    long fd = jc.cachedFixedDate;
1343                    CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
1344                    d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1);
1345                    if (fd < transition) {
1346                        value = (int)(transition - gcal.getFixedDate(d));
1347                    } else {
1348                        d.addYear(+1);
1349                        value = (int)(gcal.getFixedDate(d) - transition);
1350                    }
1351                } else {
1352                    LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
1353                                                                         getZone());
1354                    if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) {
1355                        long fd = jcal.getFixedDate(d);
1356                        long jan1 = getFixedDateJan1(d, fd);
1357                        value = (int)(fd - jan1) + 1;
1358                    } else if (date.getYear() == getMinimum(YEAR)) {
1359                        CalendarDate d1 = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1360                        long fd1 = jcal.getFixedDate(d1);
1361                        d1.addYear(1);
1362                        d1.setMonth(BaseCalendar.JANUARY).setDayOfMonth(1);
1363                        jcal.normalize(d1);
1364                        long fd2 = jcal.getFixedDate(d1);
1365                        value = (int)(fd2 - fd1);
1366                    } else {
1367                        value = jcal.getYearLength(date);
1368                    }
1369                }
1370            }
1371            break;
1372
1373        case WEEK_OF_YEAR:
1374            {
1375                if (!isTransitionYear(date.getNormalizedYear())) {
1376                    LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE,
1377                                                                          getZone());
1378                    if (date.getEra() == jd.getEra() && date.getYear() == jd.getYear()) {
1379                        long fd = jcal.getFixedDate(jd);
1380                        long jan1 = getFixedDateJan1(jd, fd);
1381                        value = getWeekNumber(jan1, fd);
1382                    } else if (date.getEra() == null && date.getYear() == getMinimum(YEAR)) {
1383                        CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1384                        // shift 400 years to avoid underflow
1385                        d.addYear(+400);
1386                        jcal.normalize(d);
1387                        jd.setEra(d.getEra());
1388                        jd.setDate(d.getYear() + 1, BaseCalendar.JANUARY, 1);
1389                        jcal.normalize(jd);
1390                        long jan1 = jcal.getFixedDate(d);
1391                        long nextJan1 = jcal.getFixedDate(jd);
1392                        long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
1393                                                                                            getFirstDayOfWeek());
1394                        int ndays = (int)(nextJan1st - nextJan1);
1395                        if (ndays >= getMinimalDaysInFirstWeek()) {
1396                            nextJan1st -= 7;
1397                        }
1398                        value = getWeekNumber(jan1, nextJan1st);
1399                    } else {
1400                        // Get the day of week of January 1 of the year
1401                        CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
1402                        d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1);
1403                        int dayOfWeek = gcal.getDayOfWeek(d);
1404                        // Normalize the day of week with the firstDayOfWeek value
1405                        dayOfWeek -= getFirstDayOfWeek();
1406                        if (dayOfWeek < 0) {
1407                            dayOfWeek += 7;
1408                        }
1409                        value = 52;
1410                        int magic = dayOfWeek + getMinimalDaysInFirstWeek() - 1;
1411                        if ((magic == 6) ||
1412                            (date.isLeapYear() && (magic == 5 || magic == 12))) {
1413                            value++;
1414                        }
1415                    }
1416                    break;
1417                }
1418
1419                if (jc == this) {
1420                    jc = (JapaneseImperialCalendar) jc.clone();
1421                }
1422                int max = getActualMaximum(DAY_OF_YEAR);
1423                jc.set(DAY_OF_YEAR, max);
1424                value = jc.get(WEEK_OF_YEAR);
1425                if (value == 1 && max > 7) {
1426                    jc.add(WEEK_OF_YEAR, -1);
1427                    value = jc.get(WEEK_OF_YEAR);
1428                }
1429            }
1430            break;
1431
1432        case WEEK_OF_MONTH:
1433            {
1434                LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE,
1435                                                                      getZone());
1436                if (!(date.getEra() == jd.getEra() && date.getYear() == jd.getYear())) {
1437                    CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
1438                    d.setDate(date.getNormalizedYear(), date.getMonth(), 1);
1439                    int dayOfWeek = gcal.getDayOfWeek(d);
1440                    int monthLength = gcal.getMonthLength(d);
1441                    dayOfWeek -= getFirstDayOfWeek();
1442                    if (dayOfWeek < 0) {
1443                        dayOfWeek += 7;
1444                    }
1445                    int nDaysFirstWeek = 7 - dayOfWeek; // # of days in the first week
1446                    value = 3;
1447                    if (nDaysFirstWeek >= getMinimalDaysInFirstWeek()) {
1448                        value++;
1449                    }
1450                    monthLength -= nDaysFirstWeek + 7 * 3;
1451                    if (monthLength > 0) {
1452                        value++;
1453                        if (monthLength > 7) {
1454                            value++;
1455                        }
1456                    }
1457                } else {
1458                    long fd = jcal.getFixedDate(jd);
1459                    long month1 = fd - jd.getDayOfMonth() + 1;
1460                    value = getWeekNumber(month1, fd);
1461                }
1462            }
1463            break;
1464
1465        case DAY_OF_WEEK_IN_MONTH:
1466            {
1467                int ndays, dow1;
1468                int dow = date.getDayOfWeek();
1469                BaseCalendar.Date d = (BaseCalendar.Date) date.clone();
1470                ndays = jcal.getMonthLength(d);
1471                d.setDayOfMonth(1);
1472                jcal.normalize(d);
1473                dow1 = d.getDayOfWeek();
1474                int x = dow - dow1;
1475                if (x < 0) {
1476                    x += 7;
1477                }
1478                ndays -= x;
1479                value = (ndays + 6) / 7;
1480            }
1481            break;
1482
1483        case YEAR:
1484            {
1485                CalendarDate jd = jcal.getCalendarDate(jc.getTimeInMillis(), getZone());
1486                CalendarDate d;
1487                int eraIndex = getEraIndex(date);
1488                if (eraIndex == eras.length - 1) {
1489                    d = jcal.getCalendarDate(Long.MAX_VALUE, getZone());
1490                    value = d.getYear();
1491                    // Use an equivalent year for the
1492                    // getYearOffsetInMillis call to avoid overflow.
1493                    if (value > 400) {
1494                        jd.setYear(value - 400);
1495                    }
1496                } else {
1497                    d = jcal.getCalendarDate(eras[eraIndex + 1].getSince(getZone()) - 1,
1498                                             getZone());
1499                    value = d.getYear();
1500                    // Use the same year as d.getYear() to be
1501                    // consistent with leap and common years.
1502                    jd.setYear(value);
1503                }
1504                jcal.normalize(jd);
1505                if (getYearOffsetInMillis(jd) > getYearOffsetInMillis(d)) {
1506                    value--;
1507                }
1508            }
1509            break;
1510
1511        default:
1512            throw new ArrayIndexOutOfBoundsException(field);
1513        }
1514        return value;
1515    }
1516
1517    /**
1518     * Returns the millisecond offset from the beginning of the
1519     * year. In the year for Long.MIN_VALUE, it's a pseudo value
1520     * beyond the limit. The given CalendarDate object must have been
1521     * normalized before calling this method.
1522     */
1523    private long getYearOffsetInMillis(CalendarDate date) {
1524        long t = (jcal.getDayOfYear(date) - 1) * ONE_DAY;
1525        return t + date.getTimeOfDay() - date.getZoneOffset();
1526    }
1527
1528    public Object clone() {
1529        JapaneseImperialCalendar other = (JapaneseImperialCalendar) super.clone();
1530
1531        other.jdate = (LocalGregorianCalendar.Date) jdate.clone();
1532        other.originalFields = null;
1533        other.zoneOffsets = null;
1534        return other;
1535    }
1536
1537    public TimeZone getTimeZone() {
1538        TimeZone zone = super.getTimeZone();
1539        // To share the zone by the CalendarDate
1540        jdate.setZone(zone);
1541        return zone;
1542    }
1543
1544    public void setTimeZone(TimeZone zone) {
1545        super.setTimeZone(zone);
1546        // To share the zone by the CalendarDate
1547        jdate.setZone(zone);
1548    }
1549
1550    /**
1551     * The fixed date corresponding to jdate. If the value is
1552     * Long.MIN_VALUE, the fixed date value is unknown.
1553     */
1554    private transient long cachedFixedDate = Long.MIN_VALUE;
1555
1556    /**
1557     * Converts the time value (millisecond offset from the <a
1558     * href="Calendar.html#Epoch">Epoch</a>) to calendar field values.
1559     * The time is <em>not</em>
1560     * recomputed first; to recompute the time, then the fields, call the
1561     * {@code complete} method.
1562     *
1563     * @see Calendar#complete
1564     */
1565    protected void computeFields() {
1566        int mask = 0;
1567        if (isPartiallyNormalized()) {
1568            // Determine which calendar fields need to be computed.
1569            mask = getSetStateFields();
1570            int fieldMask = ~mask & ALL_FIELDS;
1571            if (fieldMask != 0 || cachedFixedDate == Long.MIN_VALUE) {
1572                mask |= computeFields(fieldMask,
1573                                      mask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK));
1574                assert mask == ALL_FIELDS;
1575            }
1576        } else {
1577            // Specify all fields
1578            mask = ALL_FIELDS;
1579            computeFields(mask, 0);
1580        }
1581        // After computing all the fields, set the field state to `COMPUTED'.
1582        setFieldsComputed(mask);
1583    }
1584
1585    /**
1586     * This computeFields implements the conversion from UTC
1587     * (millisecond offset from the Epoch) to calendar
1588     * field values. fieldMask specifies which fields to change the
1589     * setting state to COMPUTED, although all fields are set to
1590     * the correct values. This is required to fix 4685354.
1591     *
1592     * @param fieldMask a bit mask to specify which fields to change
1593     * the setting state.
1594     * @param tzMask a bit mask to specify which time zone offset
1595     * fields to be used for time calculations
1596     * @return a new field mask that indicates what field values have
1597     * actually been set.
1598     */
1599    private int computeFields(int fieldMask, int tzMask) {
1600        int zoneOffset = 0;
1601        TimeZone tz = getZone();
1602        if (zoneOffsets == null) {
1603            zoneOffsets = new int[2];
1604        }
1605        if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
1606            if (tz instanceof ZoneInfo) {
1607                zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
1608            } else {
1609                zoneOffset = tz.getOffset(time);
1610                zoneOffsets[0] = tz.getRawOffset();
1611                zoneOffsets[1] = zoneOffset - zoneOffsets[0];
1612            }
1613        }
1614        if (tzMask != 0) {
1615            if (isFieldSet(tzMask, ZONE_OFFSET)) {
1616                zoneOffsets[0] = internalGet(ZONE_OFFSET);
1617            }
1618            if (isFieldSet(tzMask, DST_OFFSET)) {
1619                zoneOffsets[1] = internalGet(DST_OFFSET);
1620            }
1621            zoneOffset = zoneOffsets[0] + zoneOffsets[1];
1622        }
1623
1624        // By computing time and zoneOffset separately, we can take
1625        // the wider range of time+zoneOffset than the previous
1626        // implementation.
1627        long fixedDate = zoneOffset / ONE_DAY;
1628        int timeOfDay = zoneOffset % (int)ONE_DAY;
1629        fixedDate += time / ONE_DAY;
1630        timeOfDay += (int) (time % ONE_DAY);
1631        if (timeOfDay >= ONE_DAY) {
1632            timeOfDay -= ONE_DAY;
1633            ++fixedDate;
1634        } else {
1635            while (timeOfDay < 0) {
1636                timeOfDay += ONE_DAY;
1637                --fixedDate;
1638            }
1639        }
1640        fixedDate += EPOCH_OFFSET;
1641
1642        // See if we can use jdate to avoid date calculation.
1643        if (fixedDate != cachedFixedDate || fixedDate < 0) {
1644            jcal.getCalendarDateFromFixedDate(jdate, fixedDate);
1645            cachedFixedDate = fixedDate;
1646        }
1647        int era = getEraIndex(jdate);
1648        int year = jdate.getYear();
1649
1650        // Always set the ERA and YEAR values.
1651        internalSet(ERA, era);
1652        internalSet(YEAR, year);
1653        int mask = fieldMask | (ERA_MASK|YEAR_MASK);
1654
1655        int month =  jdate.getMonth() - 1; // 0-based
1656        int dayOfMonth = jdate.getDayOfMonth();
1657
1658        // Set the basic date fields.
1659        if ((fieldMask & (MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK))
1660            != 0) {
1661            internalSet(MONTH, month);
1662            internalSet(DAY_OF_MONTH, dayOfMonth);
1663            internalSet(DAY_OF_WEEK, jdate.getDayOfWeek());
1664            mask |= MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK;
1665        }
1666
1667        if ((fieldMask & (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
1668                          |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK)) != 0) {
1669            if (timeOfDay != 0) {
1670                int hours = timeOfDay / ONE_HOUR;
1671                internalSet(HOUR_OF_DAY, hours);
1672                internalSet(AM_PM, hours / 12); // Assume AM == 0
1673                internalSet(HOUR, hours % 12);
1674                int r = timeOfDay % ONE_HOUR;
1675                internalSet(MINUTE, r / ONE_MINUTE);
1676                r %= ONE_MINUTE;
1677                internalSet(SECOND, r / ONE_SECOND);
1678                internalSet(MILLISECOND, r % ONE_SECOND);
1679            } else {
1680                internalSet(HOUR_OF_DAY, 0);
1681                internalSet(AM_PM, AM);
1682                internalSet(HOUR, 0);
1683                internalSet(MINUTE, 0);
1684                internalSet(SECOND, 0);
1685                internalSet(MILLISECOND, 0);
1686            }
1687            mask |= (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
1688                     |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK);
1689        }
1690
1691        if ((fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) != 0) {
1692            internalSet(ZONE_OFFSET, zoneOffsets[0]);
1693            internalSet(DST_OFFSET, zoneOffsets[1]);
1694            mask |= (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
1695        }
1696
1697        if ((fieldMask & (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK
1698                          |WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK)) != 0) {
1699            int normalizedYear = jdate.getNormalizedYear();
1700            // If it's a year of an era transition, we need to handle
1701            // irregular year boundaries.
1702            boolean transitionYear = isTransitionYear(jdate.getNormalizedYear());
1703            int dayOfYear;
1704            long fixedDateJan1;
1705            if (transitionYear) {
1706                fixedDateJan1 = getFixedDateJan1(jdate, fixedDate);
1707                dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
1708            } else if (normalizedYear == MIN_VALUES[YEAR]) {
1709                CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1710                fixedDateJan1 = jcal.getFixedDate(dx);
1711                dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
1712            } else {
1713                dayOfYear = (int) jcal.getDayOfYear(jdate);
1714                fixedDateJan1 = fixedDate - dayOfYear + 1;
1715            }
1716            long fixedDateMonth1 = transitionYear ?
1717                getFixedDateMonth1(jdate, fixedDate) : fixedDate - dayOfMonth + 1;
1718
1719            internalSet(DAY_OF_YEAR, dayOfYear);
1720            internalSet(DAY_OF_WEEK_IN_MONTH, (dayOfMonth - 1) / 7 + 1);
1721
1722            int weekOfYear = getWeekNumber(fixedDateJan1, fixedDate);
1723
1724            // The spec is to calculate WEEK_OF_YEAR in the
1725            // ISO8601-style. This creates problems, though.
1726            if (weekOfYear == 0) {
1727                // If the date belongs to the last week of the
1728                // previous year, use the week number of "12/31" of
1729                // the "previous" year. Again, if the previous year is
1730                // a transition year, we need to take care of it.
1731                // Usually the previous day of the first day of a year
1732                // is December 31, which is not always true in the
1733                // Japanese imperial calendar system.
1734                long fixedDec31 = fixedDateJan1 - 1;
1735                long prevJan1;
1736                LocalGregorianCalendar.Date d = getCalendarDate(fixedDec31);
1737                if (!(transitionYear || isTransitionYear(d.getNormalizedYear()))) {
1738                    prevJan1 = fixedDateJan1 - 365;
1739                    if (d.isLeapYear()) {
1740                        --prevJan1;
1741                    }
1742                } else if (transitionYear) {
1743                    if (jdate.getYear() == 1) {
1744                        // As of Heisei (since Meiji) there's no case
1745                        // that there are multiple transitions in a
1746                        // year.  Historically there was such
1747                        // case. There might be such case again in the
1748                        // future.
1749                        if (era > HEISEI) {
1750                            CalendarDate pd = eras[era - 1].getSinceDate();
1751                            if (normalizedYear == pd.getYear()) {
1752                                d.setMonth(pd.getMonth()).setDayOfMonth(pd.getDayOfMonth());
1753                            }
1754                        } else {
1755                            d.setMonth(LocalGregorianCalendar.JANUARY).setDayOfMonth(1);
1756                        }
1757                        jcal.normalize(d);
1758                        prevJan1 = jcal.getFixedDate(d);
1759                    } else {
1760                        prevJan1 = fixedDateJan1 - 365;
1761                        if (d.isLeapYear()) {
1762                            --prevJan1;
1763                        }
1764                    }
1765                } else {
1766                    CalendarDate cd = eras[getEraIndex(jdate)].getSinceDate();
1767                    d.setMonth(cd.getMonth()).setDayOfMonth(cd.getDayOfMonth());
1768                    jcal.normalize(d);
1769                    prevJan1 = jcal.getFixedDate(d);
1770                }
1771                weekOfYear = getWeekNumber(prevJan1, fixedDec31);
1772            } else {
1773                if (!transitionYear) {
1774                    // Regular years
1775                    if (weekOfYear >= 52) {
1776                        long nextJan1 = fixedDateJan1 + 365;
1777                        if (jdate.isLeapYear()) {
1778                            nextJan1++;
1779                        }
1780                        long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
1781                                                                                            getFirstDayOfWeek());
1782                        int ndays = (int)(nextJan1st - nextJan1);
1783                        if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
1784                            // The first days forms a week in which the date is included.
1785                            weekOfYear = 1;
1786                        }
1787                    }
1788                } else {
1789                    LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
1790                    long nextJan1;
1791                    if (jdate.getYear() == 1) {
1792                        d.addYear(+1);
1793                        d.setMonth(LocalGregorianCalendar.JANUARY).setDayOfMonth(1);
1794                        nextJan1 = jcal.getFixedDate(d);
1795                    } else {
1796                        int nextEraIndex = getEraIndex(d) + 1;
1797                        CalendarDate cd = eras[nextEraIndex].getSinceDate();
1798                        d.setEra(eras[nextEraIndex]);
1799                        d.setDate(1, cd.getMonth(), cd.getDayOfMonth());
1800                        jcal.normalize(d);
1801                        nextJan1 = jcal.getFixedDate(d);
1802                    }
1803                    long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
1804                                                                                        getFirstDayOfWeek());
1805                    int ndays = (int)(nextJan1st - nextJan1);
1806                    if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
1807                        // The first days forms a week in which the date is included.
1808                        weekOfYear = 1;
1809                    }
1810                }
1811            }
1812            internalSet(WEEK_OF_YEAR, weekOfYear);
1813            internalSet(WEEK_OF_MONTH, getWeekNumber(fixedDateMonth1, fixedDate));
1814            mask |= (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK|WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK);
1815        }
1816        return mask;
1817    }
1818
1819    /**
1820     * Returns the number of weeks in a period between fixedDay1 and
1821     * fixedDate. The getFirstDayOfWeek-getMinimalDaysInFirstWeek rule
1822     * is applied to calculate the number of weeks.
1823     *
1824     * @param fixedDay1 the fixed date of the first day of the period
1825     * @param fixedDate the fixed date of the last day of the period
1826     * @return the number of weeks of the given period
1827     */
1828    private int getWeekNumber(long fixedDay1, long fixedDate) {
1829        // We can always use `jcal' since Julian and Gregorian are the
1830        // same thing for this calculation.
1831        long fixedDay1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDay1 + 6,
1832                                                                             getFirstDayOfWeek());
1833        int ndays = (int)(fixedDay1st - fixedDay1);
1834        assert ndays <= 7;
1835        if (ndays >= getMinimalDaysInFirstWeek()) {
1836            fixedDay1st -= 7;
1837        }
1838        int normalizedDayOfPeriod = (int)(fixedDate - fixedDay1st);
1839        if (normalizedDayOfPeriod >= 0) {
1840            return normalizedDayOfPeriod / 7 + 1;
1841        }
1842        return CalendarUtils.floorDivide(normalizedDayOfPeriod, 7) + 1;
1843    }
1844
1845    /**
1846     * Converts calendar field values to the time value (millisecond
1847     * offset from the <a href="Calendar.html#Epoch">Epoch</a>).
1848     *
1849     * @exception IllegalArgumentException if any calendar fields are invalid.
1850     */
1851    protected void computeTime() {
1852        // In non-lenient mode, perform brief checking of calendar
1853        // fields which have been set externally. Through this
1854        // checking, the field values are stored in originalFields[]
1855        // to see if any of them are normalized later.
1856        if (!isLenient()) {
1857            if (originalFields == null) {
1858                originalFields = new int[FIELD_COUNT];
1859            }
1860            for (int field = 0; field < FIELD_COUNT; field++) {
1861                int value = internalGet(field);
1862                if (isExternallySet(field)) {
1863                    // Quick validation for any out of range values
1864                    if (value < getMinimum(field) || value > getMaximum(field)) {
1865                        throw new IllegalArgumentException(getFieldName(field));
1866                    }
1867                }
1868                originalFields[field] = value;
1869            }
1870        }
1871
1872        // Let the super class determine which calendar fields to be
1873        // used to calculate the time.
1874        int fieldMask = selectFields();
1875
1876        int year;
1877        int era;
1878
1879        if (isSet(ERA)) {
1880            era = internalGet(ERA);
1881            year = isSet(YEAR) ? internalGet(YEAR) : 1;
1882        } else {
1883            if (isSet(YEAR)) {
1884                era = eras.length - 1;
1885                year = internalGet(YEAR);
1886            } else {
1887                // Equivalent to 1970 (Gregorian)
1888                era = SHOWA;
1889                year = 45;
1890            }
1891        }
1892
1893        // Calculate the time of day. We rely on the convention that
1894        // an UNSET field has 0.
1895        long timeOfDay = 0;
1896        if (isFieldSet(fieldMask, HOUR_OF_DAY)) {
1897            timeOfDay += (long) internalGet(HOUR_OF_DAY);
1898        } else {
1899            timeOfDay += internalGet(HOUR);
1900            // The default value of AM_PM is 0 which designates AM.
1901            if (isFieldSet(fieldMask, AM_PM)) {
1902                timeOfDay += 12 * internalGet(AM_PM);
1903            }
1904        }
1905        timeOfDay *= 60;
1906        timeOfDay += internalGet(MINUTE);
1907        timeOfDay *= 60;
1908        timeOfDay += internalGet(SECOND);
1909        timeOfDay *= 1000;
1910        timeOfDay += internalGet(MILLISECOND);
1911
1912        // Convert the time of day to the number of days and the
1913        // millisecond offset from midnight.
1914        long fixedDate = timeOfDay / ONE_DAY;
1915        timeOfDay %= ONE_DAY;
1916        while (timeOfDay < 0) {
1917            timeOfDay += ONE_DAY;
1918            --fixedDate;
1919        }
1920
1921        // Calculate the fixed date since January 1, 1 (Gregorian).
1922        fixedDate += getFixedDate(era, year, fieldMask);
1923
1924        // millis represents local wall-clock time in milliseconds.
1925        long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
1926
1927        // Compute the time zone offset and DST offset.  There are two potential
1928        // ambiguities here.  We'll assume a 2:00 am (wall time) switchover time
1929        // for discussion purposes here.
1930        // 1. The transition into DST.  Here, a designated time of 2:00 am - 2:59 am
1931        //    can be in standard or in DST depending.  However, 2:00 am is an invalid
1932        //    representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
1933        //    We assume standard time.
1934        // 2. The transition out of DST.  Here, a designated time of 1:00 am - 1:59 am
1935        //    can be in standard or DST.  Both are valid representations (the rep
1936        //    jumps from 1:59:59 DST to 1:00:00 Std).
1937        //    Again, we assume standard time.
1938        // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
1939        // or DST_OFFSET fields; then we use those fields.
1940        TimeZone zone = getZone();
1941        if (zoneOffsets == null) {
1942            zoneOffsets = new int[2];
1943        }
1944        int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
1945        if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
1946            if (zone instanceof ZoneInfo) {
1947                ((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets);
1948            } else {
1949                zone.getOffsets(millis - zone.getRawOffset(), zoneOffsets);
1950            }
1951        }
1952        if (tzMask != 0) {
1953            if (isFieldSet(tzMask, ZONE_OFFSET)) {
1954                zoneOffsets[0] = internalGet(ZONE_OFFSET);
1955            }
1956            if (isFieldSet(tzMask, DST_OFFSET)) {
1957                zoneOffsets[1] = internalGet(DST_OFFSET);
1958            }
1959        }
1960
1961        // Adjust the time zone offset values to get the UTC time.
1962        millis -= zoneOffsets[0] + zoneOffsets[1];
1963
1964        // Set this calendar's time in milliseconds
1965        time = millis;
1966
1967        int mask = computeFields(fieldMask | getSetStateFields(), tzMask);
1968
1969        if (!isLenient()) {
1970            for (int field = 0; field < FIELD_COUNT; field++) {
1971                if (!isExternallySet(field)) {
1972                    continue;
1973                }
1974                if (originalFields[field] != internalGet(field)) {
1975                    int wrongValue = internalGet(field);
1976                    // Restore the original field values
1977                    System.arraycopy(originalFields, 0, fields, 0, fields.length);
1978                    throw new IllegalArgumentException(getFieldName(field) + "=" + wrongValue
1979                                                       + ", expected " + originalFields[field]);
1980                }
1981            }
1982        }
1983        setFieldsNormalized(mask);
1984    }
1985
1986    /**
1987     * Computes the fixed date under either the Gregorian or the
1988     * Julian calendar, using the given year and the specified calendar fields.
1989     *
1990     * @param era era index
1991     * @param year the normalized year number, with 0 indicating the
1992     * year 1 BCE, -1 indicating 2 BCE, etc.
1993     * @param fieldMask the calendar fields to be used for the date calculation
1994     * @return the fixed date
1995     * @see Calendar#selectFields
1996     */
1997    private long getFixedDate(int era, int year, int fieldMask) {
1998        int month = JANUARY;
1999        int firstDayOfMonth = 1;
2000        if (isFieldSet(fieldMask, MONTH)) {
2001            // No need to check if MONTH has been set (no isSet(MONTH)
2002            // call) since its unset value happens to be JANUARY (0).
2003            month = internalGet(MONTH);
2004
2005            // If the month is out of range, adjust it into range.
2006            if (month > DECEMBER) {
2007                year += month / 12;
2008                month %= 12;
2009            } else if (month < JANUARY) {
2010                int[] rem = new int[1];
2011                year += CalendarUtils.floorDivide(month, 12, rem);
2012                month = rem[0];
2013            }
2014        } else {
2015            if (year == 1 && era != 0) {
2016                CalendarDate d = eras[era].getSinceDate();
2017                month = d.getMonth() - 1;
2018                firstDayOfMonth = d.getDayOfMonth();
2019            }
2020        }
2021
2022        // Adjust the base date if year is the minimum value.
2023        if (year == MIN_VALUES[YEAR]) {
2024            CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
2025            int m = dx.getMonth() - 1;
2026            if (month < m) {
2027                month = m;
2028            }
2029            if (month == m) {
2030                firstDayOfMonth = dx.getDayOfMonth();
2031            }
2032        }
2033
2034        LocalGregorianCalendar.Date date = jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
2035        date.setEra(era > 0 ? eras[era] : null);
2036        date.setDate(year, month + 1, firstDayOfMonth);
2037        jcal.normalize(date);
2038
2039        // Get the fixed date since Jan 1, 1 (Gregorian). We are on
2040        // the first day of either `month' or January in 'year'.
2041        long fixedDate = jcal.getFixedDate(date);
2042
2043        if (isFieldSet(fieldMask, MONTH)) {
2044            // Month-based calculations
2045            if (isFieldSet(fieldMask, DAY_OF_MONTH)) {
2046                // We are on the "first day" of the month (which may
2047                // not be 1). Just add the offset if DAY_OF_MONTH is
2048                // set. If the isSet call returns false, that means
2049                // DAY_OF_MONTH has been selected just because of the
2050                // selected combination. We don't need to add any
2051                // since the default value is the "first day".
2052                if (isSet(DAY_OF_MONTH)) {
2053                    // To avoid underflow with DAY_OF_MONTH-firstDayOfMonth, add
2054                    // DAY_OF_MONTH, then subtract firstDayOfMonth.
2055                    fixedDate += internalGet(DAY_OF_MONTH);
2056                    fixedDate -= firstDayOfMonth;
2057                }
2058            } else {
2059                if (isFieldSet(fieldMask, WEEK_OF_MONTH)) {
2060                    long firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + 6,
2061                                                                                            getFirstDayOfWeek());
2062                    // If we have enough days in the first week, then
2063                    // move to the previous week.
2064                    if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
2065                        firstDayOfWeek -= 7;
2066                    }
2067                    if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2068                        firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
2069                                                                                           internalGet(DAY_OF_WEEK));
2070                    }
2071                    // In lenient mode, we treat days of the previous
2072                    // months as a part of the specified
2073                    // WEEK_OF_MONTH. See 4633646.
2074                    fixedDate = firstDayOfWeek + 7 * (internalGet(WEEK_OF_MONTH) - 1);
2075                } else {
2076                    int dayOfWeek;
2077                    if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2078                        dayOfWeek = internalGet(DAY_OF_WEEK);
2079                    } else {
2080                        dayOfWeek = getFirstDayOfWeek();
2081                    }
2082                    // We are basing this on the day-of-week-in-month.  The only
2083                    // trickiness occurs if the day-of-week-in-month is
2084                    // negative.
2085                    int dowim;
2086                    if (isFieldSet(fieldMask, DAY_OF_WEEK_IN_MONTH)) {
2087                        dowim = internalGet(DAY_OF_WEEK_IN_MONTH);
2088                    } else {
2089                        dowim = 1;
2090                    }
2091                    if (dowim >= 0) {
2092                        fixedDate = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + (7 * dowim) - 1,
2093                                                                                      dayOfWeek);
2094                    } else {
2095                        // Go to the first day of the next week of
2096                        // the specified week boundary.
2097                        int lastDate = monthLength(month, year) + (7 * (dowim + 1));
2098                        // Then, get the day of week date on or before the last date.
2099                        fixedDate = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + lastDate - 1,
2100                                                                                      dayOfWeek);
2101                    }
2102                }
2103            }
2104        } else {
2105            // We are on the first day of the year.
2106            if (isFieldSet(fieldMask, DAY_OF_YEAR)) {
2107                if (isTransitionYear(date.getNormalizedYear())) {
2108                    fixedDate = getFixedDateJan1(date, fixedDate);
2109                }
2110                // Add the offset, then subtract 1. (Make sure to avoid underflow.)
2111                fixedDate += internalGet(DAY_OF_YEAR);
2112                fixedDate--;
2113            } else {
2114                long firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + 6,
2115                                                                                        getFirstDayOfWeek());
2116                // If we have enough days in the first week, then move
2117                // to the previous week.
2118                if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
2119                    firstDayOfWeek -= 7;
2120                }
2121                if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2122                    int dayOfWeek = internalGet(DAY_OF_WEEK);
2123                    if (dayOfWeek != getFirstDayOfWeek()) {
2124                        firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
2125                                                                                           dayOfWeek);
2126                    }
2127                }
2128                fixedDate = firstDayOfWeek + 7 * ((long)internalGet(WEEK_OF_YEAR) - 1);
2129            }
2130        }
2131        return fixedDate;
2132    }
2133
2134    /**
2135     * Returns the fixed date of the first day of the year (usually
2136     * January 1) before the specified date.
2137     *
2138     * @param date the date for which the first day of the year is
2139     * calculated. The date has to be in the cut-over year.
2140     * @param fixedDate the fixed date representation of the date
2141     */
2142    private long getFixedDateJan1(LocalGregorianCalendar.Date date, long fixedDate) {
2143        Era era = date.getEra();
2144        if (date.getEra() != null && date.getYear() == 1) {
2145            for (int eraIndex = getEraIndex(date); eraIndex > 0; eraIndex--) {
2146                CalendarDate d = eras[eraIndex].getSinceDate();
2147                long fd = gcal.getFixedDate(d);
2148                // There might be multiple era transitions in a year.
2149                if (fd > fixedDate) {
2150                    continue;
2151                }
2152                return fd;
2153            }
2154        }
2155        CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
2156        d.setDate(date.getNormalizedYear(), Gregorian.JANUARY, 1);
2157        return gcal.getFixedDate(d);
2158    }
2159
2160    /**
2161     * Returns the fixed date of the first date of the month (usually
2162     * the 1st of the month) before the specified date.
2163     *
2164     * @param date the date for which the first day of the month is
2165     * calculated. The date must be in the era transition year.
2166     * @param fixedDate the fixed date representation of the date
2167     */
2168    private long getFixedDateMonth1(LocalGregorianCalendar.Date date,
2169                                          long fixedDate) {
2170        int eraIndex = getTransitionEraIndex(date);
2171        if (eraIndex != -1) {
2172            long transition = sinceFixedDates[eraIndex];
2173            // If the given date is on or after the transition date, then
2174            // return the transition date.
2175            if (transition <= fixedDate) {
2176                return transition;
2177            }
2178        }
2179
2180        // Otherwise, we can use the 1st day of the month.
2181        return fixedDate - date.getDayOfMonth() + 1;
2182    }
2183
2184    /**
2185     * Returns a LocalGregorianCalendar.Date produced from the specified fixed date.
2186     *
2187     * @param fd the fixed date
2188     */
2189    private static LocalGregorianCalendar.Date getCalendarDate(long fd) {
2190        LocalGregorianCalendar.Date d = jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
2191        jcal.getCalendarDateFromFixedDate(d, fd);
2192        return d;
2193    }
2194
2195    /**
2196     * Returns the length of the specified month in the specified
2197     * Gregorian year. The year number must be normalized.
2198     *
2199     * @see GregorianCalendar#isLeapYear(int)
2200     */
2201    private int monthLength(int month, int gregorianYear) {
2202        return CalendarUtils.isGregorianLeapYear(gregorianYear) ?
2203            GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month];
2204    }
2205
2206    /**
2207     * Returns the length of the specified month in the year provided
2208     * by internalGet(YEAR).
2209     *
2210     * @see GregorianCalendar#isLeapYear(int)
2211     */
2212    private int monthLength(int month) {
2213        assert jdate.isNormalized();
2214        return jdate.isLeapYear() ?
2215            GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month];
2216    }
2217
2218    private int actualMonthLength() {
2219        int length = jcal.getMonthLength(jdate);
2220        int eraIndex = getTransitionEraIndex(jdate);
2221        if (eraIndex == -1) {
2222            long transitionFixedDate = sinceFixedDates[eraIndex];
2223            CalendarDate d = eras[eraIndex].getSinceDate();
2224            if (transitionFixedDate <= cachedFixedDate) {
2225                length -= d.getDayOfMonth() - 1;
2226            } else {
2227                length = d.getDayOfMonth() - 1;
2228            }
2229        }
2230        return length;
2231    }
2232
2233    /**
2234     * Returns the index to the new era if the given date is in a
2235     * transition month.  For example, if the give date is Heisei 1
2236     * (1989) January 20, then the era index for Heisei is
2237     * returned. Likewise, if the given date is Showa 64 (1989)
2238     * January 3, then the era index for Heisei is returned. If the
2239     * given date is not in any transition month, then -1 is returned.
2240     */
2241    private static int getTransitionEraIndex(LocalGregorianCalendar.Date date) {
2242        int eraIndex = getEraIndex(date);
2243        CalendarDate transitionDate = eras[eraIndex].getSinceDate();
2244        if (transitionDate.getYear() == date.getNormalizedYear() &&
2245            transitionDate.getMonth() == date.getMonth()) {
2246            return eraIndex;
2247        }
2248        if (eraIndex < eras.length - 1) {
2249            transitionDate = eras[++eraIndex].getSinceDate();
2250            if (transitionDate.getYear() == date.getNormalizedYear() &&
2251                transitionDate.getMonth() == date.getMonth()) {
2252                return eraIndex;
2253            }
2254        }
2255        return -1;
2256    }
2257
2258    private boolean isTransitionYear(int normalizedYear) {
2259        for (int i = eras.length - 1; i > 0; i--) {
2260            int transitionYear = eras[i].getSinceDate().getYear();
2261            if (normalizedYear == transitionYear) {
2262                return true;
2263            }
2264            if (normalizedYear > transitionYear) {
2265                break;
2266            }
2267        }
2268        return false;
2269    }
2270
2271    private static int getEraIndex(LocalGregorianCalendar.Date date) {
2272        Era era = date.getEra();
2273        for (int i = eras.length - 1; i > 0; i--) {
2274            if (eras[i] == era) {
2275                return i;
2276            }
2277        }
2278        return 0;
2279    }
2280
2281    /**
2282     * Returns this object if it's normalized (all fields and time are
2283     * in sync). Otherwise, a cloned object is returned after calling
2284     * complete() in lenient mode.
2285     */
2286    private JapaneseImperialCalendar getNormalizedCalendar() {
2287        JapaneseImperialCalendar jc;
2288        if (isFullyNormalized()) {
2289            jc = this;
2290        } else {
2291            // Create a clone and normalize the calendar fields
2292            jc = (JapaneseImperialCalendar) this.clone();
2293            jc.setLenient(true);
2294            jc.complete();
2295        }
2296        return jc;
2297    }
2298
2299    /**
2300     * After adjustments such as add(MONTH), add(YEAR), we don't want the
2301     * month to jump around.  E.g., we don't want Jan 31 + 1 month to go to Mar
2302     * 3, we want it to go to Feb 28.  Adjustments which might run into this
2303     * problem call this method to retain the proper month.
2304     */
2305    private void pinDayOfMonth(LocalGregorianCalendar.Date date) {
2306        int year = date.getYear();
2307        int dom = date.getDayOfMonth();
2308        if (year != getMinimum(YEAR)) {
2309            date.setDayOfMonth(1);
2310            jcal.normalize(date);
2311            int monthLength = jcal.getMonthLength(date);
2312            if (dom > monthLength) {
2313                date.setDayOfMonth(monthLength);
2314            } else {
2315                date.setDayOfMonth(dom);
2316            }
2317            jcal.normalize(date);
2318        } else {
2319            LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
2320            LocalGregorianCalendar.Date realDate = jcal.getCalendarDate(time, getZone());
2321            long tod = realDate.getTimeOfDay();
2322            // Use an equivalent year.
2323            realDate.addYear(+400);
2324            realDate.setMonth(date.getMonth());
2325            realDate.setDayOfMonth(1);
2326            jcal.normalize(realDate);
2327            int monthLength = jcal.getMonthLength(realDate);
2328            if (dom > monthLength) {
2329                realDate.setDayOfMonth(monthLength);
2330            } else {
2331                if (dom < d.getDayOfMonth()) {
2332                    realDate.setDayOfMonth(d.getDayOfMonth());
2333                } else {
2334                    realDate.setDayOfMonth(dom);
2335                }
2336            }
2337            if (realDate.getDayOfMonth() == d.getDayOfMonth() && tod < d.getTimeOfDay()) {
2338                realDate.setDayOfMonth(Math.min(dom + 1, monthLength));
2339            }
2340            // restore the year.
2341            date.setDate(year, realDate.getMonth(), realDate.getDayOfMonth());
2342            // Don't normalize date here so as not to cause underflow.
2343        }
2344    }
2345
2346    /**
2347     * Returns the new value after 'roll'ing the specified value and amount.
2348     */
2349    private static int getRolledValue(int value, int amount, int min, int max) {
2350        assert value >= min && value <= max;
2351        int range = max - min + 1;
2352        amount %= range;
2353        int n = value + amount;
2354        if (n > max) {
2355            n -= range;
2356        } else if (n < min) {
2357            n += range;
2358        }
2359        assert n >= min && n <= max;
2360        return n;
2361    }
2362
2363    /**
2364     * Returns the ERA.  We need a special method for this because the
2365     * default ERA is the current era, but a zero (unset) ERA means before Meiji.
2366     */
2367    private int internalGetEra() {
2368        return isSet(ERA) ? internalGet(ERA) : eras.length - 1;
2369    }
2370
2371    /**
2372     * Updates internal state.
2373     */
2374    private void readObject(ObjectInputStream stream)
2375            throws IOException, ClassNotFoundException {
2376        stream.defaultReadObject();
2377        if (jdate == null) {
2378            jdate = jcal.newCalendarDate(getZone());
2379            cachedFixedDate = Long.MIN_VALUE;
2380        }
2381    }
2382}
2383