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