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