JapaneseImperialCalendar.java revision 12745:f068a4ffddd2
1/* 2 * Copyright (c) 2005, 2014, 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 = size; i < eras.length; i++) { 1041 Era era = eras[i]; 1042 if (baseStyle == ALL_STYLES || baseStyle == SHORT 1043 || baseStyle == NARROW_FORMAT) { 1044 names.put(era.getAbbreviation(), i); 1045 } 1046 if (baseStyle == ALL_STYLES || baseStyle == LONG) { 1047 names.put(era.getName(), i); 1048 } 1049 } 1050 } 1051 } 1052 } 1053 return names; 1054 } 1055 1056 /** 1057 * Returns the minimum value for the given calendar field of this 1058 * {@code Calendar} instance. The minimum value is 1059 * defined as the smallest value returned by the 1060 * {@link Calendar#get(int) get} method for any possible time value, 1061 * taking into consideration the current values of the 1062 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek}, 1063 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek}, 1064 * and {@link Calendar#getTimeZone() getTimeZone} methods. 1065 * 1066 * @param field the calendar field. 1067 * @return the minimum value for the given calendar field. 1068 * @see #getMaximum(int) 1069 * @see #getGreatestMinimum(int) 1070 * @see #getLeastMaximum(int) 1071 * @see #getActualMinimum(int) 1072 * @see #getActualMaximum(int) 1073 */ 1074 public int getMinimum(int field) { 1075 return MIN_VALUES[field]; 1076 } 1077 1078 /** 1079 * Returns the maximum value for the given calendar field of this 1080 * {@code GregorianCalendar} instance. The maximum value is 1081 * defined as the largest value returned by the 1082 * {@link Calendar#get(int) get} method for any possible time value, 1083 * taking into consideration the current values of the 1084 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek}, 1085 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek}, 1086 * and {@link Calendar#getTimeZone() getTimeZone} methods. 1087 * 1088 * @param field the calendar field. 1089 * @return the maximum value for the given calendar field. 1090 * @see #getMinimum(int) 1091 * @see #getGreatestMinimum(int) 1092 * @see #getLeastMaximum(int) 1093 * @see #getActualMinimum(int) 1094 * @see #getActualMaximum(int) 1095 */ 1096 public int getMaximum(int field) { 1097 switch (field) { 1098 case YEAR: 1099 { 1100 // The value should depend on the time zone of this calendar. 1101 LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE, 1102 getZone()); 1103 return Math.max(LEAST_MAX_VALUES[YEAR], d.getYear()); 1104 } 1105 } 1106 return MAX_VALUES[field]; 1107 } 1108 1109 /** 1110 * Returns the highest minimum value for the given calendar field 1111 * of this {@code GregorianCalendar} instance. The highest 1112 * minimum value is defined as the largest value returned by 1113 * {@link #getActualMinimum(int)} for any possible time value, 1114 * taking into consideration the current values of the 1115 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek}, 1116 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek}, 1117 * and {@link Calendar#getTimeZone() getTimeZone} methods. 1118 * 1119 * @param field the calendar field. 1120 * @return the highest minimum value for the given calendar field. 1121 * @see #getMinimum(int) 1122 * @see #getMaximum(int) 1123 * @see #getLeastMaximum(int) 1124 * @see #getActualMinimum(int) 1125 * @see #getActualMaximum(int) 1126 */ 1127 public int getGreatestMinimum(int field) { 1128 return field == YEAR ? 1 : MIN_VALUES[field]; 1129 } 1130 1131 /** 1132 * Returns the lowest maximum value for the given calendar field 1133 * of this {@code GregorianCalendar} instance. The lowest 1134 * maximum value is defined as the smallest value returned by 1135 * {@link #getActualMaximum(int)} for any possible time value, 1136 * taking into consideration the current values of the 1137 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek}, 1138 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek}, 1139 * and {@link Calendar#getTimeZone() getTimeZone} methods. 1140 * 1141 * @param field the calendar field 1142 * @return the lowest maximum value for the given calendar field. 1143 * @see #getMinimum(int) 1144 * @see #getMaximum(int) 1145 * @see #getGreatestMinimum(int) 1146 * @see #getActualMinimum(int) 1147 * @see #getActualMaximum(int) 1148 */ 1149 public int getLeastMaximum(int field) { 1150 switch (field) { 1151 case YEAR: 1152 { 1153 return Math.min(LEAST_MAX_VALUES[YEAR], getMaximum(YEAR)); 1154 } 1155 } 1156 return LEAST_MAX_VALUES[field]; 1157 } 1158 1159 /** 1160 * Returns the minimum value that this calendar field could have, 1161 * taking into consideration the given time value and the current 1162 * values of the 1163 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek}, 1164 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek}, 1165 * and {@link Calendar#getTimeZone() getTimeZone} methods. 1166 * 1167 * @param field the calendar field 1168 * @return the minimum of the given field for the time value of 1169 * this {@code JapaneseImperialCalendar} 1170 * @see #getMinimum(int) 1171 * @see #getMaximum(int) 1172 * @see #getGreatestMinimum(int) 1173 * @see #getLeastMaximum(int) 1174 * @see #getActualMaximum(int) 1175 */ 1176 public int getActualMinimum(int field) { 1177 if (!isFieldSet(YEAR_MASK|MONTH_MASK|WEEK_OF_YEAR_MASK, field)) { 1178 return getMinimum(field); 1179 } 1180 1181 int value = 0; 1182 JapaneseImperialCalendar jc = getNormalizedCalendar(); 1183 // Get a local date which includes time of day and time zone, 1184 // which are missing in jc.jdate. 1185 LocalGregorianCalendar.Date jd = jcal.getCalendarDate(jc.getTimeInMillis(), 1186 getZone()); 1187 int eraIndex = getEraIndex(jd); 1188 switch (field) { 1189 case YEAR: 1190 { 1191 if (eraIndex > BEFORE_MEIJI) { 1192 value = 1; 1193 long since = eras[eraIndex].getSince(getZone()); 1194 CalendarDate d = jcal.getCalendarDate(since, getZone()); 1195 // Use the same year in jd to take care of leap 1196 // years. i.e., both jd and d must agree on leap 1197 // or common years. 1198 jd.setYear(d.getYear()); 1199 jcal.normalize(jd); 1200 assert jd.isLeapYear() == d.isLeapYear(); 1201 if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) { 1202 value++; 1203 } 1204 } else { 1205 value = getMinimum(field); 1206 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone()); 1207 // Use an equvalent year of d.getYear() if 1208 // possible. Otherwise, ignore the leap year and 1209 // common year difference. 1210 int y = d.getYear(); 1211 if (y > 400) { 1212 y -= 400; 1213 } 1214 jd.setYear(y); 1215 jcal.normalize(jd); 1216 if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) { 1217 value++; 1218 } 1219 } 1220 } 1221 break; 1222 1223 case MONTH: 1224 { 1225 // In Before Meiji and Meiji, January is the first month. 1226 if (eraIndex > MEIJI && jd.getYear() == 1) { 1227 long since = eras[eraIndex].getSince(getZone()); 1228 CalendarDate d = jcal.getCalendarDate(since, getZone()); 1229 value = d.getMonth() - 1; 1230 if (jd.getDayOfMonth() < d.getDayOfMonth()) { 1231 value++; 1232 } 1233 } 1234 } 1235 break; 1236 1237 case WEEK_OF_YEAR: 1238 { 1239 value = 1; 1240 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone()); 1241 // shift 400 years to avoid underflow 1242 d.addYear(+400); 1243 jcal.normalize(d); 1244 jd.setEra(d.getEra()); 1245 jd.setYear(d.getYear()); 1246 jcal.normalize(jd); 1247 1248 long jan1 = jcal.getFixedDate(d); 1249 long fd = jcal.getFixedDate(jd); 1250 int woy = getWeekNumber(jan1, fd); 1251 long day1 = fd - (7 * (woy - 1)); 1252 if ((day1 < jan1) || 1253 (day1 == jan1 && 1254 jd.getTimeOfDay() < d.getTimeOfDay())) { 1255 value++; 1256 } 1257 } 1258 break; 1259 } 1260 return value; 1261 } 1262 1263 /** 1264 * Returns the maximum value that this calendar field could have, 1265 * taking into consideration the given time value and the current 1266 * values of the 1267 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek}, 1268 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek}, 1269 * and 1270 * {@link Calendar#getTimeZone() getTimeZone} methods. 1271 * For example, if the date of this instance is Heisei 16February 1, 1272 * the actual maximum value of the {@code DAY_OF_MONTH} field 1273 * is 29 because Heisei 16 is a leap year, and if the date of this 1274 * instance is Heisei 17 February 1, it's 28. 1275 * 1276 * @param field the calendar field 1277 * @return the maximum of the given field for the time value of 1278 * this {@code JapaneseImperialCalendar} 1279 * @see #getMinimum(int) 1280 * @see #getMaximum(int) 1281 * @see #getGreatestMinimum(int) 1282 * @see #getLeastMaximum(int) 1283 * @see #getActualMinimum(int) 1284 */ 1285 public int getActualMaximum(int field) { 1286 final int fieldsForFixedMax = ERA_MASK|DAY_OF_WEEK_MASK|HOUR_MASK|AM_PM_MASK| 1287 HOUR_OF_DAY_MASK|MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK| 1288 ZONE_OFFSET_MASK|DST_OFFSET_MASK; 1289 if ((fieldsForFixedMax & (1<<field)) != 0) { 1290 return getMaximum(field); 1291 } 1292 1293 JapaneseImperialCalendar jc = getNormalizedCalendar(); 1294 LocalGregorianCalendar.Date date = jc.jdate; 1295 int normalizedYear = date.getNormalizedYear(); 1296 1297 int value = -1; 1298 switch (field) { 1299 case MONTH: 1300 { 1301 value = DECEMBER; 1302 if (isTransitionYear(date.getNormalizedYear())) { 1303 // TODO: there may be multiple transitions in a year. 1304 int eraIndex = getEraIndex(date); 1305 if (date.getYear() != 1) { 1306 eraIndex++; 1307 assert eraIndex < eras.length; 1308 } 1309 long transition = sinceFixedDates[eraIndex]; 1310 long fd = jc.cachedFixedDate; 1311 if (fd < transition) { 1312 LocalGregorianCalendar.Date ldate 1313 = (LocalGregorianCalendar.Date) date.clone(); 1314 jcal.getCalendarDateFromFixedDate(ldate, transition - 1); 1315 value = ldate.getMonth() - 1; 1316 } 1317 } else { 1318 LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE, 1319 getZone()); 1320 if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) { 1321 value = d.getMonth() - 1; 1322 } 1323 } 1324 } 1325 break; 1326 1327 case DAY_OF_MONTH: 1328 value = jcal.getMonthLength(date); 1329 break; 1330 1331 case DAY_OF_YEAR: 1332 { 1333 if (isTransitionYear(date.getNormalizedYear())) { 1334 // Handle transition year. 1335 // TODO: there may be multiple transitions in a year. 1336 int eraIndex = getEraIndex(date); 1337 if (date.getYear() != 1) { 1338 eraIndex++; 1339 assert eraIndex < eras.length; 1340 } 1341 long transition = sinceFixedDates[eraIndex]; 1342 long fd = jc.cachedFixedDate; 1343 CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE); 1344 d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1); 1345 if (fd < transition) { 1346 value = (int)(transition - gcal.getFixedDate(d)); 1347 } else { 1348 d.addYear(+1); 1349 value = (int)(gcal.getFixedDate(d) - transition); 1350 } 1351 } else { 1352 LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE, 1353 getZone()); 1354 if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) { 1355 long fd = jcal.getFixedDate(d); 1356 long jan1 = getFixedDateJan1(d, fd); 1357 value = (int)(fd - jan1) + 1; 1358 } else if (date.getYear() == getMinimum(YEAR)) { 1359 CalendarDate d1 = jcal.getCalendarDate(Long.MIN_VALUE, getZone()); 1360 long fd1 = jcal.getFixedDate(d1); 1361 d1.addYear(1); 1362 d1.setMonth(BaseCalendar.JANUARY).setDayOfMonth(1); 1363 jcal.normalize(d1); 1364 long fd2 = jcal.getFixedDate(d1); 1365 value = (int)(fd2 - fd1); 1366 } else { 1367 value = jcal.getYearLength(date); 1368 } 1369 } 1370 } 1371 break; 1372 1373 case WEEK_OF_YEAR: 1374 { 1375 if (!isTransitionYear(date.getNormalizedYear())) { 1376 LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE, 1377 getZone()); 1378 if (date.getEra() == jd.getEra() && date.getYear() == jd.getYear()) { 1379 long fd = jcal.getFixedDate(jd); 1380 long jan1 = getFixedDateJan1(jd, fd); 1381 value = getWeekNumber(jan1, fd); 1382 } else if (date.getEra() == null && date.getYear() == getMinimum(YEAR)) { 1383 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone()); 1384 // shift 400 years to avoid underflow 1385 d.addYear(+400); 1386 jcal.normalize(d); 1387 jd.setEra(d.getEra()); 1388 jd.setDate(d.getYear() + 1, BaseCalendar.JANUARY, 1); 1389 jcal.normalize(jd); 1390 long jan1 = jcal.getFixedDate(d); 1391 long nextJan1 = jcal.getFixedDate(jd); 1392 long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6, 1393 getFirstDayOfWeek()); 1394 int ndays = (int)(nextJan1st - nextJan1); 1395 if (ndays >= getMinimalDaysInFirstWeek()) { 1396 nextJan1st -= 7; 1397 } 1398 value = getWeekNumber(jan1, nextJan1st); 1399 } else { 1400 // Get the day of week of January 1 of the year 1401 CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE); 1402 d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1); 1403 int dayOfWeek = gcal.getDayOfWeek(d); 1404 // Normalize the day of week with the firstDayOfWeek value 1405 dayOfWeek -= getFirstDayOfWeek(); 1406 if (dayOfWeek < 0) { 1407 dayOfWeek += 7; 1408 } 1409 value = 52; 1410 int magic = dayOfWeek + getMinimalDaysInFirstWeek() - 1; 1411 if ((magic == 6) || 1412 (date.isLeapYear() && (magic == 5 || magic == 12))) { 1413 value++; 1414 } 1415 } 1416 break; 1417 } 1418 1419 if (jc == this) { 1420 jc = (JapaneseImperialCalendar) jc.clone(); 1421 } 1422 int max = getActualMaximum(DAY_OF_YEAR); 1423 jc.set(DAY_OF_YEAR, max); 1424 value = jc.get(WEEK_OF_YEAR); 1425 if (value == 1 && max > 7) { 1426 jc.add(WEEK_OF_YEAR, -1); 1427 value = jc.get(WEEK_OF_YEAR); 1428 } 1429 } 1430 break; 1431 1432 case WEEK_OF_MONTH: 1433 { 1434 LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE, 1435 getZone()); 1436 if (!(date.getEra() == jd.getEra() && date.getYear() == jd.getYear())) { 1437 CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE); 1438 d.setDate(date.getNormalizedYear(), date.getMonth(), 1); 1439 int dayOfWeek = gcal.getDayOfWeek(d); 1440 int monthLength = gcal.getMonthLength(d); 1441 dayOfWeek -= getFirstDayOfWeek(); 1442 if (dayOfWeek < 0) { 1443 dayOfWeek += 7; 1444 } 1445 int nDaysFirstWeek = 7 - dayOfWeek; // # of days in the first week 1446 value = 3; 1447 if (nDaysFirstWeek >= getMinimalDaysInFirstWeek()) { 1448 value++; 1449 } 1450 monthLength -= nDaysFirstWeek + 7 * 3; 1451 if (monthLength > 0) { 1452 value++; 1453 if (monthLength > 7) { 1454 value++; 1455 } 1456 } 1457 } else { 1458 long fd = jcal.getFixedDate(jd); 1459 long month1 = fd - jd.getDayOfMonth() + 1; 1460 value = getWeekNumber(month1, fd); 1461 } 1462 } 1463 break; 1464 1465 case DAY_OF_WEEK_IN_MONTH: 1466 { 1467 int ndays, dow1; 1468 int dow = date.getDayOfWeek(); 1469 BaseCalendar.Date d = (BaseCalendar.Date) date.clone(); 1470 ndays = jcal.getMonthLength(d); 1471 d.setDayOfMonth(1); 1472 jcal.normalize(d); 1473 dow1 = d.getDayOfWeek(); 1474 int x = dow - dow1; 1475 if (x < 0) { 1476 x += 7; 1477 } 1478 ndays -= x; 1479 value = (ndays + 6) / 7; 1480 } 1481 break; 1482 1483 case YEAR: 1484 { 1485 CalendarDate jd = jcal.getCalendarDate(jc.getTimeInMillis(), getZone()); 1486 CalendarDate d; 1487 int eraIndex = getEraIndex(date); 1488 if (eraIndex == eras.length - 1) { 1489 d = jcal.getCalendarDate(Long.MAX_VALUE, getZone()); 1490 value = d.getYear(); 1491 // Use an equivalent year for the 1492 // getYearOffsetInMillis call to avoid overflow. 1493 if (value > 400) { 1494 jd.setYear(value - 400); 1495 } 1496 } else { 1497 d = jcal.getCalendarDate(eras[eraIndex + 1].getSince(getZone()) - 1, 1498 getZone()); 1499 value = d.getYear(); 1500 // Use the same year as d.getYear() to be 1501 // consistent with leap and common years. 1502 jd.setYear(value); 1503 } 1504 jcal.normalize(jd); 1505 if (getYearOffsetInMillis(jd) > getYearOffsetInMillis(d)) { 1506 value--; 1507 } 1508 } 1509 break; 1510 1511 default: 1512 throw new ArrayIndexOutOfBoundsException(field); 1513 } 1514 return value; 1515 } 1516 1517 /** 1518 * Returns the millisecond offset from the beginning of the 1519 * year. In the year for Long.MIN_VALUE, it's a pseudo value 1520 * beyond the limit. The given CalendarDate object must have been 1521 * normalized before calling this method. 1522 */ 1523 private long getYearOffsetInMillis(CalendarDate date) { 1524 long t = (jcal.getDayOfYear(date) - 1) * ONE_DAY; 1525 return t + date.getTimeOfDay() - date.getZoneOffset(); 1526 } 1527 1528 public Object clone() { 1529 JapaneseImperialCalendar other = (JapaneseImperialCalendar) super.clone(); 1530 1531 other.jdate = (LocalGregorianCalendar.Date) jdate.clone(); 1532 other.originalFields = null; 1533 other.zoneOffsets = null; 1534 return other; 1535 } 1536 1537 public TimeZone getTimeZone() { 1538 TimeZone zone = super.getTimeZone(); 1539 // To share the zone by the CalendarDate 1540 jdate.setZone(zone); 1541 return zone; 1542 } 1543 1544 public void setTimeZone(TimeZone zone) { 1545 super.setTimeZone(zone); 1546 // To share the zone by the CalendarDate 1547 jdate.setZone(zone); 1548 } 1549 1550 /** 1551 * The fixed date corresponding to jdate. If the value is 1552 * Long.MIN_VALUE, the fixed date value is unknown. 1553 */ 1554 private transient long cachedFixedDate = Long.MIN_VALUE; 1555 1556 /** 1557 * Converts the time value (millisecond offset from the <a 1558 * href="Calendar.html#Epoch">Epoch</a>) to calendar field values. 1559 * The time is <em>not</em> 1560 * recomputed first; to recompute the time, then the fields, call the 1561 * {@code complete} method. 1562 * 1563 * @see Calendar#complete 1564 */ 1565 protected void computeFields() { 1566 int mask = 0; 1567 if (isPartiallyNormalized()) { 1568 // Determine which calendar fields need to be computed. 1569 mask = getSetStateFields(); 1570 int fieldMask = ~mask & ALL_FIELDS; 1571 if (fieldMask != 0 || cachedFixedDate == Long.MIN_VALUE) { 1572 mask |= computeFields(fieldMask, 1573 mask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK)); 1574 assert mask == ALL_FIELDS; 1575 } 1576 } else { 1577 // Specify all fields 1578 mask = ALL_FIELDS; 1579 computeFields(mask, 0); 1580 } 1581 // After computing all the fields, set the field state to `COMPUTED'. 1582 setFieldsComputed(mask); 1583 } 1584 1585 /** 1586 * This computeFields implements the conversion from UTC 1587 * (millisecond offset from the Epoch) to calendar 1588 * field values. fieldMask specifies which fields to change the 1589 * setting state to COMPUTED, although all fields are set to 1590 * the correct values. This is required to fix 4685354. 1591 * 1592 * @param fieldMask a bit mask to specify which fields to change 1593 * the setting state. 1594 * @param tzMask a bit mask to specify which time zone offset 1595 * fields to be used for time calculations 1596 * @return a new field mask that indicates what field values have 1597 * actually been set. 1598 */ 1599 private int computeFields(int fieldMask, int tzMask) { 1600 int zoneOffset = 0; 1601 TimeZone tz = getZone(); 1602 if (zoneOffsets == null) { 1603 zoneOffsets = new int[2]; 1604 } 1605 if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) { 1606 if (tz instanceof ZoneInfo) { 1607 zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets); 1608 } else { 1609 zoneOffset = tz.getOffset(time); 1610 zoneOffsets[0] = tz.getRawOffset(); 1611 zoneOffsets[1] = zoneOffset - zoneOffsets[0]; 1612 } 1613 } 1614 if (tzMask != 0) { 1615 if (isFieldSet(tzMask, ZONE_OFFSET)) { 1616 zoneOffsets[0] = internalGet(ZONE_OFFSET); 1617 } 1618 if (isFieldSet(tzMask, DST_OFFSET)) { 1619 zoneOffsets[1] = internalGet(DST_OFFSET); 1620 } 1621 zoneOffset = zoneOffsets[0] + zoneOffsets[1]; 1622 } 1623 1624 // By computing time and zoneOffset separately, we can take 1625 // the wider range of time+zoneOffset than the previous 1626 // implementation. 1627 long fixedDate = zoneOffset / ONE_DAY; 1628 int timeOfDay = zoneOffset % (int)ONE_DAY; 1629 fixedDate += time / ONE_DAY; 1630 timeOfDay += (int) (time % ONE_DAY); 1631 if (timeOfDay >= ONE_DAY) { 1632 timeOfDay -= ONE_DAY; 1633 ++fixedDate; 1634 } else { 1635 while (timeOfDay < 0) { 1636 timeOfDay += ONE_DAY; 1637 --fixedDate; 1638 } 1639 } 1640 fixedDate += EPOCH_OFFSET; 1641 1642 // See if we can use jdate to avoid date calculation. 1643 if (fixedDate != cachedFixedDate || fixedDate < 0) { 1644 jcal.getCalendarDateFromFixedDate(jdate, fixedDate); 1645 cachedFixedDate = fixedDate; 1646 } 1647 int era = getEraIndex(jdate); 1648 int year = jdate.getYear(); 1649 1650 // Always set the ERA and YEAR values. 1651 internalSet(ERA, era); 1652 internalSet(YEAR, year); 1653 int mask = fieldMask | (ERA_MASK|YEAR_MASK); 1654 1655 int month = jdate.getMonth() - 1; // 0-based 1656 int dayOfMonth = jdate.getDayOfMonth(); 1657 1658 // Set the basic date fields. 1659 if ((fieldMask & (MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK)) 1660 != 0) { 1661 internalSet(MONTH, month); 1662 internalSet(DAY_OF_MONTH, dayOfMonth); 1663 internalSet(DAY_OF_WEEK, jdate.getDayOfWeek()); 1664 mask |= MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK; 1665 } 1666 1667 if ((fieldMask & (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK 1668 |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK)) != 0) { 1669 if (timeOfDay != 0) { 1670 int hours = timeOfDay / ONE_HOUR; 1671 internalSet(HOUR_OF_DAY, hours); 1672 internalSet(AM_PM, hours / 12); // Assume AM == 0 1673 internalSet(HOUR, hours % 12); 1674 int r = timeOfDay % ONE_HOUR; 1675 internalSet(MINUTE, r / ONE_MINUTE); 1676 r %= ONE_MINUTE; 1677 internalSet(SECOND, r / ONE_SECOND); 1678 internalSet(MILLISECOND, r % ONE_SECOND); 1679 } else { 1680 internalSet(HOUR_OF_DAY, 0); 1681 internalSet(AM_PM, AM); 1682 internalSet(HOUR, 0); 1683 internalSet(MINUTE, 0); 1684 internalSet(SECOND, 0); 1685 internalSet(MILLISECOND, 0); 1686 } 1687 mask |= (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK 1688 |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK); 1689 } 1690 1691 if ((fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) != 0) { 1692 internalSet(ZONE_OFFSET, zoneOffsets[0]); 1693 internalSet(DST_OFFSET, zoneOffsets[1]); 1694 mask |= (ZONE_OFFSET_MASK|DST_OFFSET_MASK); 1695 } 1696 1697 if ((fieldMask & (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK 1698 |WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK)) != 0) { 1699 int normalizedYear = jdate.getNormalizedYear(); 1700 // If it's a year of an era transition, we need to handle 1701 // irregular year boundaries. 1702 boolean transitionYear = isTransitionYear(jdate.getNormalizedYear()); 1703 int dayOfYear; 1704 long fixedDateJan1; 1705 if (transitionYear) { 1706 fixedDateJan1 = getFixedDateJan1(jdate, fixedDate); 1707 dayOfYear = (int)(fixedDate - fixedDateJan1) + 1; 1708 } else if (normalizedYear == MIN_VALUES[YEAR]) { 1709 CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone()); 1710 fixedDateJan1 = jcal.getFixedDate(dx); 1711 dayOfYear = (int)(fixedDate - fixedDateJan1) + 1; 1712 } else { 1713 dayOfYear = (int) jcal.getDayOfYear(jdate); 1714 fixedDateJan1 = fixedDate - dayOfYear + 1; 1715 } 1716 long fixedDateMonth1 = transitionYear ? 1717 getFixedDateMonth1(jdate, fixedDate) : fixedDate - dayOfMonth + 1; 1718 1719 internalSet(DAY_OF_YEAR, dayOfYear); 1720 internalSet(DAY_OF_WEEK_IN_MONTH, (dayOfMonth - 1) / 7 + 1); 1721 1722 int weekOfYear = getWeekNumber(fixedDateJan1, fixedDate); 1723 1724 // The spec is to calculate WEEK_OF_YEAR in the 1725 // ISO8601-style. This creates problems, though. 1726 if (weekOfYear == 0) { 1727 // If the date belongs to the last week of the 1728 // previous year, use the week number of "12/31" of 1729 // the "previous" year. Again, if the previous year is 1730 // a transition year, we need to take care of it. 1731 // Usually the previous day of the first day of a year 1732 // is December 31, which is not always true in the 1733 // Japanese imperial calendar system. 1734 long fixedDec31 = fixedDateJan1 - 1; 1735 long prevJan1; 1736 LocalGregorianCalendar.Date d = getCalendarDate(fixedDec31); 1737 if (!(transitionYear || isTransitionYear(d.getNormalizedYear()))) { 1738 prevJan1 = fixedDateJan1 - 365; 1739 if (d.isLeapYear()) { 1740 --prevJan1; 1741 } 1742 } else if (transitionYear) { 1743 if (jdate.getYear() == 1) { 1744 // As of Heisei (since Meiji) there's no case 1745 // that there are multiple transitions in a 1746 // year. Historically there was such 1747 // case. There might be such case again in the 1748 // future. 1749 if (era > HEISEI) { 1750 CalendarDate pd = eras[era - 1].getSinceDate(); 1751 if (normalizedYear == pd.getYear()) { 1752 d.setMonth(pd.getMonth()).setDayOfMonth(pd.getDayOfMonth()); 1753 } 1754 } else { 1755 d.setMonth(LocalGregorianCalendar.JANUARY).setDayOfMonth(1); 1756 } 1757 jcal.normalize(d); 1758 prevJan1 = jcal.getFixedDate(d); 1759 } else { 1760 prevJan1 = fixedDateJan1 - 365; 1761 if (d.isLeapYear()) { 1762 --prevJan1; 1763 } 1764 } 1765 } else { 1766 CalendarDate cd = eras[getEraIndex(jdate)].getSinceDate(); 1767 d.setMonth(cd.getMonth()).setDayOfMonth(cd.getDayOfMonth()); 1768 jcal.normalize(d); 1769 prevJan1 = jcal.getFixedDate(d); 1770 } 1771 weekOfYear = getWeekNumber(prevJan1, fixedDec31); 1772 } else { 1773 if (!transitionYear) { 1774 // Regular years 1775 if (weekOfYear >= 52) { 1776 long nextJan1 = fixedDateJan1 + 365; 1777 if (jdate.isLeapYear()) { 1778 nextJan1++; 1779 } 1780 long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6, 1781 getFirstDayOfWeek()); 1782 int ndays = (int)(nextJan1st - nextJan1); 1783 if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) { 1784 // The first days forms a week in which the date is included. 1785 weekOfYear = 1; 1786 } 1787 } 1788 } else { 1789 LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone(); 1790 long nextJan1; 1791 if (jdate.getYear() == 1) { 1792 d.addYear(+1); 1793 d.setMonth(LocalGregorianCalendar.JANUARY).setDayOfMonth(1); 1794 nextJan1 = jcal.getFixedDate(d); 1795 } else { 1796 int nextEraIndex = getEraIndex(d) + 1; 1797 CalendarDate cd = eras[nextEraIndex].getSinceDate(); 1798 d.setEra(eras[nextEraIndex]); 1799 d.setDate(1, cd.getMonth(), cd.getDayOfMonth()); 1800 jcal.normalize(d); 1801 nextJan1 = jcal.getFixedDate(d); 1802 } 1803 long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6, 1804 getFirstDayOfWeek()); 1805 int ndays = (int)(nextJan1st - nextJan1); 1806 if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) { 1807 // The first days forms a week in which the date is included. 1808 weekOfYear = 1; 1809 } 1810 } 1811 } 1812 internalSet(WEEK_OF_YEAR, weekOfYear); 1813 internalSet(WEEK_OF_MONTH, getWeekNumber(fixedDateMonth1, fixedDate)); 1814 mask |= (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK|WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK); 1815 } 1816 return mask; 1817 } 1818 1819 /** 1820 * Returns the number of weeks in a period between fixedDay1 and 1821 * fixedDate. The getFirstDayOfWeek-getMinimalDaysInFirstWeek rule 1822 * is applied to calculate the number of weeks. 1823 * 1824 * @param fixedDay1 the fixed date of the first day of the period 1825 * @param fixedDate the fixed date of the last day of the period 1826 * @return the number of weeks of the given period 1827 */ 1828 private int getWeekNumber(long fixedDay1, long fixedDate) { 1829 // We can always use `jcal' since Julian and Gregorian are the 1830 // same thing for this calculation. 1831 long fixedDay1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDay1 + 6, 1832 getFirstDayOfWeek()); 1833 int ndays = (int)(fixedDay1st - fixedDay1); 1834 assert ndays <= 7; 1835 if (ndays >= getMinimalDaysInFirstWeek()) { 1836 fixedDay1st -= 7; 1837 } 1838 int normalizedDayOfPeriod = (int)(fixedDate - fixedDay1st); 1839 if (normalizedDayOfPeriod >= 0) { 1840 return normalizedDayOfPeriod / 7 + 1; 1841 } 1842 return CalendarUtils.floorDivide(normalizedDayOfPeriod, 7) + 1; 1843 } 1844 1845 /** 1846 * Converts calendar field values to the time value (millisecond 1847 * offset from the <a href="Calendar.html#Epoch">Epoch</a>). 1848 * 1849 * @exception IllegalArgumentException if any calendar fields are invalid. 1850 */ 1851 protected void computeTime() { 1852 // In non-lenient mode, perform brief checking of calendar 1853 // fields which have been set externally. Through this 1854 // checking, the field values are stored in originalFields[] 1855 // to see if any of them are normalized later. 1856 if (!isLenient()) { 1857 if (originalFields == null) { 1858 originalFields = new int[FIELD_COUNT]; 1859 } 1860 for (int field = 0; field < FIELD_COUNT; field++) { 1861 int value = internalGet(field); 1862 if (isExternallySet(field)) { 1863 // Quick validation for any out of range values 1864 if (value < getMinimum(field) || value > getMaximum(field)) { 1865 throw new IllegalArgumentException(getFieldName(field)); 1866 } 1867 } 1868 originalFields[field] = value; 1869 } 1870 } 1871 1872 // Let the super class determine which calendar fields to be 1873 // used to calculate the time. 1874 int fieldMask = selectFields(); 1875 1876 int year; 1877 int era; 1878 1879 if (isSet(ERA)) { 1880 era = internalGet(ERA); 1881 year = isSet(YEAR) ? internalGet(YEAR) : 1; 1882 } else { 1883 if (isSet(YEAR)) { 1884 era = eras.length - 1; 1885 year = internalGet(YEAR); 1886 } else { 1887 // Equivalent to 1970 (Gregorian) 1888 era = SHOWA; 1889 year = 45; 1890 } 1891 } 1892 1893 // Calculate the time of day. We rely on the convention that 1894 // an UNSET field has 0. 1895 long timeOfDay = 0; 1896 if (isFieldSet(fieldMask, HOUR_OF_DAY)) { 1897 timeOfDay += (long) internalGet(HOUR_OF_DAY); 1898 } else { 1899 timeOfDay += internalGet(HOUR); 1900 // The default value of AM_PM is 0 which designates AM. 1901 if (isFieldSet(fieldMask, AM_PM)) { 1902 timeOfDay += 12 * internalGet(AM_PM); 1903 } 1904 } 1905 timeOfDay *= 60; 1906 timeOfDay += internalGet(MINUTE); 1907 timeOfDay *= 60; 1908 timeOfDay += internalGet(SECOND); 1909 timeOfDay *= 1000; 1910 timeOfDay += internalGet(MILLISECOND); 1911 1912 // Convert the time of day to the number of days and the 1913 // millisecond offset from midnight. 1914 long fixedDate = timeOfDay / ONE_DAY; 1915 timeOfDay %= ONE_DAY; 1916 while (timeOfDay < 0) { 1917 timeOfDay += ONE_DAY; 1918 --fixedDate; 1919 } 1920 1921 // Calculate the fixed date since January 1, 1 (Gregorian). 1922 fixedDate += getFixedDate(era, year, fieldMask); 1923 1924 // millis represents local wall-clock time in milliseconds. 1925 long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay; 1926 1927 // Compute the time zone offset and DST offset. There are two potential 1928 // ambiguities here. We'll assume a 2:00 am (wall time) switchover time 1929 // for discussion purposes here. 1930 // 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am 1931 // can be in standard or in DST depending. However, 2:00 am is an invalid 1932 // representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST). 1933 // We assume standard time. 1934 // 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am 1935 // can be in standard or DST. Both are valid representations (the rep 1936 // jumps from 1:59:59 DST to 1:00:00 Std). 1937 // Again, we assume standard time. 1938 // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET 1939 // or DST_OFFSET fields; then we use those fields. 1940 TimeZone zone = getZone(); 1941 if (zoneOffsets == null) { 1942 zoneOffsets = new int[2]; 1943 } 1944 int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK); 1945 if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) { 1946 if (zone instanceof ZoneInfo) { 1947 ((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets); 1948 } else { 1949 zone.getOffsets(millis - zone.getRawOffset(), zoneOffsets); 1950 } 1951 } 1952 if (tzMask != 0) { 1953 if (isFieldSet(tzMask, ZONE_OFFSET)) { 1954 zoneOffsets[0] = internalGet(ZONE_OFFSET); 1955 } 1956 if (isFieldSet(tzMask, DST_OFFSET)) { 1957 zoneOffsets[1] = internalGet(DST_OFFSET); 1958 } 1959 } 1960 1961 // Adjust the time zone offset values to get the UTC time. 1962 millis -= zoneOffsets[0] + zoneOffsets[1]; 1963 1964 // Set this calendar's time in milliseconds 1965 time = millis; 1966 1967 int mask = computeFields(fieldMask | getSetStateFields(), tzMask); 1968 1969 if (!isLenient()) { 1970 for (int field = 0; field < FIELD_COUNT; field++) { 1971 if (!isExternallySet(field)) { 1972 continue; 1973 } 1974 if (originalFields[field] != internalGet(field)) { 1975 int wrongValue = internalGet(field); 1976 // Restore the original field values 1977 System.arraycopy(originalFields, 0, fields, 0, fields.length); 1978 throw new IllegalArgumentException(getFieldName(field) + "=" + wrongValue 1979 + ", expected " + originalFields[field]); 1980 } 1981 } 1982 } 1983 setFieldsNormalized(mask); 1984 } 1985 1986 /** 1987 * Computes the fixed date under either the Gregorian or the 1988 * Julian calendar, using the given year and the specified calendar fields. 1989 * 1990 * @param era era index 1991 * @param year the normalized year number, with 0 indicating the 1992 * year 1 BCE, -1 indicating 2 BCE, etc. 1993 * @param fieldMask the calendar fields to be used for the date calculation 1994 * @return the fixed date 1995 * @see Calendar#selectFields 1996 */ 1997 private long getFixedDate(int era, int year, int fieldMask) { 1998 int month = JANUARY; 1999 int firstDayOfMonth = 1; 2000 if (isFieldSet(fieldMask, MONTH)) { 2001 // No need to check if MONTH has been set (no isSet(MONTH) 2002 // call) since its unset value happens to be JANUARY (0). 2003 month = internalGet(MONTH); 2004 2005 // If the month is out of range, adjust it into range. 2006 if (month > DECEMBER) { 2007 year += month / 12; 2008 month %= 12; 2009 } else if (month < JANUARY) { 2010 int[] rem = new int[1]; 2011 year += CalendarUtils.floorDivide(month, 12, rem); 2012 month = rem[0]; 2013 } 2014 } else { 2015 if (year == 1 && era != 0) { 2016 CalendarDate d = eras[era].getSinceDate(); 2017 month = d.getMonth() - 1; 2018 firstDayOfMonth = d.getDayOfMonth(); 2019 } 2020 } 2021 2022 // Adjust the base date if year is the minimum value. 2023 if (year == MIN_VALUES[YEAR]) { 2024 CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone()); 2025 int m = dx.getMonth() - 1; 2026 if (month < m) { 2027 month = m; 2028 } 2029 if (month == m) { 2030 firstDayOfMonth = dx.getDayOfMonth(); 2031 } 2032 } 2033 2034 LocalGregorianCalendar.Date date = jcal.newCalendarDate(TimeZone.NO_TIMEZONE); 2035 date.setEra(era > 0 ? eras[era] : null); 2036 date.setDate(year, month + 1, firstDayOfMonth); 2037 jcal.normalize(date); 2038 2039 // Get the fixed date since Jan 1, 1 (Gregorian). We are on 2040 // the first day of either `month' or January in 'year'. 2041 long fixedDate = jcal.getFixedDate(date); 2042 2043 if (isFieldSet(fieldMask, MONTH)) { 2044 // Month-based calculations 2045 if (isFieldSet(fieldMask, DAY_OF_MONTH)) { 2046 // We are on the "first day" of the month (which may 2047 // not be 1). Just add the offset if DAY_OF_MONTH is 2048 // set. If the isSet call returns false, that means 2049 // DAY_OF_MONTH has been selected just because of the 2050 // selected combination. We don't need to add any 2051 // since the default value is the "first day". 2052 if (isSet(DAY_OF_MONTH)) { 2053 // To avoid underflow with DAY_OF_MONTH-firstDayOfMonth, add 2054 // DAY_OF_MONTH, then subtract firstDayOfMonth. 2055 fixedDate += internalGet(DAY_OF_MONTH); 2056 fixedDate -= firstDayOfMonth; 2057 } 2058 } else { 2059 if (isFieldSet(fieldMask, WEEK_OF_MONTH)) { 2060 long firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + 6, 2061 getFirstDayOfWeek()); 2062 // If we have enough days in the first week, then 2063 // move to the previous week. 2064 if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) { 2065 firstDayOfWeek -= 7; 2066 } 2067 if (isFieldSet(fieldMask, DAY_OF_WEEK)) { 2068 firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6, 2069 internalGet(DAY_OF_WEEK)); 2070 } 2071 // In lenient mode, we treat days of the previous 2072 // months as a part of the specified 2073 // WEEK_OF_MONTH. See 4633646. 2074 fixedDate = firstDayOfWeek + 7 * (internalGet(WEEK_OF_MONTH) - 1); 2075 } else { 2076 int dayOfWeek; 2077 if (isFieldSet(fieldMask, DAY_OF_WEEK)) { 2078 dayOfWeek = internalGet(DAY_OF_WEEK); 2079 } else { 2080 dayOfWeek = getFirstDayOfWeek(); 2081 } 2082 // We are basing this on the day-of-week-in-month. The only 2083 // trickiness occurs if the day-of-week-in-month is 2084 // negative. 2085 int dowim; 2086 if (isFieldSet(fieldMask, DAY_OF_WEEK_IN_MONTH)) { 2087 dowim = internalGet(DAY_OF_WEEK_IN_MONTH); 2088 } else { 2089 dowim = 1; 2090 } 2091 if (dowim >= 0) { 2092 fixedDate = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + (7 * dowim) - 1, 2093 dayOfWeek); 2094 } else { 2095 // Go to the first day of the next week of 2096 // the specified week boundary. 2097 int lastDate = monthLength(month, year) + (7 * (dowim + 1)); 2098 // Then, get the day of week date on or before the last date. 2099 fixedDate = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + lastDate - 1, 2100 dayOfWeek); 2101 } 2102 } 2103 } 2104 } else { 2105 // We are on the first day of the year. 2106 if (isFieldSet(fieldMask, DAY_OF_YEAR)) { 2107 if (isTransitionYear(date.getNormalizedYear())) { 2108 fixedDate = getFixedDateJan1(date, fixedDate); 2109 } 2110 // Add the offset, then subtract 1. (Make sure to avoid underflow.) 2111 fixedDate += internalGet(DAY_OF_YEAR); 2112 fixedDate--; 2113 } else { 2114 long firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + 6, 2115 getFirstDayOfWeek()); 2116 // If we have enough days in the first week, then move 2117 // to the previous week. 2118 if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) { 2119 firstDayOfWeek -= 7; 2120 } 2121 if (isFieldSet(fieldMask, DAY_OF_WEEK)) { 2122 int dayOfWeek = internalGet(DAY_OF_WEEK); 2123 if (dayOfWeek != getFirstDayOfWeek()) { 2124 firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6, 2125 dayOfWeek); 2126 } 2127 } 2128 fixedDate = firstDayOfWeek + 7 * ((long)internalGet(WEEK_OF_YEAR) - 1); 2129 } 2130 } 2131 return fixedDate; 2132 } 2133 2134 /** 2135 * Returns the fixed date of the first day of the year (usually 2136 * January 1) before the specified date. 2137 * 2138 * @param date the date for which the first day of the year is 2139 * calculated. The date has to be in the cut-over year. 2140 * @param fixedDate the fixed date representation of the date 2141 */ 2142 private long getFixedDateJan1(LocalGregorianCalendar.Date date, long fixedDate) { 2143 Era era = date.getEra(); 2144 if (date.getEra() != null && date.getYear() == 1) { 2145 for (int eraIndex = getEraIndex(date); eraIndex > 0; eraIndex--) { 2146 CalendarDate d = eras[eraIndex].getSinceDate(); 2147 long fd = gcal.getFixedDate(d); 2148 // There might be multiple era transitions in a year. 2149 if (fd > fixedDate) { 2150 continue; 2151 } 2152 return fd; 2153 } 2154 } 2155 CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE); 2156 d.setDate(date.getNormalizedYear(), Gregorian.JANUARY, 1); 2157 return gcal.getFixedDate(d); 2158 } 2159 2160 /** 2161 * Returns the fixed date of the first date of the month (usually 2162 * the 1st of the month) before the specified date. 2163 * 2164 * @param date the date for which the first day of the month is 2165 * calculated. The date must be in the era transition year. 2166 * @param fixedDate the fixed date representation of the date 2167 */ 2168 private long getFixedDateMonth1(LocalGregorianCalendar.Date date, 2169 long fixedDate) { 2170 int eraIndex = getTransitionEraIndex(date); 2171 if (eraIndex != -1) { 2172 long transition = sinceFixedDates[eraIndex]; 2173 // If the given date is on or after the transition date, then 2174 // return the transition date. 2175 if (transition <= fixedDate) { 2176 return transition; 2177 } 2178 } 2179 2180 // Otherwise, we can use the 1st day of the month. 2181 return fixedDate - date.getDayOfMonth() + 1; 2182 } 2183 2184 /** 2185 * Returns a LocalGregorianCalendar.Date produced from the specified fixed date. 2186 * 2187 * @param fd the fixed date 2188 */ 2189 private static LocalGregorianCalendar.Date getCalendarDate(long fd) { 2190 LocalGregorianCalendar.Date d = jcal.newCalendarDate(TimeZone.NO_TIMEZONE); 2191 jcal.getCalendarDateFromFixedDate(d, fd); 2192 return d; 2193 } 2194 2195 /** 2196 * Returns the length of the specified month in the specified 2197 * Gregorian year. The year number must be normalized. 2198 * 2199 * @see GregorianCalendar#isLeapYear(int) 2200 */ 2201 private int monthLength(int month, int gregorianYear) { 2202 return CalendarUtils.isGregorianLeapYear(gregorianYear) ? 2203 GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month]; 2204 } 2205 2206 /** 2207 * Returns the length of the specified month in the year provided 2208 * by internalGet(YEAR). 2209 * 2210 * @see GregorianCalendar#isLeapYear(int) 2211 */ 2212 private int monthLength(int month) { 2213 assert jdate.isNormalized(); 2214 return jdate.isLeapYear() ? 2215 GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month]; 2216 } 2217 2218 private int actualMonthLength() { 2219 int length = jcal.getMonthLength(jdate); 2220 int eraIndex = getTransitionEraIndex(jdate); 2221 if (eraIndex == -1) { 2222 long transitionFixedDate = sinceFixedDates[eraIndex]; 2223 CalendarDate d = eras[eraIndex].getSinceDate(); 2224 if (transitionFixedDate <= cachedFixedDate) { 2225 length -= d.getDayOfMonth() - 1; 2226 } else { 2227 length = d.getDayOfMonth() - 1; 2228 } 2229 } 2230 return length; 2231 } 2232 2233 /** 2234 * Returns the index to the new era if the given date is in a 2235 * transition month. For example, if the give date is Heisei 1 2236 * (1989) January 20, then the era index for Heisei is 2237 * returned. Likewise, if the given date is Showa 64 (1989) 2238 * January 3, then the era index for Heisei is returned. If the 2239 * given date is not in any transition month, then -1 is returned. 2240 */ 2241 private static int getTransitionEraIndex(LocalGregorianCalendar.Date date) { 2242 int eraIndex = getEraIndex(date); 2243 CalendarDate transitionDate = eras[eraIndex].getSinceDate(); 2244 if (transitionDate.getYear() == date.getNormalizedYear() && 2245 transitionDate.getMonth() == date.getMonth()) { 2246 return eraIndex; 2247 } 2248 if (eraIndex < eras.length - 1) { 2249 transitionDate = eras[++eraIndex].getSinceDate(); 2250 if (transitionDate.getYear() == date.getNormalizedYear() && 2251 transitionDate.getMonth() == date.getMonth()) { 2252 return eraIndex; 2253 } 2254 } 2255 return -1; 2256 } 2257 2258 private boolean isTransitionYear(int normalizedYear) { 2259 for (int i = eras.length - 1; i > 0; i--) { 2260 int transitionYear = eras[i].getSinceDate().getYear(); 2261 if (normalizedYear == transitionYear) { 2262 return true; 2263 } 2264 if (normalizedYear > transitionYear) { 2265 break; 2266 } 2267 } 2268 return false; 2269 } 2270 2271 private static int getEraIndex(LocalGregorianCalendar.Date date) { 2272 Era era = date.getEra(); 2273 for (int i = eras.length - 1; i > 0; i--) { 2274 if (eras[i] == era) { 2275 return i; 2276 } 2277 } 2278 return 0; 2279 } 2280 2281 /** 2282 * Returns this object if it's normalized (all fields and time are 2283 * in sync). Otherwise, a cloned object is returned after calling 2284 * complete() in lenient mode. 2285 */ 2286 private JapaneseImperialCalendar getNormalizedCalendar() { 2287 JapaneseImperialCalendar jc; 2288 if (isFullyNormalized()) { 2289 jc = this; 2290 } else { 2291 // Create a clone and normalize the calendar fields 2292 jc = (JapaneseImperialCalendar) this.clone(); 2293 jc.setLenient(true); 2294 jc.complete(); 2295 } 2296 return jc; 2297 } 2298 2299 /** 2300 * After adjustments such as add(MONTH), add(YEAR), we don't want the 2301 * month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar 2302 * 3, we want it to go to Feb 28. Adjustments which might run into this 2303 * problem call this method to retain the proper month. 2304 */ 2305 private void pinDayOfMonth(LocalGregorianCalendar.Date date) { 2306 int year = date.getYear(); 2307 int dom = date.getDayOfMonth(); 2308 if (year != getMinimum(YEAR)) { 2309 date.setDayOfMonth(1); 2310 jcal.normalize(date); 2311 int monthLength = jcal.getMonthLength(date); 2312 if (dom > monthLength) { 2313 date.setDayOfMonth(monthLength); 2314 } else { 2315 date.setDayOfMonth(dom); 2316 } 2317 jcal.normalize(date); 2318 } else { 2319 LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MIN_VALUE, getZone()); 2320 LocalGregorianCalendar.Date realDate = jcal.getCalendarDate(time, getZone()); 2321 long tod = realDate.getTimeOfDay(); 2322 // Use an equivalent year. 2323 realDate.addYear(+400); 2324 realDate.setMonth(date.getMonth()); 2325 realDate.setDayOfMonth(1); 2326 jcal.normalize(realDate); 2327 int monthLength = jcal.getMonthLength(realDate); 2328 if (dom > monthLength) { 2329 realDate.setDayOfMonth(monthLength); 2330 } else { 2331 if (dom < d.getDayOfMonth()) { 2332 realDate.setDayOfMonth(d.getDayOfMonth()); 2333 } else { 2334 realDate.setDayOfMonth(dom); 2335 } 2336 } 2337 if (realDate.getDayOfMonth() == d.getDayOfMonth() && tod < d.getTimeOfDay()) { 2338 realDate.setDayOfMonth(Math.min(dom + 1, monthLength)); 2339 } 2340 // restore the year. 2341 date.setDate(year, realDate.getMonth(), realDate.getDayOfMonth()); 2342 // Don't normalize date here so as not to cause underflow. 2343 } 2344 } 2345 2346 /** 2347 * Returns the new value after 'roll'ing the specified value and amount. 2348 */ 2349 private static int getRolledValue(int value, int amount, int min, int max) { 2350 assert value >= min && value <= max; 2351 int range = max - min + 1; 2352 amount %= range; 2353 int n = value + amount; 2354 if (n > max) { 2355 n -= range; 2356 } else if (n < min) { 2357 n += range; 2358 } 2359 assert n >= min && n <= max; 2360 return n; 2361 } 2362 2363 /** 2364 * Returns the ERA. We need a special method for this because the 2365 * default ERA is the current era, but a zero (unset) ERA means before Meiji. 2366 */ 2367 private int internalGetEra() { 2368 return isSet(ERA) ? internalGet(ERA) : eras.length - 1; 2369 } 2370 2371 /** 2372 * Updates internal state. 2373 */ 2374 private void readObject(ObjectInputStream stream) 2375 throws IOException, ClassNotFoundException { 2376 stream.defaultReadObject(); 2377 if (jdate == null) { 2378 jdate = jcal.newCalendarDate(getZone()); 2379 cachedFixedDate = Long.MIN_VALUE; 2380 } 2381 } 2382} 2383