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