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