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