1/* GENERATED SOURCE. DO NOT MODIFY. */ 2// © 2016 and later: Unicode, Inc. and others. 3// License & terms of use: http://www.unicode.org/copyright.html#License 4/********************************************************************* 5 * Copyright (C) 2000-2014, International Business Machines 6 * Corporation and others. All Rights Reserved. 7 ********************************************************************* 8 */ 9 10package android.icu.util; 11 12import java.io.IOException; 13import java.io.ObjectInputStream; 14import java.util.Date; 15import java.util.Locale; 16 17import android.icu.impl.CalendarAstronomer; 18import android.icu.impl.CalendarCache; 19import android.icu.text.DateFormat; 20import android.icu.util.ULocale.Category; 21 22/** 23 * <code>ChineseCalendar</code> is a concrete subclass of {@link Calendar} 24 * that implements a traditional Chinese calendar. The traditional Chinese 25 * calendar is a lunisolar calendar: Each month starts on a new moon, and 26 * the months are numbered according to solar events, specifically, to 27 * guarantee that month 11 always contains the winter solstice. In order 28 * to accomplish this, leap months are inserted in certain years. Leap 29 * months are numbered the same as the month they follow. The decision of 30 * which month is a leap month depends on the relative movements of the sun 31 * and moon. 32 * 33 * <p>All astronomical computations are performed with respect to a time 34 * zone of GMT+8:00 and a longitude of 120 degrees east. Although some 35 * calendars implement a historically more accurate convention of using 36 * Beijing's local longitude (116 degrees 25 minutes east) and time zone 37 * (GMT+7:45:40) for dates before 1929, we do not implement this here. 38 * 39 * <p>Years are counted in two different ways in the Chinese calendar. The 40 * first method is by sequential numbering from the 61st year of the reign 41 * of Huang Di, 2637 BCE, which is designated year 1 on the Chinese 42 * calendar. The second method uses 60-year cycles from the same starting 43 * point, which is designated year 1 of cycle 1. In this class, the 44 * <code>EXTENDED_YEAR</code> field contains the sequential year count. 45 * The <code>ERA</code> field contains the cycle number, and the 46 * <code>YEAR</code> field contains the year of the cycle, a value between 47 * 1 and 60. 48 * 49 * <p>There is some variation in what is considered the starting point of 50 * the calendar, with some sources starting in the first year of the reign 51 * of Huang Di, rather than the 61st. This gives continuous year numbers 52 * 60 years greater and cycle numbers one greater than what this class 53 * implements. 54 * 55 * <p>Because <code>ChineseCalendar</code> defines an additional field and 56 * redefines the way the <code>ERA</code> field is used, it requires a new 57 * format class, <code>ChineseDateFormat</code>. As always, use the 58 * methods <code>DateFormat.getXxxInstance(Calendar cal,...)</code> to 59 * obtain a formatter for this calendar. 60 * 61 * <p>References:<ul> 62 * 63 * <li>Dershowitz and Reingold, <i>Calendrical Calculations</i>, 64 * Cambridge University Press, 1997</li> 65 * 66 * <li>Helmer Aslaksen's 67 * <a href="http://www.math.nus.edu.sg/aslaksen/calendar/chinese.shtml"> 68 * Chinese Calendar page</a></li> 69 * 70 * <li>The <a href="http://www.tondering.dk/claus/calendar.html"> 71 * Calendar FAQ</a></li> 72 * 73 * </ul> 74 * 75 * <p> 76 * This class should not be subclassed.</p> 77 * <p> 78 * ChineseCalendar usually should be instantiated using 79 * {@link android.icu.util.Calendar#getInstance(ULocale)} passing in a <code>ULocale</code> 80 * with the tag <code>"@calendar=chinese"</code>.</p> 81 * 82 * @see android.icu.util.Calendar 83 * @author Alan Liu 84 */ 85public class ChineseCalendar extends Calendar { 86 // jdk1.4.2 serialver 87 private static final long serialVersionUID = 7312110751940929420L; 88 89 //------------------------------------------------------------------ 90 // Developer Notes 91 // 92 // Time is represented as a scalar in two ways in this class. One is 93 // the usual UTC epoch millis, that is, milliseconds after January 1, 94 // 1970 Gregorian, 0:00:00.000 UTC. The other is in terms of 'local 95 // days.' This is the number of days after January 1, 1970 Gregorian, 96 // local to Beijing, China (since all computations of the Chinese 97 // calendar are done in Beijing). That is, 0 represents January 1, 98 // 1970 0:00 Asia/Shanghai. Conversion of local days to and from 99 // standard epoch milliseconds is accomplished by the daysToMillis() 100 // and millisToDays() methods. 101 // 102 // Several methods use caches to improve performance. Caches are at 103 // the object, not class level, under the assumption that typical 104 // usage will be to have one instance of ChineseCalendar at a time. 105 106 /** 107 * The start year of this Chinese calendar instance. 108 */ 109 private int epochYear; 110 111 /** 112 * The zone used for the astronomical calculation of this Chinese 113 * calendar instance. 114 */ 115 private TimeZone zoneAstro; 116 117 /** 118 * We have one instance per object, and we don't synchronize it because 119 * Calendar doesn't support multithreaded execution in the first place. 120 */ 121 private transient CalendarAstronomer astro = new CalendarAstronomer(); 122 123 /** 124 * Cache that maps Gregorian year to local days of winter solstice. 125 * @see #winterSolstice 126 */ 127 private transient CalendarCache winterSolsticeCache = new CalendarCache(); 128 129 /** 130 * Cache that maps Gregorian year to local days of Chinese new year. 131 * @see #newYear 132 */ 133 private transient CalendarCache newYearCache = new CalendarCache(); 134 135 /** 136 * True if the current year is a leap year. Updated with each time to 137 * fields resolution. 138 * @see #computeChineseFields 139 */ 140 private transient boolean isLeapYear; 141 142 //------------------------------------------------------------------ 143 // Constructors 144 //------------------------------------------------------------------ 145 146 /** 147 * Construct a <code>ChineseCalendar</code> with the default time zone and locale. 148 */ 149 public ChineseCalendar() { 150 this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT), CHINESE_EPOCH_YEAR, CHINA_ZONE); 151 } 152 153 /** 154 * Construct a <code>ChineseCalendar</code> with the give date set in the default time zone 155 * with the default locale. 156 * @param date The date to which the new calendar is set. 157 */ 158 public ChineseCalendar(Date date) { 159 this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT), CHINESE_EPOCH_YEAR, CHINA_ZONE); 160 setTime(date); 161 } 162 163 /** 164 * Constructs a <code>ChineseCalendar</code> with the given date set 165 * in the default time zone with the default <code>FORMAT</code> locale. 166 * 167 * @param year The value used to set the calendar's {@link #YEAR YEAR} time field. 168 * @param month The value used to set the calendar's {@link #MONTH MONTH} time field. 169 * The value is 0-based. e.g., 0 for January. 170 * @param isLeapMonth The value used to set the Chinese calendar's {@link #IS_LEAP_MONTH} 171 * time field. 172 * @param date The value used to set the calendar's {@link #DATE DATE} time field. 173 * @see Category#FORMAT 174 */ 175 public ChineseCalendar(int year, int month, int isLeapMonth, int date) { 176 this(year, month, isLeapMonth, date, 0, 0, 0); 177 } 178 179 /** 180 * Constructs a <code>ChineseCalendar</code> with the given date 181 * and time set for the default time zone with the default <code>FORMAT</code> locale. 182 * 183 * @param year the value used to set the {@link #YEAR YEAR} time field in the calendar. 184 * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar. 185 * Note that the month value is 0-based. e.g., 0 for January. 186 * @param isLeapMonth the value used to set the {@link #IS_LEAP_MONTH} time field 187 * in the calendar. 188 * @param date the value used to set the {@link #DATE DATE} time field in the calendar. 189 * @param hour the value used to set the {@link #HOUR_OF_DAY HOUR_OF_DAY} time field 190 * in the calendar. 191 * @param minute the value used to set the {@link #MINUTE MINUTE} time field 192 * in the calendar. 193 * @param second the value used to set the {@link #SECOND SECOND} time field 194 * in the calendar. 195 * @see Category#FORMAT 196 */ 197 public ChineseCalendar(int year, int month, int isLeapMonth, int date, int hour, 198 int minute, int second) 199 { 200 this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT), CHINESE_EPOCH_YEAR, CHINA_ZONE); 201 202 // The current time is set at this point, so ERA field is already 203 // set to the current era. 204 205 // Then we need to clean up time fields 206 this.set(MILLISECOND, 0); 207 208 // Then, set the given field values. 209 this.set(YEAR, year); 210 this.set(MONTH, month); 211 this.set(IS_LEAP_MONTH, isLeapMonth); 212 this.set(DATE, date); 213 this.set(HOUR_OF_DAY, hour); 214 this.set(MINUTE, minute); 215 this.set(SECOND, second); 216 } 217 218 /** 219 * Constructs a <code>ChineseCalendar</code> with the given date set 220 * in the default time zone with the default <code>FORMAT</code> locale. 221 * 222 * @param era The value used to set the calendar's {@link #ERA ERA} time field. 223 * @param year The value used to set the calendar's {@link #YEAR YEAR} time field. 224 * @param month The value used to set the calendar's {@link #MONTH MONTH} time field. 225 * The value is 0-based. e.g., 0 for January. 226 * @param isLeapMonth The value used to set the Chinese calendar's {@link #IS_LEAP_MONTH} 227 * time field. 228 * @param date The value used to set the calendar's {@link #DATE DATE} time field. 229 * @see Category#FORMAT 230 */ 231 public ChineseCalendar(int era, int year, int month, int isLeapMonth, int date) 232 { 233 this(era, year, month, isLeapMonth, date, 0, 0, 0); 234 } 235 236 /** 237 * Constructs a <code>ChineseCalendar</code> with the given date 238 * and time set for the default time zone with the default <code>FORMAT</code> locale. 239 * 240 * @param era the value used to set the calendar's {@link #ERA ERA} time field. 241 * @param year the value used to set the {@link #YEAR YEAR} time field in the calendar. 242 * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar. 243 * Note that the month value is 0-based. e.g., 0 for January. 244 * @param isLeapMonth the value used to set the {@link #IS_LEAP_MONTH} time field 245 * in the calendar. 246 * @param date the value used to set the {@link #DATE DATE} time field in the calendar. 247 * @param hour the value used to set the {@link #HOUR_OF_DAY HOUR_OF_DAY} time field 248 * in the calendar. 249 * @param minute the value used to set the {@link #MINUTE MINUTE} time field 250 * in the calendar. 251 * @param second the value used to set the {@link #SECOND SECOND} time field 252 * in the calendar. 253 * @see Category#FORMAT 254 */ 255 public ChineseCalendar(int era, int year, int month, int isLeapMonth, int date, int hour, 256 int minute, int second) 257 { 258 this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT), CHINESE_EPOCH_YEAR, CHINA_ZONE); 259 260 // Set 0 to millisecond field 261 this.set(MILLISECOND, 0); 262 263 // Then, set the given field values. 264 this.set(ERA, era); 265 this.set(YEAR, year); 266 this.set(MONTH, month); 267 this.set(IS_LEAP_MONTH, isLeapMonth); 268 this.set(DATE, date); 269 this.set(HOUR_OF_DAY, hour); 270 this.set(MINUTE, minute); 271 this.set(SECOND, second); 272 } 273 274 /** 275 * Constructs a <code>ChineseCalendar</code> based on the current time 276 * in the default time zone with the given locale. 277 * @param aLocale The given locale 278 */ 279 public ChineseCalendar(Locale aLocale) { 280 this(TimeZone.getDefault(), ULocale.forLocale(aLocale), CHINESE_EPOCH_YEAR, CHINA_ZONE); 281 } 282 283 /** 284 * Construct a <code>ChineseCalendar</code> based on the current time 285 * in the given time zone with the default <code>FORMAT</code> locale. 286 * @param zone the given time zone 287 * @see Category#FORMAT 288 */ 289 public ChineseCalendar(TimeZone zone) { 290 this(zone, ULocale.getDefault(Category.FORMAT), CHINESE_EPOCH_YEAR, CHINA_ZONE); 291 } 292 293 /** 294 * Construct a <code>ChineseCalendar</code> based on the current time 295 * in the given time zone with the given locale. 296 * @param zone the given time zone 297 * @param aLocale the given locale 298 */ 299 public ChineseCalendar(TimeZone zone, Locale aLocale) { 300 this(zone, ULocale.forLocale(aLocale), CHINESE_EPOCH_YEAR, CHINA_ZONE); 301 } 302 303 /** 304 * Constructs a <code>ChineseCalendar</code> based on the current time 305 * in the default time zone with the given locale. 306 * 307 * @param locale the given ulocale 308 */ 309 public ChineseCalendar(ULocale locale) { 310 this(TimeZone.getDefault(), locale, CHINESE_EPOCH_YEAR, CHINA_ZONE); 311 } 312 313 /** 314 * Construct a <code>ChineseCalendar</code> based on the current time 315 * with the given time zone with the given locale. 316 * @param zone the given time zone 317 * @param locale the given ulocale 318 */ 319 public ChineseCalendar(TimeZone zone, ULocale locale) { 320 this(zone, locale, CHINESE_EPOCH_YEAR, CHINA_ZONE); 321 } 322 323 /** 324 * Construct a <code>ChineseCalenar</code> based on the current time 325 * with the given time zone, the locale, the epoch year and the time zone 326 * used for astronomical calculation. 327 * @deprecated This API is ICU internal only. 328 * @hide original deprecated declaration 329 * @hide draft / provisional / internal are hidden on Android 330 */ 331 @Deprecated 332 protected ChineseCalendar(TimeZone zone, ULocale locale, int epochYear, TimeZone zoneAstroCalc) { 333 super(zone, locale); 334 this.epochYear = epochYear; 335 this.zoneAstro = zoneAstroCalc; 336 setTimeInMillis(System.currentTimeMillis()); 337 } 338 339 //------------------------------------------------------------------ 340 // Public constants 341 //------------------------------------------------------------------ 342 343 /** 344 * Field indicating whether or not the current month is a leap month. 345 * Should have a value of 0 for non-leap months, and 1 for leap months. 346 * @stable ICU 2.8 347 */ 348 // public static int IS_LEAP_MONTH = BASE_FIELD_COUNT; 349 350 351 //------------------------------------------------------------------ 352 // Calendar framework 353 //------------------------------------------------------------------ 354 355 /** 356 * Array defining the limits of field values for this class. Field 357 * limits which are invariant with respect to calendar system and 358 * defined by Calendar are left blank. 359 * 360 * Notes: 361 * 362 * ERA 5000000 / 60 = 83333. 363 * 364 * MONTH There are 12 or 13 lunar months in a year. However, we always 365 * number them 0..11, with an intercalated, identically numbered leap 366 * month, when necessary. 367 * 368 * DAY_OF_YEAR In a non-leap year there are 353, 354, or 355 days. In 369 * a leap year there are 383, 384, or 385 days. 370 * 371 * WEEK_OF_YEAR The least maximum occurs if there are 353 days in the 372 * year, and the first 6 are the last week of the previous year. Then 373 * we have 49 full weeks and 4 days in the last week: 6 + 49*7 + 4 = 374 * 353. So the least maximum is 50. The maximum occurs if there are 375 * 385 days in the year, and WOY 1 extends 6 days into the prior year. 376 * Then there are 54 full weeks, and 6 days in the last week: 1 + 54*7 377 * + 6 = 385. The 6 days of the last week will fall into WOY 1 of the 378 * next year. Maximum is 55. 379 * 380 * WEEK_OF_MONTH In a 29 day month, if the first 7 days make up week 1 381 * that leaves 3 full weeks and 1 day at the end. The least maximum is 382 * thus 5. In a 30 days month, if the previous 6 days belong WOM 1 of 383 * this month, we have 4 full weeks and 1 days at the end (which 384 * technically will be WOM 1 of the next month, but will be reported by 385 * time->fields and hence by getActualMaximum as WOM 6 of this month). 386 * Maximum is 6. 387 * 388 * DAY_OF_WEEK_IN_MONTH In a 29 or 30 day month, there are 4 full weeks 389 * plus 1 or 2 days at the end, so the maximum is always 5. 390 */ 391 private static final int LIMITS[][] = { 392 // Minimum Greatest Least Maximum 393 // Minimum Maximum 394 { 1, 1, 83333, 83333 }, // ERA 395 { 1, 1, 60, 60 }, // YEAR 396 { 0, 0, 11, 11 }, // MONTH 397 { 1, 1, 50, 55 }, // WEEK_OF_YEAR 398 {/* */}, // WEEK_OF_MONTH 399 { 1, 1, 29, 30 }, // DAY_OF_MONTH 400 { 1, 1, 353, 385 }, // DAY_OF_YEAR 401 {/* */}, // DAY_OF_WEEK 402 { -1, -1, 5, 5 }, // DAY_OF_WEEK_IN_MONTH 403 {/* */}, // AM_PM 404 {/* */}, // HOUR 405 {/* */}, // HOUR_OF_DAY 406 {/* */}, // MINUTE 407 {/* */}, // SECOND 408 {/* */}, // MILLISECOND 409 {/* */}, // ZONE_OFFSET 410 {/* */}, // DST_OFFSET 411 { -5000000, -5000000, 5000000, 5000000 }, // YEAR_WOY 412 {/* */}, // DOW_LOCAL 413 { -5000000, -5000000, 5000000, 5000000 }, // EXTENDED_YEAR 414 {/* */}, // JULIAN_DAY 415 {/* */}, // MILLISECONDS_IN_DAY 416 { 0, 0, 1, 1 }, // IS_LEAP_MONTH 417 }; 418 419 /** 420 * Override Calendar to return the limit value for the given field. 421 */ 422 protected int handleGetLimit(int field, int limitType) { 423 return LIMITS[field][limitType]; 424 } 425 426 /** 427 * Implement abstract Calendar method to return the extended year 428 * defined by the current fields. This will use either the ERA and 429 * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR 430 * field as the continuous year count, depending on which is newer. 431 */ 432 protected int handleGetExtendedYear() { 433 int year; 434 if (newestStamp(ERA, YEAR, UNSET) <= getStamp(EXTENDED_YEAR)) { 435 year = internalGet(EXTENDED_YEAR, 1); // Default to year 1 436 } else { 437 int cycle = internalGet(ERA, 1) - 1; // 0-based cycle 438 // adjust to the instance specific epoch 439 year = cycle * 60 + internalGet(YEAR, 1) - (epochYear - CHINESE_EPOCH_YEAR); 440 } 441 return year; 442 } 443 444 /** 445 * Override Calendar method to return the number of days in the given 446 * extended year and month. 447 * 448 * <p>Note: This method also reads the IS_LEAP_MONTH field to determine 449 * whether or not the given month is a leap month. 450 */ 451 protected int handleGetMonthLength(int extendedYear, int month) { 452 int thisStart = handleComputeMonthStart(extendedYear, month, true) - 453 EPOCH_JULIAN_DAY + 1; // Julian day -> local days 454 int nextStart = newMoonNear(thisStart + SYNODIC_GAP, true); 455 return nextStart - thisStart; 456 } 457 458 /** 459 * {@inheritDoc} 460 */ 461 protected DateFormat handleGetDateFormat(String pattern, String override, ULocale locale) { 462 // Note: ICU 50 or later versions no longer use ChineseDateFormat. 463 // The super class's handleGetDateFormat will create an instance of 464 // SimpleDateFormat which supports Chinese calendar date formatting 465 // since ICU 49. 466 467 //return new ChineseDateFormat(pattern, override, locale); 468 return super.handleGetDateFormat(pattern, override, locale); 469 } 470 471 /** 472 * Field resolution table that incorporates IS_LEAP_MONTH. 473 */ 474 static final int[][][] CHINESE_DATE_PRECEDENCE = { 475 { 476 { DAY_OF_MONTH }, 477 { WEEK_OF_YEAR, DAY_OF_WEEK }, 478 { WEEK_OF_MONTH, DAY_OF_WEEK }, 479 { DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK }, 480 { WEEK_OF_YEAR, DOW_LOCAL }, 481 { WEEK_OF_MONTH, DOW_LOCAL }, 482 { DAY_OF_WEEK_IN_MONTH, DOW_LOCAL }, 483 { DAY_OF_YEAR }, 484 { RESOLVE_REMAP | DAY_OF_MONTH, IS_LEAP_MONTH }, 485 }, 486 { 487 { WEEK_OF_YEAR }, 488 { WEEK_OF_MONTH }, 489 { DAY_OF_WEEK_IN_MONTH }, 490 { RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK }, 491 { RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DOW_LOCAL }, 492 }, 493 }; 494 495 /** 496 * Override Calendar to add IS_LEAP_MONTH to the field resolution 497 * table. 498 */ 499 protected int[][][] getFieldResolutionTable() { 500 return CHINESE_DATE_PRECEDENCE; 501 } 502 503 /** 504 * Adjust this calendar to be delta months before or after a given 505 * start position, pinning the day of month if necessary. The start 506 * position is given as a local days number for the start of the month 507 * and a day-of-month. Used by add() and roll(). 508 * @param newMoon the local days of the first day of the month of the 509 * start position (days after January 1, 1970 0:00 Asia/Shanghai) 510 * @param dom the 1-based day-of-month of the start position 511 * @param delta the number of months to move forward or backward from 512 * the start position 513 */ 514 private void offsetMonth(int newMoon, int dom, int delta) { 515 // Move to the middle of the month before our target month. 516 newMoon += (int) (CalendarAstronomer.SYNODIC_MONTH * (delta - 0.5)); 517 518 // Search forward to the target month's new moon 519 newMoon = newMoonNear(newMoon, true); 520 521 // Find the target dom 522 int jd = newMoon + EPOCH_JULIAN_DAY - 1 + dom; 523 524 // Pin the dom. In this calendar all months are 29 or 30 days 525 // so pinning just means handling dom 30. 526 if (dom > 29) { 527 set(JULIAN_DAY, jd-1); 528 // TODO Fix this. We really shouldn't ever have to 529 // explicitly call complete(). This is either a bug in 530 // this method, in ChineseCalendar, or in 531 // Calendar.getActualMaximum(). I suspect the last. 532 complete(); 533 if (getActualMaximum(DAY_OF_MONTH) >= dom) { 534 set(JULIAN_DAY, jd); 535 } 536 } else { 537 set(JULIAN_DAY, jd); 538 } 539 } 540 541 /** 542 * Override Calendar to handle leap months properly. 543 */ 544 public void add(int field, int amount) { 545 switch (field) { 546 case MONTH: 547 if (amount != 0) { 548 int dom = get(DAY_OF_MONTH); 549 int day = get(JULIAN_DAY) - EPOCH_JULIAN_DAY; // Get local day 550 int moon = day - dom + 1; // New moon 551 offsetMonth(moon, dom, amount); 552 } 553 break; 554 default: 555 super.add(field, amount); 556 break; 557 } 558 } 559 560 /** 561 * Override Calendar to handle leap months properly. 562 */ 563 public void roll(int field, int amount) { 564 switch (field) { 565 case MONTH: 566 if (amount != 0) { 567 int dom = get(DAY_OF_MONTH); 568 int day = get(JULIAN_DAY) - EPOCH_JULIAN_DAY; // Get local day 569 int moon = day - dom + 1; // New moon (start of this month) 570 571 // Note throughout the following: Months 12 and 1 are never 572 // followed by a leap month (D&R p. 185). 573 574 // Compute the adjusted month number m. This is zero-based 575 // value from 0..11 in a non-leap year, and from 0..12 in a 576 // leap year. 577 int m = get(MONTH); // 0-based month 578 if (isLeapYear) { // (member variable) 579 if (get(IS_LEAP_MONTH) == 1) { 580 ++m; 581 } else { 582 // Check for a prior leap month. (In the 583 // following, month 0 is the first month of the 584 // year.) Month 0 is never followed by a leap 585 // month, and we know month m is not a leap month. 586 // moon1 will be the start of month 0 if there is 587 // no leap month between month 0 and month m; 588 // otherwise it will be the start of month 1. 589 int moon1 = moon - 590 (int) (CalendarAstronomer.SYNODIC_MONTH * (m - 0.5)); 591 moon1 = newMoonNear(moon1, true); 592 if (isLeapMonthBetween(moon1, moon)) { 593 ++m; 594 } 595 } 596 } 597 598 // Now do the standard roll computation on m, with the 599 // allowed range of 0..n-1, where n is 12 or 13. 600 int n = isLeapYear ? 13 : 12; // Months in this year 601 int newM = (m + amount) % n; 602 if (newM < 0) { 603 newM += n; 604 } 605 606 if (newM != m) { 607 offsetMonth(moon, dom, newM - m); 608 } 609 } 610 break; 611 default: 612 super.roll(field, amount); 613 break; 614 } 615 } 616 617 //------------------------------------------------------------------ 618 // Support methods and constants 619 //------------------------------------------------------------------ 620 621 /** 622 * The start year of the Chinese calendar, the 61st year of the reign 623 * of Huang Di. Some sources use the first year of his reign, 624 * resulting in EXTENDED_YEAR values 60 years greater and ERA (cycle) 625 * values one greater. 626 */ 627 private static final int CHINESE_EPOCH_YEAR = -2636; // Gregorian year 628 629 /** 630 * The time zone used for performing astronomical computations. 631 * Some sources use a different historically accurate 632 * offset of GMT+7:45:40 for years before 1929; we do not do this. 633 */ 634 private static final TimeZone CHINA_ZONE = new SimpleTimeZone(8 * ONE_HOUR, "CHINA_ZONE").freeze(); 635 636 /** 637 * Value to be added or subtracted from the local days of a new moon to 638 * get close to the next or prior new moon, but not cross it. Must be 639 * >= 1 and < CalendarAstronomer.SYNODIC_MONTH. 640 */ 641 private static final int SYNODIC_GAP = 25; 642 643 /** 644 * Convert local days to UTC epoch milliseconds. 645 * This is not an accurate conversion in terms that getTimezoneOffset 646 * takes the milliseconds in GMT (not local time). In theory, more 647 * accurate algorithm can be implemented but practically we do not need 648 * to go through that complication as long as the historically timezone 649 * changes did not happen around the 'tricky' new moon (new moon around 650 * the midnight). 651 * 652 * @param days days after January 1, 1970 0:00 in the astronomical base zone 653 * @return milliseconds after January 1, 1970 0:00 GMT 654 */ 655 private final long daysToMillis(int days) { 656 long millis = days * ONE_DAY; 657 return millis - zoneAstro.getOffset(millis); 658 } 659 660 /** 661 * Convert UTC epoch milliseconds to local days. 662 * @param millis milliseconds after January 1, 1970 0:00 GMT 663 * @return days days after January 1, 1970 0:00 in the astronomical base zone 664 */ 665 private final int millisToDays(long millis) { 666 return (int) floorDivide(millis + zoneAstro.getOffset(millis), ONE_DAY); 667 } 668 669 //------------------------------------------------------------------ 670 // Astronomical computations 671 //------------------------------------------------------------------ 672 673 /** 674 * Return the major solar term on or after December 15 of the given 675 * Gregorian year, that is, the winter solstice of the given year. 676 * Computations are relative to Asia/Shanghai time zone. 677 * @param gyear a Gregorian year 678 * @return days after January 1, 1970 0:00 Asia/Shanghai of the 679 * winter solstice of the given year 680 */ 681 private int winterSolstice(int gyear) { 682 683 long cacheValue = winterSolsticeCache.get(gyear); 684 685 if (cacheValue == CalendarCache.EMPTY) { 686 // In books December 15 is used, but it fails for some years 687 // using our algorithms, e.g.: 1298 1391 1492 1553 1560. That 688 // is, winterSolstice(1298) starts search at Dec 14 08:00:00 689 // PST 1298 with a final result of Dec 14 10:31:59 PST 1299. 690 long ms = daysToMillis(computeGregorianMonthStart(gyear, DECEMBER) + 691 1 - EPOCH_JULIAN_DAY); 692 astro.setTime(ms); 693 694 // Winter solstice is 270 degrees solar longitude aka Dongzhi 695 long solarLong = astro.getSunTime(CalendarAstronomer.WINTER_SOLSTICE, 696 true); 697 cacheValue = millisToDays(solarLong); 698 winterSolsticeCache.put(gyear, cacheValue); 699 } 700 return (int) cacheValue; 701 } 702 703 /** 704 * Return the closest new moon to the given date, searching either 705 * forward or backward in time. 706 * @param days days after January 1, 1970 0:00 Asia/Shanghai 707 * @param after if true, search for a new moon on or after the given 708 * date; otherwise, search for a new moon before it 709 * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest 710 * new moon after or before <code>days</code> 711 */ 712 private int newMoonNear(int days, boolean after) { 713 714 astro.setTime(daysToMillis(days)); 715 long newMoon = astro.getMoonTime(CalendarAstronomer.NEW_MOON, after); 716 717 return millisToDays(newMoon); 718 } 719 720 /** 721 * Return the nearest integer number of synodic months between 722 * two dates. 723 * @param day1 days after January 1, 1970 0:00 Asia/Shanghai 724 * @param day2 days after January 1, 1970 0:00 Asia/Shanghai 725 * @return the nearest integer number of months between day1 and day2 726 */ 727 private int synodicMonthsBetween(int day1, int day2) { 728 return (int) Math.round((day2 - day1) / CalendarAstronomer.SYNODIC_MONTH); 729 } 730 731 /** 732 * Return the major solar term on or before a given date. This 733 * will be an integer from 1..12, with 1 corresponding to 330 degrees, 734 * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees. 735 * @param days days after January 1, 1970 0:00 Asia/Shanghai 736 */ 737 private int majorSolarTerm(int days) { 738 739 astro.setTime(daysToMillis(days)); 740 741 // Compute (floor(solarLongitude / (pi/6)) + 2) % 12 742 int term = ((int) Math.floor(6 * astro.getSunLongitude() / Math.PI) + 2) % 12; 743 if (term < 1) { 744 term += 12; 745 } 746 return term; 747 } 748 749 /** 750 * Return true if the given month lacks a major solar term. 751 * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new 752 * moon 753 */ 754 private boolean hasNoMajorSolarTerm(int newMoon) { 755 756 int mst = majorSolarTerm(newMoon); 757 int nmn = newMoonNear(newMoon + SYNODIC_GAP, true); 758 int mstt = majorSolarTerm(nmn); 759 return mst == mstt; 760 /* 761 return majorSolarTerm(newMoon) == 762 majorSolarTerm(newMoonNear(newMoon + SYNODIC_GAP, true)); 763 */ 764 } 765 766 //------------------------------------------------------------------ 767 // Time to fields 768 //------------------------------------------------------------------ 769 770 /** 771 * Return true if there is a leap month on or after month newMoon1 and 772 * at or before month newMoon2. 773 * @param newMoon1 days after January 1, 1970 0:00 astronomical base zone of a 774 * new moon 775 * @param newMoon2 days after January 1, 1970 0:00 astronomical base zone of a 776 * new moon 777 */ 778 private boolean isLeapMonthBetween(int newMoon1, int newMoon2) { 779 780 // This is only needed to debug the timeOfAngle divergence bug. 781 // Remove this later. Liu 11/9/00 782 // DEBUG 783 if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) { 784 throw new IllegalArgumentException("isLeapMonthBetween(" + newMoon1 + 785 ", " + newMoon2 + 786 "): Invalid parameters"); 787 } 788 789 return (newMoon2 >= newMoon1) && 790 (isLeapMonthBetween(newMoon1, newMoonNear(newMoon2 - SYNODIC_GAP, false)) || 791 hasNoMajorSolarTerm(newMoon2)); 792 } 793 794 /** 795 * Override Calendar to compute several fields specific to the Chinese 796 * calendar system. These are: 797 * 798 * <ul><li>ERA 799 * <li>YEAR 800 * <li>MONTH 801 * <li>DAY_OF_MONTH 802 * <li>DAY_OF_YEAR 803 * <li>EXTENDED_YEAR</ul> 804 * 805 * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this 806 * method is called. The getGregorianXxx() methods return Gregorian 807 * calendar equivalents for the given Julian day. 808 * 809 * <p>Compute the ChineseCalendar-specific field IS_LEAP_MONTH. 810 */ 811 protected void handleComputeFields(int julianDay) { 812 813 computeChineseFields(julianDay - EPOCH_JULIAN_DAY, // local days 814 getGregorianYear(), getGregorianMonth(), 815 true); // set all fields 816 } 817 818 /** 819 * Compute fields for the Chinese calendar system. This method can 820 * either set all relevant fields, as required by 821 * <code>handleComputeFields()</code>, or it can just set the MONTH and 822 * IS_LEAP_MONTH fields, as required by 823 * <code>handleComputeMonthStart()</code>. 824 * 825 * <p>As a side effect, this method sets {@link #isLeapYear}. 826 * @param days days after January 1, 1970 0:00 astronomical base zone of the 827 * date to compute fields for 828 * @param gyear the Gregorian year of the given date 829 * @param gmonth the Gregorian month of the given date 830 * @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR, 831 * DAY_OF_MONTH, and DAY_OF_YEAR fields. In either case set the MONTH 832 * and IS_LEAP_MONTH fields. 833 */ 834 private void computeChineseFields(int days, int gyear, int gmonth, 835 boolean setAllFields) { 836 837 // Find the winter solstices before and after the target date. 838 // These define the boundaries of this Chinese year, specifically, 839 // the position of month 11, which always contains the solstice. 840 // We want solsticeBefore <= date < solsticeAfter. 841 int solsticeBefore; 842 int solsticeAfter = winterSolstice(gyear); 843 if (days < solsticeAfter) { 844 solsticeBefore = winterSolstice(gyear - 1); 845 } else { 846 solsticeBefore = solsticeAfter; 847 solsticeAfter = winterSolstice(gyear + 1); 848 } 849 850 // Find the start of the month after month 11. This will be either 851 // the prior month 12 or leap month 11 (very rare). Also find the 852 // start of the following month 11. 853 int firstMoon = newMoonNear(solsticeBefore + 1, true); 854 int lastMoon = newMoonNear(solsticeAfter + 1, false); 855 int thisMoon = newMoonNear(days + 1, false); // Start of this month 856 // Note: isLeapYear is a member variable 857 isLeapYear = synodicMonthsBetween(firstMoon, lastMoon) == 12; 858 859 int month = synodicMonthsBetween(firstMoon, thisMoon); 860 if (isLeapYear && isLeapMonthBetween(firstMoon, thisMoon)) { 861 month--; 862 } 863 if (month < 1) { 864 month += 12; 865 } 866 867 boolean isLeapMonth = isLeapYear && 868 hasNoMajorSolarTerm(thisMoon) && 869 !isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, false)); 870 871 internalSet(MONTH, month-1); // Convert from 1-based to 0-based 872 internalSet(IS_LEAP_MONTH, isLeapMonth?1:0); 873 874 if (setAllFields) { 875 876 // Extended year and cycle year is based on the epoch year 877 int extended_year = gyear - epochYear; 878 int cycle_year = gyear - CHINESE_EPOCH_YEAR; 879 if (month < 11 || 880 gmonth >= JULY) { 881 extended_year++; 882 cycle_year++; 883 } 884 int dayOfMonth = days - thisMoon + 1; 885 886 internalSet(EXTENDED_YEAR, extended_year); 887 888 // 0->0,60 1->1,1 60->1,60 61->2,1 etc. 889 int[] yearOfCycle = new int[1]; 890 int cycle = floorDivide(cycle_year-1, 60, yearOfCycle); 891 internalSet(ERA, cycle+1); 892 internalSet(YEAR, yearOfCycle[0]+1); 893 894 internalSet(DAY_OF_MONTH, dayOfMonth); 895 896 // Days will be before the first new year we compute if this 897 // date is in month 11, leap 11, 12. There is never a leap 12. 898 // New year computations are cached so this should be cheap in 899 // the long run. 900 int newYear = newYear(gyear); 901 if (days < newYear) { 902 newYear = newYear(gyear-1); 903 } 904 internalSet(DAY_OF_YEAR, days - newYear + 1); 905 } 906 } 907 908 //------------------------------------------------------------------ 909 // Fields to time 910 //------------------------------------------------------------------ 911 912 /** 913 * Return the Chinese new year of the given Gregorian year. 914 * @param gyear a Gregorian year 915 * @return days after January 1, 1970 0:00 astronomical base zone of the 916 * Chinese new year of the given year (this will be a new moon) 917 */ 918 private int newYear(int gyear) { 919 920 long cacheValue = newYearCache.get(gyear); 921 922 if (cacheValue == CalendarCache.EMPTY) { 923 924 int solsticeBefore= winterSolstice(gyear - 1); 925 int solsticeAfter = winterSolstice(gyear); 926 int newMoon1 = newMoonNear(solsticeBefore + 1, true); 927 int newMoon2 = newMoonNear(newMoon1 + SYNODIC_GAP, true); 928 int newMoon11 = newMoonNear(solsticeAfter + 1, false); 929 930 if (synodicMonthsBetween(newMoon1, newMoon11) == 12 && 931 (hasNoMajorSolarTerm(newMoon1) || hasNoMajorSolarTerm(newMoon2))) { 932 cacheValue = newMoonNear(newMoon2 + SYNODIC_GAP, true); 933 } else { 934 cacheValue = newMoon2; 935 } 936 937 newYearCache.put(gyear, cacheValue); 938 } 939 return (int) cacheValue; 940 } 941 942 /** 943 * Return the Julian day number of day before the first day of the 944 * given month in the given extended year. 945 * 946 * <p>Note: This method reads the IS_LEAP_MONTH field to determine 947 * whether the given month is a leap month. 948 * @param eyear the extended year 949 * @param month the zero-based month. The month is also determined 950 * by reading the IS_LEAP_MONTH field. 951 * @return the Julian day number of the day before the first 952 * day of the given month and year 953 */ 954 protected int handleComputeMonthStart(int eyear, int month, boolean useMonth) { 955 956 // If the month is out of range, adjust it into range, and 957 // modify the extended year value accordingly. 958 if (month < 0 || month > 11) { 959 int[] rem = new int[1]; 960 eyear += floorDivide(month, 12, rem); 961 month = rem[0]; 962 } 963 964 int gyear = eyear + epochYear - 1; // Gregorian year 965 int newYear = newYear(gyear); 966 int newMoon = newMoonNear(newYear + month * 29, true); 967 968 int julianDay = newMoon + EPOCH_JULIAN_DAY; 969 970 // Save fields for later restoration 971 int saveMonth = internalGet(MONTH); 972 int saveIsLeapMonth = internalGet(IS_LEAP_MONTH); 973 974 // Ignore IS_LEAP_MONTH field if useMonth is false 975 int isLeapMonth = useMonth ? saveIsLeapMonth : 0; 976 977 computeGregorianFields(julianDay); 978 979 // This will modify the MONTH and IS_LEAP_MONTH fields (only) 980 computeChineseFields(newMoon, getGregorianYear(), 981 getGregorianMonth(), false); 982 983 if (month != internalGet(MONTH) || 984 isLeapMonth != internalGet(IS_LEAP_MONTH)) { 985 newMoon = newMoonNear(newMoon + SYNODIC_GAP, true); 986 julianDay = newMoon + EPOCH_JULIAN_DAY; 987 } 988 989 internalSet(MONTH, saveMonth); 990 internalSet(IS_LEAP_MONTH, saveIsLeapMonth); 991 992 return julianDay - 1; 993 } 994 995 /** 996 * {@inheritDoc} 997 */ 998 public String getType() { 999 return "chinese"; 1000 } 1001 1002 /** 1003 * {@inheritDoc} 1004 * @deprecated This API is ICU internal only. 1005 * @hide original deprecated declaration 1006 * @hide draft / provisional / internal are hidden on Android 1007 */ 1008 @Deprecated 1009 public boolean haveDefaultCentury() { 1010 return false; 1011 } 1012 1013 /** 1014 * Override readObject. 1015 */ 1016 private void readObject(ObjectInputStream stream) 1017 throws IOException, ClassNotFoundException 1018 { 1019 epochYear = CHINESE_EPOCH_YEAR; 1020 zoneAstro = CHINA_ZONE; 1021 1022 stream.defaultReadObject(); 1023 1024 /* set up the transient caches... */ 1025 astro = new CalendarAstronomer(); 1026 winterSolsticeCache = new CalendarCache(); 1027 newYearCache = new CalendarCache(); 1028 } 1029 1030 /* 1031 private static CalendarFactory factory; 1032 public static CalendarFactory factory() { 1033 if (factory == null) { 1034 factory = new CalendarFactory() { 1035 public Calendar create(TimeZone tz, ULocale loc) { 1036 return new ChineseCalendar(tz, loc); 1037 } 1038 1039 public String factoryName() { 1040 return "Chinese"; 1041 } 1042 }; 1043 } 1044 return factory; 1045 } 1046 */ 1047} 1048