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