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