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