blob: 955b5722a75d8d199d23c779b6589fe9921c4ccc [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.util;
27
28import java.io.IOException;
29import java.io.ObjectInputStream;
30import sun.util.calendar.BaseCalendar;
31import sun.util.calendar.CalendarDate;
32import sun.util.calendar.CalendarSystem;
33import sun.util.calendar.CalendarUtils;
34import sun.util.calendar.Era;
35import sun.util.calendar.Gregorian;
36import sun.util.calendar.LocalGregorianCalendar;
37import sun.util.calendar.ZoneInfo;
38import 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 */
69class 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}