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