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