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