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