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