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