History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/util/JapaneseImperialCalendar.java (1 of 18352 files). Show all

Unified · Split

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