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