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