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 10 package ohos.global.icu.util; 11 12 import java.io.IOException; 13 import java.io.ObjectInputStream; 14 import java.util.Date; 15 import java.util.Locale; 16 17 import ohos.global.icu.impl.CalendarAstronomer; 18 import ohos.global.icu.impl.CalendarCache; 19 import ohos.global.icu.text.DateFormat; 20 import ohos.global.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 ohos.global.icu.util.Calendar#getInstance(ULocale)} passing in a <code>ULocale</code> 80 * with the tag <code>"@calendar=chinese"</code>.</p> 81 * 82 * @see ohos.global.icu.util.Calendar 83 * @author Alan Liu 84 */ 85 public 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 */ ChineseCalendar()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 */ ChineseCalendar(Date date)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 */ ChineseCalendar(int year, int month, int isLeapMonth, int date)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 */ ChineseCalendar(int year, int month, int isLeapMonth, int date, int hour, int minute, int second)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 */ ChineseCalendar(int era, int year, int month, int isLeapMonth, int date)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 */ ChineseCalendar(int era, int year, int month, int isLeapMonth, int date, int hour, int minute, int second)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 */ ChineseCalendar(Locale aLocale)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 */ ChineseCalendar(TimeZone zone)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 */ ChineseCalendar(TimeZone zone, Locale aLocale)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 */ ChineseCalendar(ULocale locale)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 */ ChineseCalendar(TimeZone zone, ULocale locale)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 deprecated on icu4j-org 329 * @hide draft / provisional / internal are hidden on OHOS 330 */ 331 @Deprecated ChineseCalendar(TimeZone zone, ULocale locale, int epochYear, TimeZone zoneAstroCalc)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 */ handleGetLimit(int field, int limitType)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 */ handleGetExtendedYear()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 */ handleGetMonthLength(int extendedYear, int month)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 */ handleGetDateFormat(String pattern, String override, ULocale locale)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 */ getFieldResolutionTable()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 */ offsetMonth(int newMoon, int dom, int delta)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 */ add(int field, int amount)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 */ roll(int field, int amount)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 */ daysToMillis(int days)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 */ millisToDays(long millis)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 */ winterSolstice(int gyear)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 */ newMoonNear(int days, boolean after)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 */ synodicMonthsBetween(int day1, int day2)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 */ majorSolarTerm(int days)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 */ hasNoMajorSolarTerm(int newMoon)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 */ isLeapMonthBetween(int newMoon1, int newMoon2)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 */ handleComputeFields(int julianDay)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 */ computeChineseFields(int days, int gyear, int gmonth, boolean setAllFields)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 */ newYear(int gyear)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 */ handleComputeMonthStart(int eyear, int month, boolean useMonth)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 */ getType()998 public String getType() { 999 return "chinese"; 1000 } 1001 1002 /** 1003 * {@inheritDoc} 1004 * @deprecated This API is ICU internal only. 1005 * @hide deprecated on icu4j-org 1006 * @hide draft / provisional / internal are hidden on OHOS 1007 */ 1008 @Deprecated haveDefaultCentury()1009 public boolean haveDefaultCentury() { 1010 return false; 1011 } 1012 1013 /** 1014 * Override readObject. 1015 */ readObject(ObjectInputStream stream)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