0001 /*
0002 * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package java.util;
0027
0028 import java.io.IOException;
0029 import java.io.ObjectInputStream;
0030 import sun.util.calendar.BaseCalendar;
0031 import sun.util.calendar.CalendarDate;
0032 import sun.util.calendar.CalendarSystem;
0033 import sun.util.calendar.CalendarUtils;
0034 import sun.util.calendar.Era;
0035 import sun.util.calendar.Gregorian;
0036 import sun.util.calendar.LocalGregorianCalendar;
0037 import sun.util.calendar.ZoneInfo;
0038 import sun.util.resources.LocaleData;
0039
0040 /**
0041 * <code>JapaneseImperialCalendar</code> implements a Japanese
0042 * calendar system in which the imperial era-based year numbering is
0043 * supported from the Meiji era. The following are the eras supported
0044 * by this calendar system.
0045 * <pre><tt>
0046 * ERA value Era name Since (in Gregorian)
0047 * ------------------------------------------------------
0048 * 0 N/A N/A
0049 * 1 Meiji 1868-01-01 midnight local time
0050 * 2 Taisho 1912-07-30 midnight local time
0051 * 3 Showa 1926-12-25 midnight local time
0052 * 4 Heisei 1989-01-08 midnight local time
0053 * ------------------------------------------------------
0054 * </tt></pre>
0055 *
0056 * <p><code>ERA</code> value 0 specifies the years before Meiji and
0057 * the Gregorian year values are used. Unlike {@link
0058 * GregorianCalendar}, the Julian to Gregorian transition is not
0059 * supported because it doesn't make any sense to the Japanese
0060 * calendar systems used before Meiji. To represent the years before
0061 * Gregorian year 1, 0 and negative values are used. The Japanese
0062 * Imperial rescripts and government decrees don't specify how to deal
0063 * with time differences for applying the era transitions. This
0064 * calendar implementation assumes local time for all transitions.
0065 *
0066 * @author Masayoshi Okutsu
0067 * @since 1.6
0068 */
0069 class JapaneseImperialCalendar extends Calendar {
0070 /*
0071 * Implementation Notes
0072 *
0073 * This implementation uses
0074 * sun.util.calendar.LocalGregorianCalendar to perform most of the
0075 * calendar calculations. LocalGregorianCalendar is configurable
0076 * and reads <JRE_HOME>/lib/calendars.properties at the start-up.
0077 */
0078
0079 /**
0080 * The ERA constant designating the era before Meiji.
0081 */
0082 public static final int BEFORE_MEIJI = 0;
0083
0084 /**
0085 * The ERA constant designating the Meiji era.
0086 */
0087 public static final int MEIJI = 1;
0088
0089 /**
0090 * The ERA constant designating the Taisho era.
0091 */
0092 public static final int TAISHO = 2;
0093
0094 /**
0095 * The ERA constant designating the Showa era.
0096 */
0097 public static final int SHOWA = 3;
0098
0099 /**
0100 * The ERA constant designating the Heisei era.
0101 */
0102 public static final int HEISEI = 4;
0103
0104 private static final int EPOCH_OFFSET = 719163; // Fixed date of January 1, 1970 (Gregorian)
0105 private static final int EPOCH_YEAR = 1970;
0106
0107 // Useful millisecond constants. Although ONE_DAY and ONE_WEEK can fit
0108 // into ints, they must be longs in order to prevent arithmetic overflow
0109 // when performing (bug 4173516).
0110 private static final int ONE_SECOND = 1000;
0111 private static final int ONE_MINUTE = 60 * ONE_SECOND;
0112 private static final int ONE_HOUR = 60 * ONE_MINUTE;
0113 private static final long ONE_DAY = 24 * ONE_HOUR;
0114 private static final long ONE_WEEK = 7 * ONE_DAY;
0115
0116 // Reference to the sun.util.calendar.LocalGregorianCalendar instance (singleton).
0117 private static final LocalGregorianCalendar jcal = (LocalGregorianCalendar) CalendarSystem
0118 .forName("japanese");
0119
0120 // Gregorian calendar instance. This is required because era
0121 // transition dates are given in Gregorian dates.
0122 private static final Gregorian gcal = CalendarSystem
0123 .getGregorianCalendar();
0124
0125 // The Era instance representing "before Meiji".
0126 private static final Era BEFORE_MEIJI_ERA = new Era("BeforeMeiji",
0127 "BM", Long.MIN_VALUE, false);
0128
0129 // Imperial eras. The sun.util.calendar.LocalGregorianCalendar
0130 // doesn't have an Era representing before Meiji, which is
0131 // inconvenient for a Calendar. So, era[0] is a reference to
0132 // BEFORE_MEIJI_ERA.
0133 private static final Era[] eras;
0134
0135 // Fixed date of the first date of each era.
0136 private static final long[] sinceFixedDates;
0137
0138 /*
0139 * <pre>
0140 * Greatest Least
0141 * Field name Minimum Minimum Maximum Maximum
0142 * ---------- ------- ------- ------- -------
0143 * ERA 0 0 1 1
0144 * YEAR -292275055 1 ? ?
0145 * MONTH 0 0 11 11
0146 * WEEK_OF_YEAR 1 1 52* 53
0147 * WEEK_OF_MONTH 0 0 4* 6
0148 * DAY_OF_MONTH 1 1 28* 31
0149 * DAY_OF_YEAR 1 1 365* 366
0150 * DAY_OF_WEEK 1 1 7 7
0151 * DAY_OF_WEEK_IN_MONTH -1 -1 4* 6
0152 * AM_PM 0 0 1 1
0153 * HOUR 0 0 11 11
0154 * HOUR_OF_DAY 0 0 23 23
0155 * MINUTE 0 0 59 59
0156 * SECOND 0 0 59 59
0157 * MILLISECOND 0 0 999 999
0158 * ZONE_OFFSET -13:00 -13:00 14:00 14:00
0159 * DST_OFFSET 0:00 0:00 0:20 2:00
0160 * </pre>
0161 * *: depends on eras
0162 */
0163 static final int MIN_VALUES[] = { 0, // ERA
0164 -292275055, // YEAR
0165 JANUARY, // MONTH
0166 1, // WEEK_OF_YEAR
0167 0, // WEEK_OF_MONTH
0168 1, // DAY_OF_MONTH
0169 1, // DAY_OF_YEAR
0170 SUNDAY, // DAY_OF_WEEK
0171 1, // DAY_OF_WEEK_IN_MONTH
0172 AM, // AM_PM
0173 0, // HOUR
0174 0, // HOUR_OF_DAY
0175 0, // MINUTE
0176 0, // SECOND
0177 0, // MILLISECOND
0178 -13 * ONE_HOUR, // ZONE_OFFSET (UNIX compatibility)
0179 0 // DST_OFFSET
0180 };
0181 static final int LEAST_MAX_VALUES[] = { 0, // ERA (initialized later)
0182 0, // YEAR (initialized later)
0183 JANUARY, // MONTH (Showa 64 ended in January.)
0184 0, // WEEK_OF_YEAR (Showa 1 has only 6 days which could be 0 weeks.)
0185 4, // WEEK_OF_MONTH
0186 28, // DAY_OF_MONTH
0187 0, // DAY_OF_YEAR (initialized later)
0188 SATURDAY, // DAY_OF_WEEK
0189 4, // DAY_OF_WEEK_IN
0190 PM, // AM_PM
0191 11, // HOUR
0192 23, // HOUR_OF_DAY
0193 59, // MINUTE
0194 59, // SECOND
0195 999, // MILLISECOND
0196 14 * ONE_HOUR, // ZONE_OFFSET
0197 20 * ONE_MINUTE // DST_OFFSET (historical least maximum)
0198 };
0199 static final int MAX_VALUES[] = { 0, // ERA
0200 292278994, // YEAR
0201 DECEMBER, // MONTH
0202 53, // WEEK_OF_YEAR
0203 6, // WEEK_OF_MONTH
0204 31, // DAY_OF_MONTH
0205 366, // DAY_OF_YEAR
0206 SATURDAY, // DAY_OF_WEEK
0207 6, // DAY_OF_WEEK_IN
0208 PM, // AM_PM
0209 11, // HOUR
0210 23, // HOUR_OF_DAY
0211 59, // MINUTE
0212 59, // SECOND
0213 999, // MILLISECOND
0214 14 * ONE_HOUR, // ZONE_OFFSET
0215 2 * ONE_HOUR // DST_OFFSET (double summer time)
0216 };
0217
0218 // Proclaim serialization compatibility with JDK 1.6
0219 private static final long serialVersionUID = -3364572813905467929L;
0220
0221 static {
0222 Era[] es = jcal.getEras();
0223 int length = es.length + 1;
0224 eras = new Era[length];
0225 sinceFixedDates = new long[length];
0226
0227 // eras[BEFORE_MEIJI] and sinceFixedDate[BEFORE_MEIJI] are the
0228 // same as Gregorian.
0229 int index = BEFORE_MEIJI;
0230 sinceFixedDates[index] = gcal.getFixedDate(BEFORE_MEIJI_ERA
0231 .getSinceDate());
0232 eras[index++] = BEFORE_MEIJI_ERA;
0233 for (Era e : es) {
0234 CalendarDate d = e.getSinceDate();
0235 sinceFixedDates[index] = gcal.getFixedDate(d);
0236 eras[index++] = e;
0237 }
0238
0239 LEAST_MAX_VALUES[ERA] = MAX_VALUES[ERA] = eras.length - 1;
0240
0241 // Calculate the least maximum year and least day of Year
0242 // values. The following code assumes that there's at most one
0243 // era transition in a Gregorian year.
0244 int year = Integer.MAX_VALUE;
0245 int dayOfYear = Integer.MAX_VALUE;
0246 CalendarDate date = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
0247 for (int i = 1; i < eras.length; i++) {
0248 long fd = sinceFixedDates[i];
0249 CalendarDate transitionDate = eras[i].getSinceDate();
0250 date.setDate(transitionDate.getYear(),
0251 BaseCalendar.JANUARY, 1);
0252 long fdd = gcal.getFixedDate(date);
0253 dayOfYear = Math.min((int) (fdd - fd), dayOfYear);
0254 date.setDate(transitionDate.getYear(),
0255 BaseCalendar.DECEMBER, 31);
0256 fdd = gcal.getFixedDate(date) + 1;
0257 dayOfYear = Math.min((int) (fd - fdd), dayOfYear);
0258
0259 LocalGregorianCalendar.Date lgd = getCalendarDate(fd - 1);
0260 int y = lgd.getYear();
0261 // Unless the first year starts from January 1, the actual
0262 // max value could be one year short. For example, if it's
0263 // Showa 63 January 8, 63 is the actual max value since
0264 // Showa 64 January 8 doesn't exist.
0265 if (!(lgd.getMonth() == BaseCalendar.JANUARY && lgd
0266 .getDayOfMonth() == 1))
0267 y--;
0268 year = Math.min(y, year);
0269 }
0270 LEAST_MAX_VALUES[YEAR] = year; // Max year could be smaller than this value.
0271 LEAST_MAX_VALUES[DAY_OF_YEAR] = dayOfYear;
0272 }
0273
0274 /**
0275 * jdate always has a sun.util.calendar.LocalGregorianCalendar.Date instance to
0276 * avoid overhead of creating it for each calculation.
0277 */
0278 private transient LocalGregorianCalendar.Date jdate;
0279
0280 /**
0281 * Temporary int[2] to get time zone offsets. zoneOffsets[0] gets
0282 * the GMT offset value and zoneOffsets[1] gets the daylight saving
0283 * value.
0284 */
0285 private transient int[] zoneOffsets;
0286
0287 /**
0288 * Temporary storage for saving original fields[] values in
0289 * non-lenient mode.
0290 */
0291 private transient int[] originalFields;
0292
0293 /**
0294 * Constructs a <code>JapaneseImperialCalendar</code> based on the current time
0295 * in the given time zone with the given locale.
0296 *
0297 * @param zone the given time zone.
0298 * @param aLocale the given locale.
0299 */
0300 public JapaneseImperialCalendar(TimeZone zone, Locale aLocale) {
0301 super (zone, aLocale);
0302 jdate = jcal.newCalendarDate(zone);
0303 setTimeInMillis(System.currentTimeMillis());
0304 }
0305
0306 /**
0307 * Compares this <code>JapaneseImperialCalendar</code> to the specified
0308 * <code>Object</code>. The result is <code>true</code> if and
0309 * only if the argument is a <code>JapaneseImperialCalendar</code> object
0310 * that represents the same time value (millisecond offset from
0311 * the <a href="Calendar.html#Epoch">Epoch</a>) under the same
0312 * <code>Calendar</code> parameters.
0313 *
0314 * @param obj the object to compare with.
0315 * @return <code>true</code> if this object is equal to <code>obj</code>;
0316 * <code>false</code> otherwise.
0317 * @see Calendar#compareTo(Calendar)
0318 */
0319 public boolean equals(Object obj) {
0320 return obj instanceof JapaneseImperialCalendar
0321 && super .equals(obj);
0322 }
0323
0324 /**
0325 * Generates the hash code for this
0326 * <code>JapaneseImperialCalendar</code> object.
0327 */
0328 public int hashCode() {
0329 return super .hashCode() ^ jdate.hashCode();
0330 }
0331
0332 /**
0333 * Adds the specified (signed) amount of time to the given calendar field,
0334 * based on the calendar's rules.
0335 *
0336 * <p><em>Add rule 1</em>. The value of <code>field</code>
0337 * after the call minus the value of <code>field</code> before the
0338 * call is <code>amount</code>, modulo any overflow that has occurred in
0339 * <code>field</code>. Overflow occurs when a field value exceeds its
0340 * range and, as a result, the next larger field is incremented or
0341 * decremented and the field value is adjusted back into its range.</p>
0342 *
0343 * <p><em>Add rule 2</em>. If a smaller field is expected to be
0344 * invariant, but it is impossible for it to be equal to its
0345 * prior value because of changes in its minimum or maximum after
0346 * <code>field</code> is changed, then its value is adjusted to be as close
0347 * as possible to its expected value. A smaller field represents a
0348 * smaller unit of time. <code>HOUR</code> is a smaller field than
0349 * <code>DAY_OF_MONTH</code>. No adjustment is made to smaller fields
0350 * that are not expected to be invariant. The calendar system
0351 * determines what fields are expected to be invariant.</p>
0352 *
0353 * @param field the calendar field.
0354 * @param amount the amount of date or time to be added to the field.
0355 * @exception IllegalArgumentException if <code>field</code> is
0356 * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
0357 * or if any calendar fields have out-of-range values in
0358 * non-lenient mode.
0359 */
0360 public void add(int field, int amount) {
0361 // If amount == 0, do nothing even the given field is out of
0362 // range. This is tested by JCK.
0363 if (amount == 0) {
0364 return; // Do nothing!
0365 }
0366
0367 if (field < 0 || field >= ZONE_OFFSET) {
0368 throw new IllegalArgumentException();
0369 }
0370
0371 // Sync the time and calendar fields.
0372 complete();
0373
0374 if (field == YEAR) {
0375 LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate
0376 .clone();
0377 d.addYear(amount);
0378 pinDayOfMonth(d);
0379 set(ERA, getEraIndex(d));
0380 set(YEAR, d.getYear());
0381 set(MONTH, d.getMonth() - 1);
0382 set(DAY_OF_MONTH, d.getDayOfMonth());
0383 } else if (field == MONTH) {
0384 LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate
0385 .clone();
0386 d.addMonth(amount);
0387 pinDayOfMonth(d);
0388 set(ERA, getEraIndex(d));
0389 set(YEAR, d.getYear());
0390 set(MONTH, d.getMonth() - 1);
0391 set(DAY_OF_MONTH, d.getDayOfMonth());
0392 } else if (field == ERA) {
0393 int era = internalGet(ERA) + amount;
0394 if (era < 0) {
0395 era = 0;
0396 } else if (era > eras.length - 1) {
0397 era = eras.length - 1;
0398 }
0399 set(ERA, era);
0400 } else {
0401 long delta = amount;
0402 long timeOfDay = 0;
0403 switch (field) {
0404 // Handle the time fields here. Convert the given
0405 // amount to milliseconds and call setTimeInMillis.
0406 case HOUR:
0407 case HOUR_OF_DAY:
0408 delta *= 60 * 60 * 1000; // hours to milliseconds
0409 break;
0410
0411 case MINUTE:
0412 delta *= 60 * 1000; // minutes to milliseconds
0413 break;
0414
0415 case SECOND:
0416 delta *= 1000; // seconds to milliseconds
0417 break;
0418
0419 case MILLISECOND:
0420 break;
0421
0422 // Handle week, day and AM_PM fields which involves
0423 // time zone offset change adjustment. Convert the
0424 // given amount to the number of days.
0425 case WEEK_OF_YEAR:
0426 case WEEK_OF_MONTH:
0427 case DAY_OF_WEEK_IN_MONTH:
0428 delta *= 7;
0429 break;
0430
0431 case DAY_OF_MONTH: // synonym of DATE
0432 case DAY_OF_YEAR:
0433 case DAY_OF_WEEK:
0434 break;
0435
0436 case AM_PM:
0437 // Convert the amount to the number of days (delta)
0438 // and +12 or -12 hours (timeOfDay).
0439 delta = amount / 2;
0440 timeOfDay = 12 * (amount % 2);
0441 break;
0442 }
0443
0444 // The time fields don't require time zone offset change
0445 // adjustment.
0446 if (field >= HOUR) {
0447 setTimeInMillis(time + delta);
0448 return;
0449 }
0450
0451 // The rest of the fields (week, day or AM_PM fields)
0452 // require time zone offset (both GMT and DST) change
0453 // adjustment.
0454
0455 // Translate the current time to the fixed date and time
0456 // of the day.
0457 long fd = cachedFixedDate;
0458 timeOfDay += internalGet(HOUR_OF_DAY);
0459 timeOfDay *= 60;
0460 timeOfDay += internalGet(MINUTE);
0461 timeOfDay *= 60;
0462 timeOfDay += internalGet(SECOND);
0463 timeOfDay *= 1000;
0464 timeOfDay += internalGet(MILLISECOND);
0465 if (timeOfDay >= ONE_DAY) {
0466 fd++;
0467 timeOfDay -= ONE_DAY;
0468 } else if (timeOfDay < 0) {
0469 fd--;
0470 timeOfDay += ONE_DAY;
0471 }
0472
0473 fd += delta; // fd is the expected fixed date after the calculation
0474 int zoneOffset = internalGet(ZONE_OFFSET)
0475 + internalGet(DST_OFFSET);
0476 setTimeInMillis((fd - EPOCH_OFFSET) * ONE_DAY + timeOfDay
0477 - zoneOffset);
0478 zoneOffset -= internalGet(ZONE_OFFSET)
0479 + internalGet(DST_OFFSET);
0480 // If the time zone offset has changed, then adjust the difference.
0481 if (zoneOffset != 0) {
0482 setTimeInMillis(time + zoneOffset);
0483 long fd2 = cachedFixedDate;
0484 // If the adjustment has changed the date, then take
0485 // the previous one.
0486 if (fd2 != fd) {
0487 setTimeInMillis(time - zoneOffset);
0488 }
0489 }
0490 }
0491 }
0492
0493 public void roll(int field, boolean up) {
0494 roll(field, up ? +1 : -1);
0495 }
0496
0497 /**
0498 * Adds a signed amount to the specified calendar field without changing larger fields.
0499 * A negative roll amount means to subtract from field without changing
0500 * larger fields. If the specified amount is 0, this method performs nothing.
0501 *
0502 * <p>This method calls {@link #complete()} before adding the
0503 * amount so that all the calendar fields are normalized. If there
0504 * is any calendar field having an out-of-range value in non-lenient mode, then an
0505 * <code>IllegalArgumentException</code> is thrown.
0506 *
0507 * @param field the calendar field.
0508 * @param amount the signed amount to add to <code>field</code>.
0509 * @exception IllegalArgumentException if <code>field</code> is
0510 * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
0511 * or if any calendar fields have out-of-range values in
0512 * non-lenient mode.
0513 * @see #roll(int,boolean)
0514 * @see #add(int,int)
0515 * @see #set(int,int)
0516 */
0517 public void roll(int field, int amount) {
0518 // If amount == 0, do nothing even the given field is out of
0519 // range. This is tested by JCK.
0520 if (amount == 0) {
0521 return;
0522 }
0523
0524 if (field < 0 || field >= ZONE_OFFSET) {
0525 throw new IllegalArgumentException();
0526 }
0527
0528 // Sync the time and calendar fields.
0529 complete();
0530
0531 int min = getMinimum(field);
0532 int max = getMaximum(field);
0533
0534 switch (field) {
0535 case ERA:
0536 case AM_PM:
0537 case MINUTE:
0538 case SECOND:
0539 case MILLISECOND:
0540 // These fields are handled simply, since they have fixed
0541 // minima and maxima. Other fields are complicated, since
0542 // the range within they must roll varies depending on the
0543 // date, a time zone and the era transitions.
0544 break;
0545
0546 case HOUR:
0547 case HOUR_OF_DAY: {
0548 int unit = max + 1; // 12 or 24 hours
0549 int h = internalGet(field);
0550 int nh = (h + amount) % unit;
0551 if (nh < 0) {
0552 nh += unit;
0553 }
0554 time += ONE_HOUR * (nh - h);
0555
0556 // The day might have changed, which could happen if
0557 // the daylight saving time transition brings it to
0558 // the next day, although it's very unlikely. But we
0559 // have to make sure not to change the larger fields.
0560 CalendarDate d = jcal.getCalendarDate(time, getZone());
0561 if (internalGet(DAY_OF_MONTH) != d.getDayOfMonth()) {
0562 d.setEra(jdate.getEra());
0563 d.setDate(internalGet(YEAR), internalGet(MONTH) + 1,
0564 internalGet(DAY_OF_MONTH));
0565 if (field == HOUR) {
0566 assert (internalGet(AM_PM) == PM);
0567 d.addHours(+12); // restore PM
0568 }
0569 time = jcal.getTime(d);
0570 }
0571 int hourOfDay = d.getHours();
0572 internalSet(field, hourOfDay % unit);
0573 if (field == HOUR) {
0574 internalSet(HOUR_OF_DAY, hourOfDay);
0575 } else {
0576 internalSet(AM_PM, hourOfDay / 12);
0577 internalSet(HOUR, hourOfDay % 12);
0578 }
0579
0580 // Time zone offset and/or daylight saving might have changed.
0581 int zoneOffset = d.getZoneOffset();
0582 int saving = d.getDaylightSaving();
0583 internalSet(ZONE_OFFSET, zoneOffset - saving);
0584 internalSet(DST_OFFSET, saving);
0585 return;
0586 }
0587
0588 case YEAR:
0589 min = getActualMinimum(field);
0590 max = getActualMaximum(field);
0591 break;
0592
0593 case MONTH:
0594 // Rolling the month involves both pinning the final value to [0, 11]
0595 // and adjusting the DAY_OF_MONTH if necessary. We only adjust the
0596 // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
0597 // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
0598 {
0599 if (!isTransitionYear(jdate.getNormalizedYear())) {
0600 int year = jdate.getYear();
0601 if (year == getMaximum(YEAR)) {
0602 CalendarDate jd = jcal.getCalendarDate(time,
0603 getZone());
0604 CalendarDate d = jcal.getCalendarDate(
0605 Long.MAX_VALUE, getZone());
0606 max = d.getMonth() - 1;
0607 int n = getRolledValue(internalGet(field), amount,
0608 min, max);
0609 if (n == max) {
0610 // To avoid overflow, use an equivalent year.
0611 jd.addYear(-400);
0612 jd.setMonth(n + 1);
0613 if (jd.getDayOfMonth() > d.getDayOfMonth()) {
0614 jd.setDayOfMonth(d.getDayOfMonth());
0615 jcal.normalize(jd);
0616 }
0617 if (jd.getDayOfMonth() == d.getDayOfMonth()
0618 && jd.getTimeOfDay() > d.getTimeOfDay()) {
0619 jd.setMonth(n + 1);
0620 jd.setDayOfMonth(d.getDayOfMonth() - 1);
0621 jcal.normalize(jd);
0622 // Month may have changed by the normalization.
0623 n = jd.getMonth() - 1;
0624 }
0625 set(DAY_OF_MONTH, jd.getDayOfMonth());
0626 }
0627 set(MONTH, n);
0628 } else if (year == getMinimum(YEAR)) {
0629 CalendarDate jd = jcal.getCalendarDate(time,
0630 getZone());
0631 CalendarDate d = jcal.getCalendarDate(
0632 Long.MIN_VALUE, getZone());
0633 min = d.getMonth() - 1;
0634 int n = getRolledValue(internalGet(field), amount,
0635 min, max);
0636 if (n == min) {
0637 // To avoid underflow, use an equivalent year.
0638 jd.addYear(+400);
0639 jd.setMonth(n + 1);
0640 if (jd.getDayOfMonth() < d.getDayOfMonth()) {
0641 jd.setDayOfMonth(d.getDayOfMonth());
0642 jcal.normalize(jd);
0643 }
0644 if (jd.getDayOfMonth() == d.getDayOfMonth()
0645 && jd.getTimeOfDay() < d.getTimeOfDay()) {
0646 jd.setMonth(n + 1);
0647 jd.setDayOfMonth(d.getDayOfMonth() + 1);
0648 jcal.normalize(jd);
0649 // Month may have changed by the normalization.
0650 n = jd.getMonth() - 1;
0651 }
0652 set(DAY_OF_MONTH, jd.getDayOfMonth());
0653 }
0654 set(MONTH, n);
0655 } else {
0656 int mon = (internalGet(MONTH) + amount) % 12;
0657 if (mon < 0) {
0658 mon += 12;
0659 }
0660 set(MONTH, mon);
0661
0662 // Keep the day of month in the range. We
0663 // don't want to spill over into the next
0664 // month; e.g., we don't want jan31 + 1 mo ->
0665 // feb31 -> mar3.
0666 int monthLen = monthLength(mon);
0667 if (internalGet(DAY_OF_MONTH) > monthLen) {
0668 set(DAY_OF_MONTH, monthLen);
0669 }
0670 }
0671 } else {
0672 int eraIndex = getEraIndex(jdate);
0673 CalendarDate transition = null;
0674 if (jdate.getYear() == 1) {
0675 transition = eras[eraIndex].getSinceDate();
0676 min = transition.getMonth() - 1;
0677 } else {
0678 if (eraIndex < eras.length - 1) {
0679 transition = eras[eraIndex + 1].getSinceDate();
0680 if (transition.getYear() == jdate
0681 .getNormalizedYear()) {
0682 max = transition.getMonth() - 1;
0683 if (transition.getDayOfMonth() == 1) {
0684 max--;
0685 }
0686 }
0687 }
0688 }
0689
0690 if (min == max) {
0691 // The year has only one month. No need to
0692 // process further. (Showa Gan-nen (year 1)
0693 // and the last year have only one month.)
0694 return;
0695 }
0696 int n = getRolledValue(internalGet(field), amount, min,
0697 max);
0698 set(MONTH, n);
0699 if (n == min) {
0700 if (!(transition.getMonth() == BaseCalendar.JANUARY && transition
0701 .getDayOfMonth() == 1)) {
0702 if (jdate.getDayOfMonth() < transition
0703 .getDayOfMonth()) {
0704 set(DAY_OF_MONTH, transition
0705 .getDayOfMonth());
0706 }
0707 }
0708 } else if (n == max && (transition.getMonth() - 1 == n)) {
0709 int dom = transition.getDayOfMonth();
0710 if (jdate.getDayOfMonth() >= dom) {
0711 set(DAY_OF_MONTH, dom - 1);
0712 }
0713 }
0714 }
0715 return;
0716 }
0717
0718 case WEEK_OF_YEAR: {
0719 int y = jdate.getNormalizedYear();
0720 max = getActualMaximum(WEEK_OF_YEAR);
0721 set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK)); // update stamp[field]
0722 int woy = internalGet(WEEK_OF_YEAR);
0723 int value = woy + amount;
0724 if (!isTransitionYear(jdate.getNormalizedYear())) {
0725 int year = jdate.getYear();
0726 if (year == getMaximum(YEAR)) {
0727 max = getActualMaximum(WEEK_OF_YEAR);
0728 } else if (year == getMinimum(YEAR)) {
0729 min = getActualMinimum(WEEK_OF_YEAR);
0730 max = getActualMaximum(WEEK_OF_YEAR);
0731 if (value > min && value < max) {
0732 set(WEEK_OF_YEAR, value);
0733 return;
0734 }
0735
0736 }
0737 // If the new value is in between min and max
0738 // (exclusive), then we can use the value.
0739 if (value > min && value < max) {
0740 set(WEEK_OF_YEAR, value);
0741 return;
0742 }
0743 long fd = cachedFixedDate;
0744 // Make sure that the min week has the current DAY_OF_WEEK
0745 long day1 = fd - (7 * (woy - min));
0746 if (year != getMinimum(YEAR)) {
0747 if (gcal.getYearFromFixedDate(day1) != y) {
0748 min++;
0749 }
0750 } else {
0751 CalendarDate d = jcal.getCalendarDate(
0752 Long.MIN_VALUE, getZone());
0753 if (day1 < jcal.getFixedDate(d)) {
0754 min++;
0755 }
0756 }
0757
0758 // Make sure the same thing for the max week
0759 fd += 7 * (max - internalGet(WEEK_OF_YEAR));
0760 if (gcal.getYearFromFixedDate(fd) != y) {
0761 max--;
0762 }
0763 break;
0764 }
0765
0766 // Handle transition here.
0767 long fd = cachedFixedDate;
0768 long day1 = fd - (7 * (woy - min));
0769 // Make sure that the min week has the current DAY_OF_WEEK
0770 LocalGregorianCalendar.Date d = getCalendarDate(day1);
0771 if (!(d.getEra() == jdate.getEra() && d.getYear() == jdate
0772 .getYear())) {
0773 min++;
0774 }
0775
0776 // Make sure the same thing for the max week
0777 fd += 7 * (max - woy);
0778 jcal.getCalendarDateFromFixedDate(d, fd);
0779 if (!(d.getEra() == jdate.getEra() && d.getYear() == jdate
0780 .getYear())) {
0781 max--;
0782 }
0783 // value: the new WEEK_OF_YEAR which must be converted
0784 // to month and day of month.
0785 value = getRolledValue(woy, amount, min, max) - 1;
0786 d = getCalendarDate(day1 + value * 7);
0787 set(MONTH, d.getMonth() - 1);
0788 set(DAY_OF_MONTH, d.getDayOfMonth());
0789 return;
0790 }
0791
0792 case WEEK_OF_MONTH: {
0793 boolean isTransitionYear = isTransitionYear(jdate
0794 .getNormalizedYear());
0795 // dow: relative day of week from the first day of week
0796 int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
0797 if (dow < 0) {
0798 dow += 7;
0799 }
0800
0801 long fd = cachedFixedDate;
0802 long month1; // fixed date of the first day (usually 1) of the month
0803 int monthLength; // actual month length
0804 if (isTransitionYear) {
0805 month1 = getFixedDateMonth1(jdate, fd);
0806 monthLength = actualMonthLength();
0807 } else {
0808 month1 = fd - internalGet(DAY_OF_MONTH) + 1;
0809 monthLength = jcal.getMonthLength(jdate);
0810 }
0811
0812 // the first day of week of the month.
0813 long monthDay1st = jcal.getDayOfWeekDateOnOrBefore(
0814 month1 + 6, getFirstDayOfWeek());
0815 // if the week has enough days to form a week, the
0816 // week starts from the previous month.
0817 if ((int) (monthDay1st - month1) >= getMinimalDaysInFirstWeek()) {
0818 monthDay1st -= 7;
0819 }
0820 max = getActualMaximum(field);
0821
0822 // value: the new WEEK_OF_MONTH value
0823 int value = getRolledValue(internalGet(field), amount, 1,
0824 max) - 1;
0825
0826 // nfd: fixed date of the rolled date
0827 long nfd = monthDay1st + value * 7 + dow;
0828
0829 // Unlike WEEK_OF_YEAR, we need to change day of week if the
0830 // nfd is out of the month.
0831 if (nfd < month1) {
0832 nfd = month1;
0833 } else if (nfd >= (month1 + monthLength)) {
0834 nfd = month1 + monthLength - 1;
0835 }
0836 set(DAY_OF_MONTH, (int) (nfd - month1) + 1);
0837 return;
0838 }
0839
0840 case DAY_OF_MONTH: {
0841 if (!isTransitionYear(jdate.getNormalizedYear())) {
0842 max = jcal.getMonthLength(jdate);
0843 break;
0844 }
0845
0846 // TODO: Need to change the spec to be usable DAY_OF_MONTH rolling...
0847
0848 // Transition handling. We can't change year and era
0849 // values here due to the Calendar roll spec!
0850 long month1 = getFixedDateMonth1(jdate, cachedFixedDate);
0851
0852 // It may not be a regular month. Convert the date and range to
0853 // the relative values, perform the roll, and
0854 // convert the result back to the rolled date.
0855 int value = getRolledValue(
0856 (int) (cachedFixedDate - month1), amount, 0,
0857 actualMonthLength() - 1);
0858 LocalGregorianCalendar.Date d = getCalendarDate(month1
0859 + value);
0860 assert getEraIndex(d) == internalGetEra()
0861 && d.getYear() == internalGet(YEAR)
0862 && d.getMonth() - 1 == internalGet(MONTH);
0863 set(DAY_OF_MONTH, d.getDayOfMonth());
0864 return;
0865 }
0866
0867 case DAY_OF_YEAR: {
0868 max = getActualMaximum(field);
0869 if (!isTransitionYear(jdate.getNormalizedYear())) {
0870 break;
0871 }
0872
0873 // Handle transition. We can't change year and era values
0874 // here due to the Calendar roll spec.
0875 int value = getRolledValue(internalGet(DAY_OF_YEAR),
0876 amount, min, max);
0877 long jan0 = cachedFixedDate - internalGet(DAY_OF_YEAR);
0878 LocalGregorianCalendar.Date d = getCalendarDate(jan0
0879 + value);
0880 assert getEraIndex(d) == internalGetEra()
0881 && d.getYear() == internalGet(YEAR);
0882 set(MONTH, d.getMonth() - 1);
0883 set(DAY_OF_MONTH, d.getDayOfMonth());
0884 return;
0885 }
0886
0887 case DAY_OF_WEEK: {
0888 int normalizedYear = jdate.getNormalizedYear();
0889 if (!isTransitionYear(normalizedYear)
0890 && !isTransitionYear(normalizedYear - 1)) {
0891 // If the week of year is in the same year, we can
0892 // just change DAY_OF_WEEK.
0893 int weekOfYear = internalGet(WEEK_OF_YEAR);
0894 if (weekOfYear > 1 && weekOfYear < 52) {
0895 set(WEEK_OF_YEAR, internalGet(WEEK_OF_YEAR));
0896 max = SATURDAY;
0897 break;
0898 }
0899 }
0900
0901 // We need to handle it in a different way around year
0902 // boundaries and in the transition year. Note that
0903 // changing era and year values violates the roll
0904 // rule: not changing larger calendar fields...
0905 amount %= 7;
0906 if (amount == 0) {
0907 return;
0908 }
0909 long fd = cachedFixedDate;
0910 long dowFirst = jcal.getDayOfWeekDateOnOrBefore(fd,
0911 getFirstDayOfWeek());
0912 fd += amount;
0913 if (fd < dowFirst) {
0914 fd += 7;
0915 } else if (fd >= dowFirst + 7) {
0916 fd -= 7;
0917 }
0918 LocalGregorianCalendar.Date d = getCalendarDate(fd);
0919 set(ERA, getEraIndex(d));
0920 set(d.getYear(), d.getMonth() - 1, d.getDayOfMonth());
0921 return;
0922 }
0923
0924 case DAY_OF_WEEK_IN_MONTH: {
0925 min = 1; // after having normalized, min should be 1.
0926 if (!isTransitionYear(jdate.getNormalizedYear())) {
0927 int dom = internalGet(DAY_OF_MONTH);
0928 int monthLength = jcal.getMonthLength(jdate);
0929 int lastDays = monthLength % 7;
0930 max = monthLength / 7;
0931 int x = (dom - 1) % 7;
0932 if (x < lastDays) {
0933 max++;
0934 }
0935 set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK));
0936 break;
0937 }
0938
0939 // Transition year handling.
0940 long fd = cachedFixedDate;
0941 long month1 = getFixedDateMonth1(jdate, fd);
0942 int monthLength = actualMonthLength();
0943 int lastDays = monthLength % 7;
0944 max = monthLength / 7;
0945 int x = (int) (fd - month1) % 7;
0946 if (x < lastDays) {
0947 max++;
0948 }
0949 int value = getRolledValue(internalGet(field), amount, min,
0950 max) - 1;
0951 fd = month1 + value * 7 + x;
0952 LocalGregorianCalendar.Date d = getCalendarDate(fd);
0953 set(DAY_OF_MONTH, d.getDayOfMonth());
0954 return;
0955 }
0956 }
0957
0958 set(field, getRolledValue(internalGet(field), amount, min, max));
0959 }
0960
0961 public String getDisplayName(int field, int style, Locale locale) {
0962 if (!checkDisplayNameParams(field, style, SHORT, LONG, locale,
0963 ERA_MASK | YEAR_MASK | MONTH_MASK | DAY_OF_WEEK_MASK
0964 | AM_PM_MASK)) {
0965 return null;
0966 }
0967
0968 // "GanNen" is supported only in the LONG style.
0969 if (field == YEAR
0970 && (style == SHORT || get(YEAR) != 1 || get(ERA) == 0)) {
0971 return null;
0972 }
0973
0974 ResourceBundle rb = LocaleData.getDateFormatData(locale);
0975 String name = null;
0976 String key = getKey(field, style);
0977 if (key != null) {
0978 String[] strings = rb.getStringArray(key);
0979 if (field == YEAR) {
0980 if (strings.length > 0) {
0981 name = strings[0];
0982 }
0983 } else {
0984 int index = get(field);
0985 // If the ERA value is out of range for strings, then
0986 // try to get its name or abbreviation from the Era instance.
0987 if (field == ERA && index >= strings.length
0988 && index < eras.length) {
0989 Era era = eras[index];
0990 name = (style == SHORT) ? era.getAbbreviation()
0991 : era.getName();
0992 } else {
0993 if (field == DAY_OF_WEEK)
0994 --index;
0995 name = strings[index];
0996 }
0997 }
0998 }
0999 return name;
1000 }
1001
1002 public Map<String, Integer> getDisplayNames(int field, int style,
1003 Locale locale) {
1004 if (!checkDisplayNameParams(field, style, ALL_STYLES, LONG,
1005 locale, ERA_MASK | YEAR_MASK | MONTH_MASK
1006 | DAY_OF_WEEK_MASK | AM_PM_MASK)) {
1007 return null;
1008 }
1009
1010 if (style == ALL_STYLES) {
1011 Map<String, Integer> shortNames = getDisplayNamesImpl(
1012 field, SHORT, locale);
1013 if (field == AM_PM) {
1014 return shortNames;
1015 }
1016 Map<String, Integer> longNames = getDisplayNamesImpl(field,
1017 LONG, locale);
1018 if (shortNames == null) {
1019 return longNames;
1020 }
1021 if (longNames != null) {
1022 shortNames.putAll(longNames);
1023 }
1024 return shortNames;
1025 }
1026
1027 // SHORT or LONG
1028 return getDisplayNamesImpl(field, style, locale);
1029 }
1030
1031 private Map<String, Integer> getDisplayNamesImpl(int field,
1032 int style, Locale locale) {
1033 ResourceBundle rb = LocaleData.getDateFormatData(locale);
1034 String key = getKey(field, style);
1035 Map<String, Integer> map = new HashMap<String, Integer>();
1036 if (key != null) {
1037 String[] strings = rb.getStringArray(key);
1038 if (field == YEAR) {
1039 if (strings.length > 0) {
1040 map.put(strings[0], 1);
1041 }
1042 } else {
1043 int base = (field == DAY_OF_WEEK) ? 1 : 0;
1044 for (int i = 0; i < strings.length; i++) {
1045 map.put(strings[i], base + i);
1046 }
1047 // If strings[] has fewer than eras[], get more names from eras[].
1048 if (field == ERA && strings.length < eras.length) {
1049 for (int i = strings.length; i < eras.length; i++) {
1050 Era era = eras[i];
1051 String name = (style == SHORT) ? era
1052 .getAbbreviation() : era.getName();
1053 map.put(name, i);
1054 }
1055 }
1056 }
1057 }
1058 return map.size() > 0 ? map : null;
1059 }
1060
1061 private String getKey(int field, int style) {
1062 String className = JapaneseImperialCalendar.class.getName();
1063 StringBuilder key = new StringBuilder();
1064 switch (field) {
1065 case ERA:
1066 key.append(className);
1067 if (style == SHORT) {
1068 key.append(".short");
1069 }
1070 key.append(".Eras");
1071 break;
1072
1073 case YEAR:
1074 key.append(className).append(".FirstYear");
1075 break;
1076
1077 case MONTH:
1078 key.append(style == SHORT ? "MonthAbbreviations"
1079 : "MonthNames");
1080 break;
1081
1082 case DAY_OF_WEEK:
1083 key
1084 .append(style == SHORT ? "DayAbbreviations"
1085 : "DayNames");
1086 break;
1087
1088 case AM_PM:
1089 key.append("AmPmMarkers");
1090 break;
1091 }
1092 return key.length() > 0 ? key.toString() : null;
1093 }
1094
1095 /**
1096 * Returns the minimum value for the given calendar field of this
1097 * <code>Calendar</code> instance. The minimum value is
1098 * defined as the smallest value returned by the {@link
1099 * Calendar#get(int) get} method for any possible time value,
1100 * taking into consideration the current values of the
1101 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1102 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1103 * and {@link Calendar#getTimeZone() getTimeZone} methods.
1104 *
1105 * @param field the calendar field.
1106 * @return the minimum value for the given calendar field.
1107 * @see #getMaximum(int)
1108 * @see #getGreatestMinimum(int)
1109 * @see #getLeastMaximum(int)
1110 * @see #getActualMinimum(int)
1111 * @see #getActualMaximum(int)
1112 */
1113 public int getMinimum(int field) {
1114 return MIN_VALUES[field];
1115 }
1116
1117 /**
1118 * Returns the maximum value for the given calendar field of this
1119 * <code>GregorianCalendar</code> instance. The maximum value is
1120 * defined as the largest value returned by the {@link
1121 * Calendar#get(int) get} method for any possible time value,
1122 * taking into consideration the current values of the
1123 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1124 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1125 * and {@link Calendar#getTimeZone() getTimeZone} methods.
1126 *
1127 * @param field the calendar field.
1128 * @return the maximum value for the given calendar field.
1129 * @see #getMinimum(int)
1130 * @see #getGreatestMinimum(int)
1131 * @see #getLeastMaximum(int)
1132 * @see #getActualMinimum(int)
1133 * @see #getActualMaximum(int)
1134 */
1135 public int getMaximum(int field) {
1136 switch (field) {
1137 case YEAR: {
1138 // The value should depend on the time zone of this calendar.
1139 LocalGregorianCalendar.Date d = jcal.getCalendarDate(
1140 Long.MAX_VALUE, getZone());
1141 return Math.max(LEAST_MAX_VALUES[YEAR], d.getYear());
1142 }
1143 }
1144 return MAX_VALUES[field];
1145 }
1146
1147 /**
1148 * Returns the highest minimum value for the given calendar field
1149 * of this <code>GregorianCalendar</code> instance. The highest
1150 * minimum value is defined as the largest value returned by
1151 * {@link #getActualMinimum(int)} for any possible time value,
1152 * taking into consideration the current values of the
1153 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1154 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1155 * and {@link Calendar#getTimeZone() getTimeZone} methods.
1156 *
1157 * @param field the calendar field.
1158 * @return the highest minimum value for the given calendar field.
1159 * @see #getMinimum(int)
1160 * @see #getMaximum(int)
1161 * @see #getLeastMaximum(int)
1162 * @see #getActualMinimum(int)
1163 * @see #getActualMaximum(int)
1164 */
1165 public int getGreatestMinimum(int field) {
1166 return field == YEAR ? 1 : MIN_VALUES[field];
1167 }
1168
1169 /**
1170 * Returns the lowest maximum value for the given calendar field
1171 * of this <code>GregorianCalendar</code> instance. The lowest
1172 * maximum value is defined as the smallest value returned by
1173 * {@link #getActualMaximum(int)} for any possible time value,
1174 * taking into consideration the current values of the
1175 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1176 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1177 * and {@link Calendar#getTimeZone() getTimeZone} methods.
1178 *
1179 * @param field the calendar field
1180 * @return the lowest maximum value for the given calendar field.
1181 * @see #getMinimum(int)
1182 * @see #getMaximum(int)
1183 * @see #getGreatestMinimum(int)
1184 * @see #getActualMinimum(int)
1185 * @see #getActualMaximum(int)
1186 */
1187 public int getLeastMaximum(int field) {
1188 switch (field) {
1189 case YEAR: {
1190 return Math.min(LEAST_MAX_VALUES[YEAR], getMaximum(YEAR));
1191 }
1192 }
1193 return LEAST_MAX_VALUES[field];
1194 }
1195
1196 /**
1197 * Returns the minimum value that this calendar field could have,
1198 * taking into consideration the given time value and the current
1199 * values of the
1200 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1201 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1202 * and {@link Calendar#getTimeZone() getTimeZone} methods.
1203 *
1204 * @param field the calendar field
1205 * @return the minimum of the given field for the time value of
1206 * this <code>JapaneseImperialCalendar</code>
1207 * @see #getMinimum(int)
1208 * @see #getMaximum(int)
1209 * @see #getGreatestMinimum(int)
1210 * @see #getLeastMaximum(int)
1211 * @see #getActualMaximum(int)
1212 */
1213 public int getActualMinimum(int field) {
1214 if (!isFieldSet(YEAR_MASK | MONTH_MASK | WEEK_OF_YEAR_MASK,
1215 field)) {
1216 return getMinimum(field);
1217 }
1218
1219 int value = 0;
1220 JapaneseImperialCalendar jc = getNormalizedCalendar();
1221 // Get a local date which includes time of day and time zone,
1222 // which are missing in jc.jdate.
1223 LocalGregorianCalendar.Date jd = jcal.getCalendarDate(jc
1224 .getTimeInMillis(), getZone());
1225 int eraIndex = getEraIndex(jd);
1226 switch (field) {
1227 case YEAR: {
1228 if (eraIndex > BEFORE_MEIJI) {
1229 value = 1;
1230 long since = eras[eraIndex].getSince(getZone());
1231 CalendarDate d = jcal.getCalendarDate(since, getZone());
1232 // Use the same year in jd to take care of leap
1233 // years. i.e., both jd and d must agree on leap
1234 // or common years.
1235 jd.setYear(d.getYear());
1236 jcal.normalize(jd);
1237 assert jd.isLeapYear() == d.isLeapYear();
1238 if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
1239 value++;
1240 }
1241 } else {
1242 value = getMinimum(field);
1243 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE,
1244 getZone());
1245 // Use an equvalent year of d.getYear() if
1246 // possible. Otherwise, ignore the leap year and
1247 // common year difference.
1248 int y = d.getYear();
1249 if (y > 400) {
1250 y -= 400;
1251 }
1252 jd.setYear(y);
1253 jcal.normalize(jd);
1254 if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
1255 value++;
1256 }
1257 }
1258 }
1259 break;
1260
1261 case MONTH: {
1262 // In Before Meiji and Meiji, January is the first month.
1263 if (eraIndex > MEIJI && jd.getYear() == 1) {
1264 long since = eras[eraIndex].getSince(getZone());
1265 CalendarDate d = jcal.getCalendarDate(since, getZone());
1266 value = d.getMonth() - 1;
1267 if (jd.getDayOfMonth() < d.getDayOfMonth()) {
1268 value++;
1269 }
1270 }
1271 }
1272 break;
1273
1274 case WEEK_OF_YEAR: {
1275 value = 1;
1276 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE,
1277 getZone());
1278 // shift 400 years to avoid underflow
1279 d.addYear(+400);
1280 jcal.normalize(d);
1281 jd.setEra(d.getEra());
1282 jd.setYear(d.getYear());
1283 jcal.normalize(jd);
1284
1285 long jan1 = jcal.getFixedDate(d);
1286 long fd = jcal.getFixedDate(jd);
1287 int woy = getWeekNumber(jan1, fd);
1288 long day1 = fd - (7 * (woy - 1));
1289 if ((day1 < jan1)
1290 || (day1 == jan1 && jd.getTimeOfDay() < d
1291 .getTimeOfDay())) {
1292 value++;
1293 }
1294 }
1295 break;
1296 }
1297 return value;
1298 }
1299
1300 /**
1301 * Returns the maximum value that this calendar field could have,
1302 * taking into consideration the given time value and the current
1303 * values of the
1304 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1305 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1306 * and
1307 * {@link Calendar#getTimeZone() getTimeZone} methods.
1308 * For example, if the date of this instance is Heisei 16February 1,
1309 * the actual maximum value of the <code>DAY_OF_MONTH</code> field
1310 * is 29 because Heisei 16 is a leap year, and if the date of this
1311 * instance is Heisei 17 February 1, it's 28.
1312 *
1313 * @param field the calendar field
1314 * @return the maximum of the given field for the time value of
1315 * this <code>JapaneseImperialCalendar</code>
1316 * @see #getMinimum(int)
1317 * @see #getMaximum(int)
1318 * @see #getGreatestMinimum(int)
1319 * @see #getLeastMaximum(int)
1320 * @see #getActualMinimum(int)
1321 */
1322 public int getActualMaximum(int field) {
1323 final int fieldsForFixedMax = ERA_MASK | DAY_OF_WEEK_MASK
1324 | HOUR_MASK | AM_PM_MASK | HOUR_OF_DAY_MASK
1325 | MINUTE_MASK | SECOND_MASK | MILLISECOND_MASK
1326 | ZONE_OFFSET_MASK | DST_OFFSET_MASK;
1327 if ((fieldsForFixedMax & (1 << field)) != 0) {
1328 return getMaximum(field);
1329 }
1330
1331 JapaneseImperialCalendar jc = getNormalizedCalendar();
1332 LocalGregorianCalendar.Date date = jc.jdate;
1333 int normalizedYear = date.getNormalizedYear();
1334
1335 int value = -1;
1336 switch (field) {
1337 case MONTH: {
1338 value = DECEMBER;
1339 if (isTransitionYear(date.getNormalizedYear())) {
1340 // TODO: there may be multiple transitions in a year.
1341 int eraIndex = getEraIndex(date);
1342 if (date.getYear() != 1) {
1343 eraIndex++;
1344 assert eraIndex < eras.length;
1345 }
1346 long transition = sinceFixedDates[eraIndex];
1347 long fd = jc.cachedFixedDate;
1348 if (fd < transition) {
1349 LocalGregorianCalendar.Date ldate = (LocalGregorianCalendar.Date) date
1350 .clone();
1351 jcal.getCalendarDateFromFixedDate(ldate,
1352 transition - 1);
1353 value = ldate.getMonth() - 1;
1354 }
1355 } else {
1356 LocalGregorianCalendar.Date d = jcal.getCalendarDate(
1357 Long.MAX_VALUE, getZone());
1358 if (date.getEra() == d.getEra()
1359 && date.getYear() == d.getYear()) {
1360 value = d.getMonth() - 1;
1361 }
1362 }
1363 }
1364 break;
1365
1366 case DAY_OF_MONTH:
1367 value = jcal.getMonthLength(date);
1368 break;
1369
1370 case DAY_OF_YEAR: {
1371 if (isTransitionYear(date.getNormalizedYear())) {
1372 // Handle transition year.
1373 // TODO: there may be multiple transitions in a year.
1374 int eraIndex = getEraIndex(date);
1375 if (date.getYear() != 1) {
1376 eraIndex++;
1377 assert eraIndex < eras.length;
1378 }
1379 long transition = sinceFixedDates[eraIndex];
1380 long fd = jc.cachedFixedDate;
1381 CalendarDate d = gcal
1382 .newCalendarDate(TimeZone.NO_TIMEZONE);
1383 d.setDate(date.getNormalizedYear(),
1384 BaseCalendar.JANUARY, 1);
1385 if (fd < transition) {
1386 value = (int) (transition - gcal.getFixedDate(d));
1387 } else {
1388 d.addYear(+1);
1389 value = (int) (gcal.getFixedDate(d) - transition);
1390 }
1391 } else {
1392 LocalGregorianCalendar.Date d = jcal.getCalendarDate(
1393 Long.MAX_VALUE, getZone());
1394 if (date.getEra() == d.getEra()
1395 && date.getYear() == d.getYear()) {
1396 long fd = jcal.getFixedDate(d);
1397 long jan1 = getFixedDateJan1(d, fd);
1398 value = (int) (fd - jan1) + 1;
1399 } else if (date.getYear() == getMinimum(YEAR)) {
1400 CalendarDate d1 = jcal.getCalendarDate(
1401 Long.MIN_VALUE, getZone());
1402 long fd1 = jcal.getFixedDate(d1);
1403 d1.addYear(1);
1404 d1.setMonth(BaseCalendar.JANUARY).setDayOfMonth(1);
1405 jcal.normalize(d1);
1406 long fd2 = jcal.getFixedDate(d1);
1407 value = (int) (fd2 - fd1);
1408 } else {
1409 value = jcal.getYearLength(date);
1410 }
1411 }
1412 }
1413 break;
1414
1415 case WEEK_OF_YEAR: {
1416 if (!isTransitionYear(date.getNormalizedYear())) {
1417 LocalGregorianCalendar.Date jd = jcal.getCalendarDate(
1418 Long.MAX_VALUE, getZone());
1419 if (date.getEra() == jd.getEra()
1420 && date.getYear() == jd.getYear()) {
1421 long fd = jcal.getFixedDate(jd);
1422 long jan1 = getFixedDateJan1(jd, fd);
1423 value = getWeekNumber(jan1, fd);
1424 } else if (date.getEra() == null
1425 && date.getYear() == getMinimum(YEAR)) {
1426 CalendarDate d = jcal.getCalendarDate(
1427 Long.MIN_VALUE, getZone());
1428 // shift 400 years to avoid underflow
1429 d.addYear(+400);
1430 jcal.normalize(d);
1431 jd.setEra(d.getEra());
1432 jd
1433 .setDate(d.getYear() + 1,
1434 BaseCalendar.JANUARY, 1);
1435 jcal.normalize(jd);
1436 long jan1 = jcal.getFixedDate(d);
1437 long nextJan1 = jcal.getFixedDate(jd);
1438 long nextJan1st = jcal.getDayOfWeekDateOnOrBefore(
1439 nextJan1 + 6, getFirstDayOfWeek());
1440 int ndays = (int) (nextJan1st - nextJan1);
1441 if (ndays >= getMinimalDaysInFirstWeek()) {
1442 nextJan1st -= 7;
1443 }
1444 value = getWeekNumber(jan1, nextJan1st);
1445 } else {
1446 // Get the day of week of January 1 of the year
1447 CalendarDate d = gcal
1448 .newCalendarDate(TimeZone.NO_TIMEZONE);
1449 d.setDate(date.getNormalizedYear(),
1450 BaseCalendar.JANUARY, 1);
1451 int dayOfWeek = gcal.getDayOfWeek(d);
1452 // Normalize the day of week with the firstDayOfWeek value
1453 dayOfWeek -= getFirstDayOfWeek();
1454 if (dayOfWeek < 0) {
1455 dayOfWeek += 7;
1456 }
1457 value = 52;
1458 int magic = dayOfWeek + getMinimalDaysInFirstWeek()
1459 - 1;
1460 if ((magic == 6)
1461 || (date.isLeapYear() && (magic == 5 || magic == 12))) {
1462 value++;
1463 }
1464 }
1465 break;
1466 }
1467
1468 if (jc == this ) {
1469 jc = (JapaneseImperialCalendar) jc.clone();
1470 }
1471 int max = getActualMaximum(DAY_OF_YEAR);
1472 jc.set(DAY_OF_YEAR, max);
1473 value = jc.get(WEEK_OF_YEAR);
1474 if (value == 1 && max > 7) {
1475 jc.add(WEEK_OF_YEAR, -1);
1476 value = jc.get(WEEK_OF_YEAR);
1477 }
1478 }
1479 break;
1480
1481 case WEEK_OF_MONTH: {
1482 LocalGregorianCalendar.Date jd = jcal.getCalendarDate(
1483 Long.MAX_VALUE, getZone());
1484 if (!(date.getEra() == jd.getEra() && date.getYear() == jd
1485 .getYear())) {
1486 CalendarDate d = gcal
1487 .newCalendarDate(TimeZone.NO_TIMEZONE);
1488 d.setDate(date.getNormalizedYear(), date.getMonth(), 1);
1489 int dayOfWeek = gcal.getDayOfWeek(d);
1490 int monthLength = gcal.getMonthLength(d);
1491 dayOfWeek -= getFirstDayOfWeek();
1492 if (dayOfWeek < 0) {
1493 dayOfWeek += 7;
1494 }
1495 int nDaysFirstWeek = 7 - dayOfWeek; // # of days in the first week
1496 value = 3;
1497 if (nDaysFirstWeek >= getMinimalDaysInFirstWeek()) {
1498 value++;
1499 }
1500 monthLength -= nDaysFirstWeek + 7 * 3;
1501 if (monthLength > 0) {
1502 value++;
1503 if (monthLength > 7) {
1504 value++;
1505 }
1506 }
1507 } else {
1508 long fd = jcal.getFixedDate(jd);
1509 long month1 = fd - jd.getDayOfMonth() + 1;
1510 value = getWeekNumber(month1, fd);
1511 }
1512 }
1513 break;
1514
1515 case DAY_OF_WEEK_IN_MONTH: {
1516 int ndays, dow1;
1517 int dow = date.getDayOfWeek();
1518 BaseCalendar.Date d = (BaseCalendar.Date) date.clone();
1519 ndays = jcal.getMonthLength(d);
1520 d.setDayOfMonth(1);
1521 jcal.normalize(d);
1522 dow1 = d.getDayOfWeek();
1523 int x = dow - dow1;
1524 if (x < 0) {
1525 x += 7;
1526 }
1527 ndays -= x;
1528 value = (ndays + 6) / 7;
1529 }
1530 break;
1531
1532 case YEAR: {
1533 CalendarDate jd = jcal.getCalendarDate(
1534 jc.getTimeInMillis(), getZone());
1535 CalendarDate d;
1536 int eraIndex = getEraIndex(date);
1537 if (eraIndex == eras.length - 1) {
1538 d = jcal.getCalendarDate(Long.MAX_VALUE, getZone());
1539 value = d.getYear();
1540 // Use an equivalent year for the
1541 // getYearOffsetInMillis call to avoid overflow.
1542 if (value > 400) {
1543 jd.setYear(value - 400);
1544 }
1545 } else {
1546 d = jcal.getCalendarDate(eras[eraIndex + 1]
1547 .getSince(getZone()) - 1, getZone());
1548 value = d.getYear();
1549 // Use the same year as d.getYear() to be
1550 // consistent with leap and common years.
1551 jd.setYear(value);
1552 }
1553 jcal.normalize(jd);
1554 if (getYearOffsetInMillis(jd) > getYearOffsetInMillis(d)) {
1555 value--;
1556 }
1557 }
1558 break;
1559
1560 default:
1561 throw new ArrayIndexOutOfBoundsException(field);
1562 }
1563 return value;
1564 }
1565
1566 /**
1567 * Returns the millisecond offset from the beginning of the
1568 * year. In the year for Long.MIN_VALUE, it's a pseudo value
1569 * beyond the limit. The given CalendarDate object must have been
1570 * normalized before calling this method.
1571 */
1572 private final long getYearOffsetInMillis(CalendarDate date) {
1573 long t = (jcal.getDayOfYear(date) - 1) * ONE_DAY;
1574 return t + date.getTimeOfDay() - date.getZoneOffset();
1575 }
1576
1577 public Object clone() {
1578 JapaneseImperialCalendar other = (JapaneseImperialCalendar) super
1579 .clone();
1580
1581 other.jdate = (LocalGregorianCalendar.Date) jdate.clone();
1582 other.originalFields = null;
1583 other.zoneOffsets = null;
1584 return other;
1585 }
1586
1587 public TimeZone getTimeZone() {
1588 TimeZone zone = super .getTimeZone();
1589 // To share the zone by the CalendarDate
1590 jdate.setZone(zone);
1591 return zone;
1592 }
1593
1594 public void setTimeZone(TimeZone zone) {
1595 super .setTimeZone(zone);
1596 // To share the zone by the CalendarDate
1597 jdate.setZone(zone);
1598 }
1599
1600 /**
1601 * The fixed date corresponding to jdate. If the value is
1602 * Long.MIN_VALUE, the fixed date value is unknown.
1603 */
1604 transient private long cachedFixedDate = Long.MIN_VALUE;
1605
1606 /**
1607 * Converts the time value (millisecond offset from the <a
1608 * href="Calendar.html#Epoch">Epoch</a>) to calendar field values.
1609 * The time is <em>not</em>
1610 * recomputed first; to recompute the time, then the fields, call the
1611 * <code>complete</code> method.
1612 *
1613 * @see Calendar#complete
1614 */
1615 protected void computeFields() {
1616 int mask = 0;
1617 if (isPartiallyNormalized()) {
1618 // Determine which calendar fields need to be computed.
1619 mask = getSetStateFields();
1620 int fieldMask = ~mask & ALL_FIELDS;
1621 if (fieldMask != 0 || cachedFixedDate == Long.MIN_VALUE) {
1622 mask |= computeFields(fieldMask, mask
1623 & (ZONE_OFFSET_MASK | DST_OFFSET_MASK));
1624 assert mask == ALL_FIELDS;
1625 }
1626 } else {
1627 // Specify all fields
1628 mask = ALL_FIELDS;
1629 computeFields(mask, 0);
1630 }
1631 // After computing all the fields, set the field state to `COMPUTED'.
1632 setFieldsComputed(mask);
1633 }
1634
1635 /**
1636 * This computeFields implements the conversion from UTC
1637 * (millisecond offset from the Epoch) to calendar
1638 * field values. fieldMask specifies which fields to change the
1639 * setting state to COMPUTED, although all fields are set to
1640 * the correct values. This is required to fix 4685354.
1641 *
1642 * @param fieldMask a bit mask to specify which fields to change
1643 * the setting state.
1644 * @param tzMask a bit mask to specify which time zone offset
1645 * fields to be used for time calculations
1646 * @return a new field mask that indicates what field values have
1647 * actually been set.
1648 */
1649 private int computeFields(int fieldMask, int tzMask) {
1650 int zoneOffset = 0;
1651 TimeZone tz = getZone();
1652 if (zoneOffsets == null) {
1653 zoneOffsets = new int[2];
1654 }
1655 if (tzMask != (ZONE_OFFSET_MASK | DST_OFFSET_MASK)) {
1656 if (tz instanceof ZoneInfo) {
1657 zoneOffset = ((ZoneInfo) tz).getOffsets(time,
1658 zoneOffsets);
1659 } else {
1660 zoneOffset = tz.getOffset(time);
1661 zoneOffsets[0] = tz.getRawOffset();
1662 zoneOffsets[1] = zoneOffset - zoneOffsets[0];
1663 }
1664 }
1665 if (tzMask != 0) {
1666 if (isFieldSet(tzMask, ZONE_OFFSET)) {
1667 zoneOffsets[0] = internalGet(ZONE_OFFSET);
1668 }
1669 if (isFieldSet(tzMask, DST_OFFSET)) {
1670 zoneOffsets[1] = internalGet(DST_OFFSET);
1671 }
1672 zoneOffset = zoneOffsets[0] + zoneOffsets[1];
1673 }
1674
1675 // By computing time and zoneOffset separately, we can take
1676 // the wider range of time+zoneOffset than the previous
1677 // implementation.
1678 long fixedDate = zoneOffset / ONE_DAY;
1679 int timeOfDay = zoneOffset % (int) ONE_DAY;
1680 fixedDate += time / ONE_DAY;
1681 timeOfDay += (int) (time % ONE_DAY);
1682 if (timeOfDay >= ONE_DAY) {
1683 timeOfDay -= ONE_DAY;
1684 ++fixedDate;
1685 } else {
1686 while (timeOfDay < 0) {
1687 timeOfDay += ONE_DAY;
1688 --fixedDate;
1689 }
1690 }
1691 fixedDate += EPOCH_OFFSET;
1692
1693 // See if we can use jdate to avoid date calculation.
1694 if (fixedDate != cachedFixedDate || fixedDate < 0) {
1695 jcal.getCalendarDateFromFixedDate(jdate, fixedDate);
1696 cachedFixedDate = fixedDate;
1697 }
1698 int era = getEraIndex(jdate);
1699 int year = jdate.getYear();
1700
1701 // Always set the ERA and YEAR values.
1702 internalSet(ERA, era);
1703 internalSet(YEAR, year);
1704 int mask = fieldMask | (ERA_MASK | YEAR_MASK);
1705
1706 int month = jdate.getMonth() - 1; // 0-based
1707 int dayOfMonth = jdate.getDayOfMonth();
1708
1709 // Set the basic date fields.
1710 if ((fieldMask & (MONTH_MASK | DAY_OF_MONTH_MASK | DAY_OF_WEEK_MASK)) != 0) {
1711 internalSet(MONTH, month);
1712 internalSet(DAY_OF_MONTH, dayOfMonth);
1713 internalSet(DAY_OF_WEEK, jdate.getDayOfWeek());
1714 mask |= MONTH_MASK | DAY_OF_MONTH_MASK | DAY_OF_WEEK_MASK;
1715 }
1716
1717 if ((fieldMask & (HOUR_OF_DAY_MASK | AM_PM_MASK | HOUR_MASK
1718 | MINUTE_MASK | SECOND_MASK | MILLISECOND_MASK)) != 0) {
1719 if (timeOfDay != 0) {
1720 int hours = timeOfDay / ONE_HOUR;
1721 internalSet(HOUR_OF_DAY, hours);
1722 internalSet(AM_PM, hours / 12); // Assume AM == 0
1723 internalSet(HOUR, hours % 12);
1724 int r = timeOfDay % ONE_HOUR;
1725 internalSet(MINUTE, r / ONE_MINUTE);
1726 r %= ONE_MINUTE;
1727 internalSet(SECOND, r / ONE_SECOND);
1728 internalSet(MILLISECOND, r % ONE_SECOND);
1729 } else {
1730 internalSet(HOUR_OF_DAY, 0);
1731 internalSet(AM_PM, AM);
1732 internalSet(HOUR, 0);
1733 internalSet(MINUTE, 0);
1734 internalSet(SECOND, 0);
1735 internalSet(MILLISECOND, 0);
1736 }
1737 mask |= (HOUR_OF_DAY_MASK | AM_PM_MASK | HOUR_MASK
1738 | MINUTE_MASK | SECOND_MASK | MILLISECOND_MASK);
1739 }
1740
1741 if ((fieldMask & (ZONE_OFFSET_MASK | DST_OFFSET_MASK)) != 0) {
1742 internalSet(ZONE_OFFSET, zoneOffsets[0]);
1743 internalSet(DST_OFFSET, zoneOffsets[1]);
1744 mask |= (ZONE_OFFSET_MASK | DST_OFFSET_MASK);
1745 }
1746
1747 if ((fieldMask & (DAY_OF_YEAR_MASK | WEEK_OF_YEAR_MASK
1748 | WEEK_OF_MONTH_MASK | DAY_OF_WEEK_IN_MONTH_MASK)) != 0) {
1749 int normalizedYear = jdate.getNormalizedYear();
1750 // If it's a year of an era transition, we need to handle
1751 // irregular year boundaries.
1752 boolean transitionYear = isTransitionYear(jdate
1753 .getNormalizedYear());
1754 int dayOfYear;
1755 long fixedDateJan1;
1756 if (transitionYear) {
1757 fixedDateJan1 = getFixedDateJan1(jdate, fixedDate);
1758 dayOfYear = (int) (fixedDate - fixedDateJan1) + 1;
1759 } else if (normalizedYear == MIN_VALUES[YEAR]) {
1760 CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE,
1761 getZone());
1762 fixedDateJan1 = jcal.getFixedDate(dx);
1763 dayOfYear = (int) (fixedDate - fixedDateJan1) + 1;
1764 } else {
1765 dayOfYear = (int) jcal.getDayOfYear(jdate);
1766 fixedDateJan1 = fixedDate - dayOfYear + 1;
1767 }
1768 long fixedDateMonth1 = transitionYear ? getFixedDateMonth1(
1769 jdate, fixedDate) : fixedDate - dayOfMonth + 1;
1770
1771 internalSet(DAY_OF_YEAR, dayOfYear);
1772 internalSet(DAY_OF_WEEK_IN_MONTH, (dayOfMonth - 1) / 7 + 1);
1773
1774 int weekOfYear = getWeekNumber(fixedDateJan1, fixedDate);
1775
1776 // The spec is to calculate WEEK_OF_YEAR in the
1777 // ISO8601-style. This creates problems, though.
1778 if (weekOfYear == 0) {
1779 // If the date belongs to the last week of the
1780 // previous year, use the week number of "12/31" of
1781 // the "previous" year. Again, if the previous year is
1782 // a transition year, we need to take care of it.
1783 // Usually the previous day of the first day of a year
1784 // is December 31, which is not always true in the
1785 // Japanese imperial calendar system.
1786 long fixedDec31 = fixedDateJan1 - 1;
1787 long prevJan1;
1788 LocalGregorianCalendar.Date d = getCalendarDate(fixedDec31);
1789 if (!(transitionYear || isTransitionYear(d
1790 .getNormalizedYear()))) {
1791 prevJan1 = fixedDateJan1 - 365;
1792 if (d.isLeapYear()) {
1793 --prevJan1;
1794 }
1795 } else if (transitionYear) {
1796 if (jdate.getYear() == 1) {
1797 // As of Heisei (since Meiji) there's no case
1798 // that there are multiple transitions in a
1799 // year. Historically there was such
1800 // case. There might be such case again in the
1801 // future.
1802 if (era > HEISEI) {
1803 CalendarDate pd = eras[era - 1]
1804 .getSinceDate();
1805 if (normalizedYear == pd.getYear()) {
1806 d.setMonth(pd.getMonth())
1807 .setDayOfMonth(
1808 pd.getDayOfMonth());
1809 }
1810 } else {
1811 d.setMonth(jcal.JANUARY).setDayOfMonth(1);
1812 }
1813 jcal.normalize(d);
1814 prevJan1 = jcal.getFixedDate(d);
1815 } else {
1816 prevJan1 = fixedDateJan1 - 365;
1817 if (d.isLeapYear()) {
1818 --prevJan1;
1819 }
1820 }
1821 } else {
1822 CalendarDate cd = eras[getEraIndex(jdate)]
1823 .getSinceDate();
1824 d.setMonth(cd.getMonth()).setDayOfMonth(
1825 cd.getDayOfMonth());
1826 jcal.normalize(d);
1827 prevJan1 = jcal.getFixedDate(d);
1828 }
1829 weekOfYear = getWeekNumber(prevJan1, fixedDec31);
1830 } else {
1831 if (!transitionYear) {
1832 // Regular years
1833 if (weekOfYear >= 52) {
1834 long nextJan1 = fixedDateJan1 + 365;
1835 if (jdate.isLeapYear()) {
1836 nextJan1++;
1837 }
1838 long nextJan1st = jcal
1839 .getDayOfWeekDateOnOrBefore(
1840 nextJan1 + 6,
1841 getFirstDayOfWeek());
1842 int ndays = (int) (nextJan1st - nextJan1);
1843 if (ndays >= getMinimalDaysInFirstWeek()
1844 && fixedDate >= (nextJan1st - 7)) {
1845 // The first days forms a week in which the date is included.
1846 weekOfYear = 1;
1847 }
1848 }
1849 } else {
1850 LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate
1851 .clone();
1852 long nextJan1;
1853 if (jdate.getYear() == 1) {
1854 d.addYear(+1);
1855 d.setMonth(jcal.JANUARY).setDayOfMonth(1);
1856 nextJan1 = jcal.getFixedDate(d);
1857 } else {
1858 int nextEraIndex = getEraIndex(d) + 1;
1859 CalendarDate cd = eras[nextEraIndex]
1860 .getSinceDate();
1861 d.setEra(eras[nextEraIndex]);
1862 d.setDate(1, cd.getMonth(), cd.getDayOfMonth());
1863 jcal.normalize(d);
1864 nextJan1 = jcal.getFixedDate(d);
1865 }
1866 long nextJan1st = jcal.getDayOfWeekDateOnOrBefore(
1867 nextJan1 + 6, getFirstDayOfWeek());
1868 int ndays = (int) (nextJan1st - nextJan1);
1869 if (ndays >= getMinimalDaysInFirstWeek()
1870 && fixedDate >= (nextJan1st - 7)) {
1871 // The first days forms a week in which the date is included.
1872 weekOfYear = 1;
1873 }
1874 }
1875 }
1876 internalSet(WEEK_OF_YEAR, weekOfYear);
1877 internalSet(WEEK_OF_MONTH, getWeekNumber(fixedDateMonth1,
1878 fixedDate));
1879 mask |= (DAY_OF_YEAR_MASK | WEEK_OF_YEAR_MASK
1880 | WEEK_OF_MONTH_MASK | DAY_OF_WEEK_IN_MONTH_MASK);
1881 }
1882 return mask;
1883 }
1884
1885 /**
1886 * Returns the number of weeks in a period between fixedDay1 and
1887 * fixedDate. The getFirstDayOfWeek-getMinimalDaysInFirstWeek rule
1888 * is applied to calculate the number of weeks.
1889 *
1890 * @param fixedDay1 the fixed date of the first day of the period
1891 * @param fixedDate the fixed date of the last day of the period
1892 * @return the number of weeks of the given period
1893 */
1894 private final int getWeekNumber(long fixedDay1, long fixedDate) {
1895 // We can always use `jcal' since Julian and Gregorian are the
1896 // same thing for this calculation.
1897 long fixedDay1st = jcal.getDayOfWeekDateOnOrBefore(
1898 fixedDay1 + 6, getFirstDayOfWeek());
1899 int ndays = (int) (fixedDay1st - fixedDay1);
1900 assert ndays <= 7;
1901 if (ndays >= getMinimalDaysInFirstWeek()) {
1902 fixedDay1st -= 7;
1903 }
1904 int normalizedDayOfPeriod = (int) (fixedDate - fixedDay1st);
1905 if (normalizedDayOfPeriod >= 0) {
1906 return normalizedDayOfPeriod / 7 + 1;
1907 }
1908 return CalendarUtils.floorDivide(normalizedDayOfPeriod, 7) + 1;
1909 }
1910
1911 /**
1912 * Converts calendar field values to the time value (millisecond
1913 * offset from the <a href="Calendar.html#Epoch">Epoch</a>).
1914 *
1915 * @exception IllegalArgumentException if any calendar fields are invalid.
1916 */
1917 protected void computeTime() {
1918 // In non-lenient mode, perform brief checking of calendar
1919 // fields which have been set externally. Through this
1920 // checking, the field values are stored in originalFields[]
1921 // to see if any of them are normalized later.
1922 if (!isLenient()) {
1923 if (originalFields == null) {
1924 originalFields = new int[FIELD_COUNT];
1925 }
1926 for (int field = 0; field < FIELD_COUNT; field++) {
1927 int value = internalGet(field);
1928 if (isExternallySet(field)) {
1929 // Quick validation for any out of range values
1930 if (value < getMinimum(field)
1931 || value > getMaximum(field)) {
1932 throw new IllegalArgumentException(
1933 getFieldName(field));
1934 }
1935 }
1936 originalFields[field] = value;
1937 }
1938 }
1939
1940 // Let the super class determine which calendar fields to be
1941 // used to calculate the time.
1942 int fieldMask = selectFields();
1943
1944 int year;
1945 int era;
1946
1947 if (isSet(ERA)) {
1948 era = internalGet(ERA);
1949 year = isSet(YEAR) ? internalGet(YEAR) : 1;
1950 } else {
1951 if (isSet(YEAR)) {
1952 era = eras.length - 1;
1953 year = internalGet(YEAR);
1954 } else {
1955 // Equivalent to 1970 (Gregorian)
1956 era = SHOWA;
1957 year = 45;
1958 }
1959 }
1960
1961 // Calculate the time of day. We rely on the convention that
1962 // an UNSET field has 0.
1963 long timeOfDay = 0;
1964 if (isFieldSet(fieldMask, HOUR_OF_DAY)) {
1965 timeOfDay += (long) internalGet(HOUR_OF_DAY);
1966 } else {
1967 timeOfDay += internalGet(HOUR);
1968 // The default value of AM_PM is 0 which designates AM.
1969 if (isFieldSet(fieldMask, AM_PM)) {
1970 timeOfDay += 12 * internalGet(AM_PM);
1971 }
1972 }
1973 timeOfDay *= 60;
1974 timeOfDay += internalGet(MINUTE);
1975 timeOfDay *= 60;
1976 timeOfDay += internalGet(SECOND);
1977 timeOfDay *= 1000;
1978 timeOfDay += internalGet(MILLISECOND);
1979
1980 // Convert the time of day to the number of days and the
1981 // millisecond offset from midnight.
1982 long fixedDate = timeOfDay / ONE_DAY;
1983 timeOfDay %= ONE_DAY;
1984 while (timeOfDay < 0) {
1985 timeOfDay += ONE_DAY;
1986 --fixedDate;
1987 }
1988
1989 // Calculate the fixed date since January 1, 1 (Gregorian).
1990 fixedDate += getFixedDate(era, year, fieldMask);
1991
1992 // millis represents local wall-clock time in milliseconds.
1993 long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
1994
1995 // Compute the time zone offset and DST offset. There are two potential
1996 // ambiguities here. We'll assume a 2:00 am (wall time) switchover time
1997 // for discussion purposes here.
1998 // 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
1999 // can be in standard or in DST depending. However, 2:00 am is an invalid
2000 // representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
2001 // We assume standard time.
2002 // 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
2003 // can be in standard or DST. Both are valid representations (the rep
2004 // jumps from 1:59:59 DST to 1:00:00 Std).
2005 // Again, we assume standard time.
2006 // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
2007 // or DST_OFFSET fields; then we use those fields.
2008 TimeZone zone = getZone();
2009 if (zoneOffsets == null) {
2010 zoneOffsets = new int[2];
2011 }
2012 int tzMask = fieldMask & (ZONE_OFFSET_MASK | DST_OFFSET_MASK);
2013 if (tzMask != (ZONE_OFFSET_MASK | DST_OFFSET_MASK)) {
2014 if (zone instanceof ZoneInfo) {
2015 ((ZoneInfo) zone).getOffsetsByWall(millis, zoneOffsets);
2016 } else {
2017 zone.getOffsets(millis - zone.getRawOffset(),
2018 zoneOffsets);
2019 }
2020 }
2021 if (tzMask != 0) {
2022 if (isFieldSet(tzMask, ZONE_OFFSET)) {
2023 zoneOffsets[0] = internalGet(ZONE_OFFSET);
2024 }
2025 if (isFieldSet(tzMask, DST_OFFSET)) {
2026 zoneOffsets[1] = internalGet(DST_OFFSET);
2027 }
2028 }
2029
2030 // Adjust the time zone offset values to get the UTC time.
2031 millis -= zoneOffsets[0] + zoneOffsets[1];
2032
2033 // Set this calendar's time in milliseconds
2034 time = millis;
2035
2036 int mask = computeFields(fieldMask | getSetStateFields(),
2037 tzMask);
2038
2039 if (!isLenient()) {
2040 for (int field = 0; field < FIELD_COUNT; field++) {
2041 if (!isExternallySet(field)) {
2042 continue;
2043 }
2044 if (originalFields[field] != internalGet(field)) {
2045 int wrongValue = internalGet(field);
2046 // Restore the original field values
2047 System.arraycopy(originalFields, 0, fields, 0,
2048 fields.length);
2049 throw new IllegalArgumentException(
2050 getFieldName(field) + "=" + wrongValue
2051 + ", expected "
2052 + originalFields[field]);
2053 }
2054 }
2055 }
2056 setFieldsNormalized(mask);
2057 }
2058
2059 /**
2060 * Computes the fixed date under either the Gregorian or the
2061 * Julian calendar, using the given year and the specified calendar fields.
2062 *
2063 * @param cal the CalendarSystem to be used for the date calculation
2064 * @param year the normalized year number, with 0 indicating the
2065 * year 1 BCE, -1 indicating 2 BCE, etc.
2066 * @param fieldMask the calendar fields to be used for the date calculation
2067 * @return the fixed date
2068 * @see Calendar#selectFields
2069 */
2070 private long getFixedDate(int era, int year, int fieldMask) {
2071 int month = JANUARY;
2072 int firstDayOfMonth = 1;
2073 if (isFieldSet(fieldMask, MONTH)) {
2074 // No need to check if MONTH has been set (no isSet(MONTH)
2075 // call) since its unset value happens to be JANUARY (0).
2076 month = internalGet(MONTH);
2077
2078 // If the month is out of range, adjust it into range.
2079 if (month > DECEMBER) {
2080 year += month / 12;
2081 month %= 12;
2082 } else if (month < JANUARY) {
2083 int[] rem = new int[1];
2084 year += CalendarUtils.floorDivide(month, 12, rem);
2085 month = rem[0];
2086 }
2087 } else {
2088 if (year == 1 && era != 0) {
2089 CalendarDate d = eras[era].getSinceDate();
2090 month = d.getMonth() - 1;
2091 firstDayOfMonth = d.getDayOfMonth();
2092 }
2093 }
2094
2095 // Adjust the base date if year is the minimum value.
2096 if (year == MIN_VALUES[YEAR]) {
2097 CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE,
2098 getZone());
2099 int m = dx.getMonth() - 1;
2100 if (month < m)
2101 month = m;
2102 if (month == m)
2103 firstDayOfMonth = dx.getDayOfMonth();
2104 }
2105
2106 LocalGregorianCalendar.Date date = jcal
2107 .newCalendarDate(TimeZone.NO_TIMEZONE);
2108 date.setEra(era > 0 ? eras[era] : null);
2109 date.setDate(year, month + 1, firstDayOfMonth);
2110 jcal.normalize(date);
2111
2112 // Get the fixed date since Jan 1, 1 (Gregorian). We are on
2113 // the first day of either `month' or January in 'year'.
2114 long fixedDate = jcal.getFixedDate(date);
2115
2116 if (isFieldSet(fieldMask, MONTH)) {
2117 // Month-based calculations
2118 if (isFieldSet(fieldMask, DAY_OF_MONTH)) {
2119 // We are on the "first day" of the month (which may
2120 // not be 1). Just add the offset if DAY_OF_MONTH is
2121 // set. If the isSet call returns false, that means
2122 // DAY_OF_MONTH has been selected just because of the
2123 // selected combination. We don't need to add any
2124 // since the default value is the "first day".
2125 if (isSet(DAY_OF_MONTH)) {
2126 // To avoid underflow with DAY_OF_MONTH-firstDayOfMonth, add
2127 // DAY_OF_MONTH, then subtract firstDayOfMonth.
2128 fixedDate += internalGet(DAY_OF_MONTH);
2129 fixedDate -= firstDayOfMonth;
2130 }
2131 } else {
2132 if (isFieldSet(fieldMask, WEEK_OF_MONTH)) {
2133 long firstDayOfWeek = jcal
2134 .getDayOfWeekDateOnOrBefore(fixedDate + 6,
2135 getFirstDayOfWeek());
2136 // If we have enough days in the first week, then
2137 // move to the previous week.
2138 if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
2139 firstDayOfWeek -= 7;
2140 }
2141 if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2142 firstDayOfWeek = jcal
2143 .getDayOfWeekDateOnOrBefore(
2144 firstDayOfWeek + 6,
2145 internalGet(DAY_OF_WEEK));
2146 }
2147 // In lenient mode, we treat days of the previous
2148 // months as a part of the specified
2149 // WEEK_OF_MONTH. See 4633646.
2150 fixedDate = firstDayOfWeek + 7
2151 * (internalGet(WEEK_OF_MONTH) - 1);
2152 } else {
2153 int dayOfWeek;
2154 if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2155 dayOfWeek = internalGet(DAY_OF_WEEK);
2156 } else {
2157 dayOfWeek = getFirstDayOfWeek();
2158 }
2159 // We are basing this on the day-of-week-in-month. The only
2160 // trickiness occurs if the day-of-week-in-month is
2161 // negative.
2162 int dowim;
2163 if (isFieldSet(fieldMask, DAY_OF_WEEK_IN_MONTH)) {
2164 dowim = internalGet(DAY_OF_WEEK_IN_MONTH);
2165 } else {
2166 dowim = 1;
2167 }
2168 if (dowim >= 0) {
2169 fixedDate = jcal.getDayOfWeekDateOnOrBefore(
2170 fixedDate + (7 * dowim) - 1, dayOfWeek);
2171 } else {
2172 // Go to the first day of the next week of
2173 // the specified week boundary.
2174 int lastDate = monthLength(month, year)
2175 + (7 * (dowim + 1));
2176 // Then, get the day of week date on or before the last date.
2177 fixedDate = jcal.getDayOfWeekDateOnOrBefore(
2178 fixedDate + lastDate - 1, dayOfWeek);
2179 }
2180 }
2181 }
2182 } else {
2183 // We are on the first day of the year.
2184 if (isFieldSet(fieldMask, DAY_OF_YEAR)) {
2185 if (isTransitionYear(date.getNormalizedYear())) {
2186 fixedDate = getFixedDateJan1(date, fixedDate);
2187 }
2188 // Add the offset, then subtract 1. (Make sure to avoid underflow.)
2189 fixedDate += internalGet(DAY_OF_YEAR);
2190 fixedDate--;
2191 } else {
2192 long firstDayOfWeek = jcal.getDayOfWeekDateOnOrBefore(
2193 fixedDate + 6, getFirstDayOfWeek());
2194 // If we have enough days in the first week, then move
2195 // to the previous week.
2196 if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
2197 firstDayOfWeek -= 7;
2198 }
2199 if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2200 int dayOfWeek = internalGet(DAY_OF_WEEK);
2201 if (dayOfWeek != getFirstDayOfWeek()) {
2202 firstDayOfWeek = jcal
2203 .getDayOfWeekDateOnOrBefore(
2204 firstDayOfWeek + 6, dayOfWeek);
2205 }
2206 }
2207 fixedDate = firstDayOfWeek + 7
2208 * ((long) internalGet(WEEK_OF_YEAR) - 1);
2209 }
2210 }
2211 return fixedDate;
2212 }
2213
2214 /**
2215 * Returns the fixed date of the first day of the year (usually
2216 * January 1) before the specified date.
2217 *
2218 * @param date the date for which the first day of the year is
2219 * calculated. The date has to be in the cut-over year.
2220 * @param fixedDate the fixed date representation of the date
2221 */
2222 private final long getFixedDateJan1(
2223 LocalGregorianCalendar.Date date, long fixedDate) {
2224 Era era = date.getEra();
2225 if (date.getEra() != null && date.getYear() == 1) {
2226 for (int eraIndex = getEraIndex(date); eraIndex > 0; eraIndex--) {
2227 CalendarDate d = eras[eraIndex].getSinceDate();
2228 long fd = gcal.getFixedDate(d);
2229 // There might be multiple era transitions in a year.
2230 if (fd > fixedDate) {
2231 continue;
2232 }
2233 return fd;
2234 }
2235 }
2236 CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
2237 d.setDate(date.getNormalizedYear(), gcal.JANUARY, 1);
2238 return gcal.getFixedDate(d);
2239 }
2240
2241 /**
2242 * Returns the fixed date of the first date of the month (usually
2243 * the 1st of the month) before the specified date.
2244 *
2245 * @param date the date for which the first day of the month is
2246 * calculated. The date must be in the era transition year.
2247 * @param fixedDate the fixed date representation of the date
2248 */
2249 private final long getFixedDateMonth1(
2250 LocalGregorianCalendar.Date date, long fixedDate) {
2251 int eraIndex = getTransitionEraIndex(date);
2252 if (eraIndex != -1) {
2253 long transition = sinceFixedDates[eraIndex];
2254 // If the given date is on or after the transition date, then
2255 // return the transition date.
2256 if (transition <= fixedDate) {
2257 return transition;
2258 }
2259 }
2260
2261 // Otherwise, we can use the 1st day of the month.
2262 return fixedDate - date.getDayOfMonth() + 1;
2263 }
2264
2265 /**
2266 * Returns a LocalGregorianCalendar.Date produced from the specified fixed date.
2267 *
2268 * @param fd the fixed date
2269 */
2270 private static final LocalGregorianCalendar.Date getCalendarDate(
2271 long fd) {
2272 LocalGregorianCalendar.Date d = jcal
2273 .newCalendarDate(TimeZone.NO_TIMEZONE);
2274 jcal.getCalendarDateFromFixedDate(d, fd);
2275 return d;
2276 }
2277
2278 /**
2279 * Returns the length of the specified month in the specified
2280 * Gregorian year. The year number must be normalized.
2281 *
2282 * @see #isLeapYear(int)
2283 */
2284 private final int monthLength(int month, int gregorianYear) {
2285 return CalendarUtils.isGregorianLeapYear(gregorianYear) ? GregorianCalendar.LEAP_MONTH_LENGTH[month]
2286 : GregorianCalendar.MONTH_LENGTH[month];
2287 }
2288
2289 /**
2290 * Returns the length of the specified month in the year provided
2291 * by internalGet(YEAR).
2292 *
2293 * @see #isLeapYear(int)
2294 */
2295 private final int monthLength(int month) {
2296 assert jdate.isNormalized();
2297 return jdate.isLeapYear() ? GregorianCalendar.LEAP_MONTH_LENGTH[month]
2298 : GregorianCalendar.MONTH_LENGTH[month];
2299 }
2300
2301 private final int actualMonthLength() {
2302 int length = jcal.getMonthLength(jdate);
2303 int eraIndex = getTransitionEraIndex(jdate);
2304 if (eraIndex == -1) {
2305 long transitionFixedDate = sinceFixedDates[eraIndex];
2306 CalendarDate d = eras[eraIndex].getSinceDate();
2307 if (transitionFixedDate <= cachedFixedDate) {
2308 length -= d.getDayOfMonth() - 1;
2309 } else {
2310 length = d.getDayOfMonth() - 1;
2311 }
2312 }
2313 return length;
2314 }
2315
2316 /**
2317 * Returns the index to the new era if the given date is in a
2318 * transition month. For example, if the give date is Heisei 1
2319 * (1989) January 20, then the era index for Heisei is
2320 * returned. Likewise, if the given date is Showa 64 (1989)
2321 * January 3, then the era index for Heisei is returned. If the
2322 * given date is not in any transition month, then -1 is returned.
2323 */
2324 private static final int getTransitionEraIndex(
2325 LocalGregorianCalendar.Date date) {
2326 int eraIndex = getEraIndex(date);
2327 CalendarDate transitionDate = eras[eraIndex].getSinceDate();
2328 if (transitionDate.getYear() == date.getNormalizedYear()
2329 && transitionDate.getMonth() == date.getMonth()) {
2330 return eraIndex;
2331 }
2332 if (eraIndex < eras.length - 1) {
2333 transitionDate = eras[++eraIndex].getSinceDate();
2334 if (transitionDate.getYear() == date.getNormalizedYear()
2335 && transitionDate.getMonth() == date.getMonth()) {
2336 return eraIndex;
2337 }
2338 }
2339 return -1;
2340 }
2341
2342 private final boolean isTransitionYear(int normalizedYear) {
2343 for (int i = eras.length - 1; i > 0; i--) {
2344 int transitionYear = eras[i].getSinceDate().getYear();
2345 if (normalizedYear == transitionYear) {
2346 return true;
2347 }
2348 if (normalizedYear > transitionYear) {
2349 break;
2350 }
2351 }
2352 return false;
2353 }
2354
2355 private static final int getEraIndex(
2356 LocalGregorianCalendar.Date date) {
2357 Era era = date.getEra();
2358 for (int i = eras.length - 1; i > 0; i--) {
2359 if (eras[i] == era) {
2360 return i;
2361 }
2362 }
2363 return 0;
2364 }
2365
2366 /**
2367 * Returns this object if it's normalized (all fields and time are
2368 * in sync). Otherwise, a cloned object is returned after calling
2369 * complete() in lenient mode.
2370 */
2371 private final JapaneseImperialCalendar getNormalizedCalendar() {
2372 JapaneseImperialCalendar jc;
2373 if (isFullyNormalized()) {
2374 jc = this ;
2375 } else {
2376 // Create a clone and normalize the calendar fields
2377 jc = (JapaneseImperialCalendar) this .clone();
2378 jc.setLenient(true);
2379 jc.complete();
2380 }
2381 return jc;
2382 }
2383
2384 /**
2385 * After adjustments such as add(MONTH), add(YEAR), we don't want the
2386 * month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar
2387 * 3, we want it to go to Feb 28. Adjustments which might run into this
2388 * problem call this method to retain the proper month.
2389 */
2390 private final void pinDayOfMonth(LocalGregorianCalendar.Date date) {
2391 int year = date.getYear();
2392 int dom = date.getDayOfMonth();
2393 if (year != getMinimum(YEAR)) {
2394 date.setDayOfMonth(1);
2395 jcal.normalize(date);
2396 int monthLength = jcal.getMonthLength(date);
2397 if (dom > monthLength) {
2398 date.setDayOfMonth(monthLength);
2399 } else {
2400 date.setDayOfMonth(dom);
2401 }
2402 jcal.normalize(date);
2403 } else {
2404 LocalGregorianCalendar.Date d = jcal.getCalendarDate(
2405 Long.MIN_VALUE, getZone());
2406 LocalGregorianCalendar.Date realDate = jcal
2407 .getCalendarDate(time, getZone());
2408 long tod = realDate.getTimeOfDay();
2409 // Use an equivalent year.
2410 realDate.addYear(+400);
2411 realDate.setMonth(date.getMonth());
2412 realDate.setDayOfMonth(1);
2413 jcal.normalize(realDate);
2414 int monthLength = jcal.getMonthLength(realDate);
2415 if (dom > monthLength) {
2416 realDate.setDayOfMonth(monthLength);
2417 } else {
2418 if (dom < d.getDayOfMonth()) {
2419 realDate.setDayOfMonth(d.getDayOfMonth());
2420 } else {
2421 realDate.setDayOfMonth(dom);
2422 }
2423 }
2424 if (realDate.getDayOfMonth() == d.getDayOfMonth()
2425 && tod < d.getTimeOfDay()) {
2426 realDate.setDayOfMonth(Math.min(dom + 1, monthLength));
2427 }
2428 // restore the year.
2429 date.setDate(year, realDate.getMonth(), realDate
2430 .getDayOfMonth());
2431 // Don't normalize date here so as not to cause underflow.
2432 }
2433 }
2434
2435 /**
2436 * Returns the new value after 'roll'ing the specified value and amount.
2437 */
2438 private static final int getRolledValue(int value, int amount,
2439 int min, int max) {
2440 assert value >= min && value <= max;
2441 int range = max - min + 1;
2442 amount %= range;
2443 int n = value + amount;
2444 if (n > max) {
2445 n -= range;
2446 } else if (n < min) {
2447 n += range;
2448 }
2449 assert n >= min && n <= max;
2450 return n;
2451 }
2452
2453 /**
2454 * Returns the ERA. We need a special method for this because the
2455 * default ERA is the current era, but a zero (unset) ERA means before Meiji.
2456 */
2457 private final int internalGetEra() {
2458 return isSet(ERA) ? internalGet(ERA) : eras.length - 1;
2459 }
2460
2461 /**
2462 * Updates internal state.
2463 */
2464 private void readObject(ObjectInputStream stream)
2465 throws IOException, ClassNotFoundException {
2466 stream.defaultReadObject();
2467 if (jdate == null) {
2468 jdate = jcal.newCalendarDate(getZone());
2469 cachedFixedDate = Long.MIN_VALUE;
2470 }
2471 }
2472 }
|