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