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