chnsecal.cpp (42188B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * Copyright (C) 2007-2014, International Business Machines Corporation 6 * and others. All Rights Reserved. 7 ****************************************************************************** 8 * 9 * File CHNSECAL.CPP 10 * 11 * Modification History: 12 * 13 * Date Name Description 14 * 9/18/2007 ajmacher ported from java ChineseCalendar 15 ***************************************************************************** 16 */ 17 18 #include "chnsecal.h" 19 20 #include <cstdint> 21 22 #if !UCONFIG_NO_FORMATTING 23 24 #include "umutex.h" 25 #include <float.h> 26 #include "gregoimp.h" // Math 27 #include "astro.h" // CalendarAstronomer and CalendarCache 28 #include "unicode/simpletz.h" 29 #include "uhash.h" 30 #include "ucln_in.h" 31 #include "cstring.h" 32 33 // Debugging 34 #ifdef U_DEBUG_CHNSECAL 35 # include <stdio.h> 36 # include <stdarg.h> 37 static void debug_chnsecal_loc(const char *f, int32_t l) 38 { 39 fprintf(stderr, "%s:%d: ", f, l); 40 } 41 42 static void debug_chnsecal_msg(const char *pat, ...) 43 { 44 va_list ap; 45 va_start(ap, pat); 46 vfprintf(stderr, pat, ap); 47 fflush(stderr); 48 } 49 // must use double parens, i.e.: U_DEBUG_CHNSECAL_MSG(("four is: %d",4)); 50 #define U_DEBUG_CHNSECAL_MSG(x) {debug_chnsecal_loc(__FILE__,__LINE__);debug_chnsecal_msg x;} 51 #else 52 #define U_DEBUG_CHNSECAL_MSG(x) 53 #endif 54 55 56 // Lazy Creation & Access synchronized by class CalendarCache with a mutex. 57 static icu::CalendarCache *gWinterSolsticeCache = nullptr; 58 static icu::CalendarCache *gNewYearCache = nullptr; 59 60 static icu::TimeZone *gAstronomerTimeZone = nullptr; 61 static icu::UInitOnce gAstronomerTimeZoneInitOnce {}; 62 63 /* 64 * The start year of the Chinese calendar, 1CE. 65 */ 66 static const int32_t CHINESE_EPOCH_YEAR = 1; // Gregorian year 67 // 68 /** 69 * The start year of the Chinese calendar for cycle calculation, 70 * the 61st year of the reign of Huang Di. 71 * Some sources use the first year of his reign, 72 * resulting in ERA (cycle) values one greater. 73 */ 74 static const int32_t CYCLE_EPOCH = -2636; // Gregorian year 75 76 /** 77 * The offset from GMT in milliseconds at which we perform astronomical 78 * computations. Some sources use a different historically accurate 79 * offset of GMT+7:45:40 for years before 1929; we do not do this. 80 */ 81 static const int32_t CHINA_OFFSET = 8 * kOneHour; 82 83 /** 84 * Value to be added or subtracted from the local days of a new moon to 85 * get close to the next or prior new moon, but not cross it. Must be 86 * >= 1 and < CalendarAstronomer.SYNODIC_MONTH. 87 */ 88 static const int32_t SYNODIC_GAP = 25; 89 90 91 U_CDECL_BEGIN 92 static UBool calendar_chinese_cleanup() { 93 if (gWinterSolsticeCache) { 94 delete gWinterSolsticeCache; 95 gWinterSolsticeCache = nullptr; 96 } 97 if (gNewYearCache) { 98 delete gNewYearCache; 99 gNewYearCache = nullptr; 100 } 101 if (gAstronomerTimeZone) { 102 delete gAstronomerTimeZone; 103 gAstronomerTimeZone = nullptr; 104 } 105 gAstronomerTimeZoneInitOnce.reset(); 106 return true; 107 } 108 U_CDECL_END 109 110 U_NAMESPACE_BEGIN 111 112 113 // Implementation of the ChineseCalendar class 114 115 116 //------------------------------------------------------------------------- 117 // Constructors... 118 //------------------------------------------------------------------------- 119 120 121 namespace { 122 123 const TimeZone* getAstronomerTimeZone(); 124 int32_t newMoonNear(const TimeZone*, double, UBool, UErrorCode&); 125 int32_t newYear(const icu::ChineseCalendar::Setting&, int32_t, UErrorCode&); 126 UBool isLeapMonthBetween(const TimeZone*, int32_t, int32_t, UErrorCode&); 127 128 } // namespace 129 130 ChineseCalendar* ChineseCalendar::clone() const { 131 return new ChineseCalendar(*this); 132 } 133 134 ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success) 135 : Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success), 136 hasLeapMonthBetweenWinterSolstices(false) 137 { 138 } 139 140 ChineseCalendar::ChineseCalendar(const ChineseCalendar& other) : Calendar(other) { 141 hasLeapMonthBetweenWinterSolstices = other.hasLeapMonthBetweenWinterSolstices; 142 } 143 144 ChineseCalendar::~ChineseCalendar() 145 { 146 } 147 148 const char *ChineseCalendar::getType() const { 149 return "chinese"; 150 } 151 152 namespace { // anonymous 153 154 static void U_CALLCONV initAstronomerTimeZone() { 155 gAstronomerTimeZone = new SimpleTimeZone(CHINA_OFFSET, UNICODE_STRING_SIMPLE("CHINA_ZONE") ); 156 ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); 157 } 158 159 const TimeZone* getAstronomerTimeZone() { 160 umtx_initOnce(gAstronomerTimeZoneInitOnce, &initAstronomerTimeZone); 161 return gAstronomerTimeZone; 162 } 163 164 } // namespace anonymous 165 166 //------------------------------------------------------------------------- 167 // Minimum / Maximum access functions 168 //------------------------------------------------------------------------- 169 170 171 static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { 172 // Minimum Greatest Least Maximum 173 // Minimum Maximum 174 { 1, 1, 83333, 83333}, // ERA 175 { 1, 1, 60, 60}, // YEAR 176 { 0, 0, 11, 11}, // MONTH 177 { 1, 1, 50, 55}, // WEEK_OF_YEAR 178 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH 179 { 1, 1, 29, 30}, // DAY_OF_MONTH 180 { 1, 1, 353, 385}, // DAY_OF_YEAR 181 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK 182 { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH 183 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM 184 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR 185 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY 186 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE 187 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND 188 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND 189 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET 190 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET 191 { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY 192 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL 193 { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR 194 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY 195 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY 196 { 0, 0, 1, 1}, // IS_LEAP_MONTH 197 { 0, 0, 11, 12}, // ORDINAL_MONTH 198 }; 199 200 201 /** 202 * @draft ICU 2.4 203 */ 204 int32_t ChineseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { 205 return LIMITS[field][limitType]; 206 } 207 208 209 //---------------------------------------------------------------------- 210 // Calendar framework 211 //---------------------------------------------------------------------- 212 213 /** 214 * Implement abstract Calendar method to return the extended year 215 * defined by the current fields. This will use either the ERA and 216 * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR 217 * field as the continuous year count, depending on which is newer. 218 * @stable ICU 2.8 219 */ 220 int32_t ChineseCalendar::handleGetExtendedYear(UErrorCode& status) { 221 if (U_FAILURE(status)) { 222 return 0; 223 } 224 225 int32_t year; 226 // if UCAL_EXTENDED_YEAR is not older than UCAL_ERA nor UCAL_YEAR 227 if (newerField(UCAL_EXTENDED_YEAR, newerField(UCAL_ERA, UCAL_YEAR)) == 228 UCAL_EXTENDED_YEAR) { 229 year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 230 } else { 231 // adjust to the instance specific epoch 232 int32_t cycle = internalGet(UCAL_ERA, 1); 233 year = internalGet(UCAL_YEAR, 1); 234 // Handle int32 overflow calculation for 235 // year = year + (cycle-1) * 60 + CYCLE_EPOCH - CHINESE_EPOCH_YEAR 236 if (uprv_add32_overflow(cycle, -1, &cycle) || // 0-based cycle 237 uprv_mul32_overflow(cycle, 60, &cycle) || 238 uprv_add32_overflow(year, cycle, &year) || 239 uprv_add32_overflow(year, CYCLE_EPOCH-CHINESE_EPOCH_YEAR, 240 &year)) { 241 status = U_ILLEGAL_ARGUMENT_ERROR; 242 return 0; 243 } 244 } 245 return year; 246 } 247 248 /** 249 * Override Calendar method to return the number of days in the given 250 * extended year and month. 251 * 252 * <p>Note: This method also reads the IS_LEAP_MONTH field to determine 253 * whether or not the given month is a leap month. 254 * @stable ICU 2.8 255 */ 256 int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const { 257 bool isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) == 1; 258 return handleGetMonthLengthWithLeap(extendedYear, month, isLeapMonth, status); 259 } 260 261 int32_t ChineseCalendar::handleGetMonthLengthWithLeap(int32_t extendedYear, int32_t month, bool leap, UErrorCode& status) const { 262 const Setting setting = getSetting(status); 263 if (U_FAILURE(status)) { 264 return 0; 265 } 266 int32_t thisStart = handleComputeMonthStartWithLeap(extendedYear, month, leap, status); 267 if (U_FAILURE(status)) { 268 return 0; 269 } 270 thisStart = thisStart - 271 kEpochStartAsJulianDay + 1; // Julian day -> local days 272 int32_t nextStart = newMoonNear(setting.zoneAstroCalc, thisStart + SYNODIC_GAP, true, status); 273 return nextStart - thisStart; 274 } 275 276 /** 277 * Field resolution table that incorporates IS_LEAP_MONTH. 278 */ 279 const UFieldResolutionTable ChineseCalendar::CHINESE_DATE_PRECEDENCE[] = 280 { 281 { 282 { UCAL_DAY_OF_MONTH, kResolveSTOP }, 283 { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP }, 284 { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 285 { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 286 { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP }, 287 { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 288 { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 289 { UCAL_DAY_OF_YEAR, kResolveSTOP }, 290 { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_IS_LEAP_MONTH, kResolveSTOP }, 291 { kResolveSTOP } 292 }, 293 { 294 { UCAL_WEEK_OF_YEAR, kResolveSTOP }, 295 { UCAL_WEEK_OF_MONTH, kResolveSTOP }, 296 { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP }, 297 { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 298 { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 299 { kResolveSTOP } 300 }, 301 {{kResolveSTOP}} 302 }; 303 304 /** 305 * Override Calendar to add IS_LEAP_MONTH to the field resolution 306 * table. 307 * @stable ICU 2.8 308 */ 309 const UFieldResolutionTable* ChineseCalendar::getFieldResolutionTable() const { 310 return CHINESE_DATE_PRECEDENCE; 311 } 312 313 namespace { 314 315 struct MonthInfo { 316 int32_t month; 317 int32_t ordinalMonth; 318 int32_t thisMoon; 319 bool isLeapMonth; 320 bool hasLeapMonthBetweenWinterSolstices; 321 }; 322 struct MonthInfo computeMonthInfo( 323 const icu::ChineseCalendar::Setting& setting, 324 int32_t gyear, int32_t days, UErrorCode& status); 325 326 } // namespace 327 328 /** 329 * Return the Julian day number of day before the first day of the 330 * given month in the given extended year. 331 * 332 * <p>Note: This method reads the IS_LEAP_MONTH field to determine 333 * whether the given month is a leap month. 334 * @param eyear the extended year 335 * @param month the zero-based month. The month is also determined 336 * by reading the IS_LEAP_MONTH field. 337 * @return the Julian day number of the day before the first 338 * day of the given month and year 339 * @stable ICU 2.8 340 */ 341 int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth, UErrorCode& status) const { 342 bool isLeapMonth = false; 343 if (useMonth) { 344 isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) != 0; 345 } 346 return handleComputeMonthStartWithLeap(eyear, month, isLeapMonth, status); 347 } 348 349 int64_t ChineseCalendar::handleComputeMonthStartWithLeap(int32_t eyear, int32_t month, bool isLeapMonth, UErrorCode& status) const { 350 if (U_FAILURE(status)) { 351 return 0; 352 } 353 // If the month is out of range, adjust it into range, and 354 // modify the extended year value accordingly. 355 if (month < 0 || month > 11) { 356 if (uprv_add32_overflow(eyear, ClockMath::floorDivide(month, 12, &month), &eyear)) { 357 status = U_ILLEGAL_ARGUMENT_ERROR; 358 return 0; 359 } 360 } 361 362 const Setting setting = getSetting(status); 363 if (U_FAILURE(status)) { 364 return 0; 365 } 366 int32_t gyear = eyear; 367 int32_t theNewYear = newYear(setting, gyear, status); 368 int32_t newMoon = newMoonNear(setting.zoneAstroCalc, theNewYear + month * 29, true, status); 369 if (U_FAILURE(status)) { 370 return 0; 371 } 372 373 int32_t newMonthYear = Grego::dayToYear(newMoon, status); 374 375 struct MonthInfo monthInfo = computeMonthInfo(setting, newMonthYear, newMoon, status); 376 if (U_FAILURE(status)) { 377 return 0; 378 } 379 if (month != monthInfo.month-1 || isLeapMonth != monthInfo.isLeapMonth) { 380 newMoon = newMoonNear(setting.zoneAstroCalc, newMoon + SYNODIC_GAP, true, status); 381 if (U_FAILURE(status)) { 382 return 0; 383 } 384 } 385 int32_t julianDay; 386 if (uprv_add32_overflow(newMoon-1, kEpochStartAsJulianDay, &julianDay)) { 387 status = U_ILLEGAL_ARGUMENT_ERROR; 388 return 0; 389 } 390 391 return julianDay; 392 } 393 394 395 /** 396 * Override Calendar to handle leap months properly. 397 * @stable ICU 2.8 398 */ 399 void ChineseCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) { 400 switch (field) { 401 case UCAL_MONTH: 402 case UCAL_ORDINAL_MONTH: 403 if (amount != 0) { 404 int32_t dom = get(UCAL_DAY_OF_MONTH, status); 405 if (U_FAILURE(status)) break; 406 int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day 407 if (U_FAILURE(status)) break; 408 int32_t moon = day - dom + 1; // New moon 409 offsetMonth(moon, dom, amount, status); 410 } 411 break; 412 default: 413 Calendar::add(field, amount, status); 414 break; 415 } 416 } 417 418 /** 419 * Override Calendar to handle leap months properly. 420 * @stable ICU 2.8 421 */ 422 void ChineseCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) { 423 add(static_cast<UCalendarDateFields>(field), amount, status); 424 } 425 426 namespace { 427 428 struct RollMonthInfo { 429 int32_t month; 430 int32_t newMoon; 431 int32_t thisMoon; 432 }; 433 434 struct RollMonthInfo rollMonth(const TimeZone* timeZone, int32_t amount, int32_t day, int32_t month, int32_t dayOfMonth, 435 bool isLeapMonth, bool hasLeapMonthBetweenWinterSolstices, 436 UErrorCode& status) { 437 struct RollMonthInfo output = {0, 0, 0}; 438 if (U_FAILURE(status)) { 439 return output; 440 } 441 442 output.thisMoon = day - dayOfMonth + 1; // New moon (start of this month) 443 444 // Note throughout the following: Months 12 and 1 are never 445 // followed by a leap month (D&R p. 185). 446 447 // Compute the adjusted month number m. This is zero-based 448 // value from 0..11 in a non-leap year, and from 0..12 in a 449 // leap year. 450 if (hasLeapMonthBetweenWinterSolstices) { // (member variable) 451 if (isLeapMonth) { 452 ++month; 453 } else { 454 // Check for a prior leap month. (In the 455 // following, month 0 is the first month of the 456 // year.) Month 0 is never followed by a leap 457 // month, and we know month m is not a leap month. 458 // moon1 will be the start of month 0 if there is 459 // no leap month between month 0 and month m; 460 // otherwise it will be the start of month 1. 461 int prevMoon = output.thisMoon - 462 static_cast<int>(CalendarAstronomer::SYNODIC_MONTH * (month - 0.5)); 463 prevMoon = newMoonNear(timeZone, prevMoon, true, status); 464 if (U_FAILURE(status)) { 465 return output; 466 } 467 if (isLeapMonthBetween(timeZone, prevMoon, output.thisMoon, status)) { 468 ++month; 469 } 470 if (U_FAILURE(status)) { 471 return output; 472 } 473 } 474 } 475 // Now do the standard roll computation on month, with the 476 // allowed range of 0..n-1, where n is 12 or 13. 477 int32_t numberOfMonths = hasLeapMonthBetweenWinterSolstices ? 13 : 12; // Months in this year 478 if (uprv_add32_overflow(amount, month, &amount)) { 479 status = U_ILLEGAL_ARGUMENT_ERROR; 480 return output; 481 } 482 output.newMoon = amount % numberOfMonths; 483 if (output.newMoon < 0) { 484 output.newMoon += numberOfMonths; 485 } 486 output.month = month; 487 return output; 488 } 489 490 } // namespace 491 492 /** 493 * Override Calendar to handle leap months properly. 494 * @stable ICU 2.8 495 */ 496 void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) { 497 switch (field) { 498 case UCAL_MONTH: 499 case UCAL_ORDINAL_MONTH: 500 if (amount != 0) { 501 const Setting setting = getSetting(status); 502 int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day 503 int32_t month = get(UCAL_MONTH, status); // 0-based month 504 int32_t dayOfMonth = get(UCAL_DAY_OF_MONTH, status); 505 bool isLeapMonth = get(UCAL_IS_LEAP_MONTH, status) == 1; 506 if (U_FAILURE(status)) break; 507 struct RollMonthInfo r = rollMonth( 508 setting.zoneAstroCalc, amount, day, month, dayOfMonth, isLeapMonth, 509 hasLeapMonthBetweenWinterSolstices, status); 510 if (U_FAILURE(status)) break; 511 if (r.newMoon != r.month) { 512 offsetMonth(r.thisMoon, dayOfMonth, r.newMoon - r.month, status); 513 } 514 } 515 break; 516 default: 517 Calendar::roll(field, amount, status); 518 break; 519 } 520 } 521 522 void ChineseCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { 523 roll(static_cast<UCalendarDateFields>(field), amount, status); 524 } 525 526 527 //------------------------------------------------------------------ 528 // Support methods and constants 529 //------------------------------------------------------------------ 530 531 namespace { 532 /** 533 * Convert local days to UTC epoch milliseconds. 534 * This is not an accurate conversion in that getTimezoneOffset 535 * takes the milliseconds in GMT (not local time). In theory, more 536 * accurate algorithm can be implemented but practically we do not need 537 * to go through that complication as long as the historical timezone 538 * changes did not happen around the 'tricky' new moon (new moon around 539 * midnight). 540 * 541 * @param timeZone time zone for the Astro calculation. 542 * @param days days after January 1, 1970 0:00 in the astronomical base zone 543 * @return milliseconds after January 1, 1970 0:00 GMT 544 */ 545 double daysToMillis(const TimeZone* timeZone, double days, UErrorCode& status) { 546 if (U_FAILURE(status)) { 547 return 0; 548 } 549 double millis = days * kOneDay; 550 if (timeZone != nullptr) { 551 int32_t rawOffset, dstOffset; 552 timeZone->getOffset(millis, false, rawOffset, dstOffset, status); 553 if (U_FAILURE(status)) { 554 return 0; 555 } 556 return millis - static_cast<double>(rawOffset + dstOffset); 557 } 558 return millis - static_cast<double>(CHINA_OFFSET); 559 } 560 561 /** 562 * Convert UTC epoch milliseconds to local days. 563 * @param timeZone time zone for the Astro calculation. 564 * @param millis milliseconds after January 1, 1970 0:00 GMT 565 * @return days after January 1, 1970 0:00 in the astronomical base zone 566 */ 567 double millisToDays(const TimeZone* timeZone, double millis, UErrorCode& status) { 568 if (U_FAILURE(status)) { 569 return 0; 570 } 571 if (timeZone != nullptr) { 572 int32_t rawOffset, dstOffset; 573 timeZone->getOffset(millis, false, rawOffset, dstOffset, status); 574 if (U_FAILURE(status)) { 575 return 0; 576 } 577 return ClockMath::floorDivide(millis + static_cast<double>(rawOffset + dstOffset), kOneDay); 578 } 579 return ClockMath::floorDivide(millis + static_cast<double>(CHINA_OFFSET), kOneDay); 580 } 581 582 //------------------------------------------------------------------ 583 // Astronomical computations 584 //------------------------------------------------------------------ 585 586 587 /** 588 * Return the major solar term on or after December 15 of the given 589 * Gregorian year, that is, the winter solstice of the given year. 590 * Computations are relative to Asia/Shanghai time zone. 591 * @param setting setting (time zone and caches) for the Astro calculation. 592 * @param gyear a Gregorian year 593 * @return days after January 1, 1970 0:00 Asia/Shanghai of the 594 * winter solstice of the given year 595 */ 596 int32_t winterSolstice(const icu::ChineseCalendar::Setting& setting, 597 int32_t gyear, UErrorCode& status) { 598 if (U_FAILURE(status)) { 599 return 0; 600 } 601 const TimeZone* timeZone = setting.zoneAstroCalc; 602 603 int32_t cacheValue = CalendarCache::get(setting.winterSolsticeCache, gyear, status); 604 if (U_FAILURE(status)) { 605 return 0; 606 } 607 608 if (cacheValue == 0) { 609 // In books December 15 is used, but it fails for some years 610 // using our algorithms, e.g.: 1298 1391 1492 1553 1560. That 611 // is, winterSolstice(1298) starts search at Dec 14 08:00:00 612 // PST 1298 with a final result of Dec 14 10:31:59 PST 1299. 613 double ms = daysToMillis(timeZone, Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1), status); 614 if (U_FAILURE(status)) { 615 return 0; 616 } 617 618 // Winter solstice is 270 degrees solar longitude aka Dongzhi 619 double days = millisToDays(timeZone, 620 CalendarAstronomer(ms) 621 .getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), true), 622 status); 623 if (U_FAILURE(status)) { 624 return 0; 625 } 626 if (days < INT32_MIN || days > INT32_MAX) { 627 status = U_ILLEGAL_ARGUMENT_ERROR; 628 return 0; 629 } 630 cacheValue = static_cast<int32_t>(days); 631 CalendarCache::put(setting.winterSolsticeCache, gyear, cacheValue, status); 632 } 633 if(U_FAILURE(status)) { 634 cacheValue = 0; 635 } 636 return cacheValue; 637 } 638 639 /** 640 * Return the closest new moon to the given date, searching either 641 * forward or backward in time. 642 * @param timeZone time zone for the Astro calculation. 643 * @param days days after January 1, 1970 0:00 Asia/Shanghai 644 * @param after if true, search for a new moon on or after the given 645 * date; otherwise, search for a new moon before it 646 * @param status 647 * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest 648 * new moon after or before <code>days</code> 649 */ 650 int32_t newMoonNear(const TimeZone* timeZone, double days, UBool after, UErrorCode& status) { 651 if (U_FAILURE(status)) { 652 return 0; 653 } 654 double ms = daysToMillis(timeZone, days, status); 655 if (U_FAILURE(status)) { 656 return 0; 657 } 658 return static_cast<int32_t>(millisToDays( 659 timeZone, 660 CalendarAstronomer(ms) 661 .getMoonTime(CalendarAstronomer::NEW_MOON(), after), 662 status)); 663 } 664 665 /** 666 * Return the nearest integer number of synodic months between 667 * two dates. 668 * @param day1 days after January 1, 1970 0:00 Asia/Shanghai 669 * @param day2 days after January 1, 1970 0:00 Asia/Shanghai 670 * @return the nearest integer number of months between day1 and day2 671 */ 672 int32_t synodicMonthsBetween(int32_t day1, int32_t day2) { 673 double roundme = ((day2 - day1) / CalendarAstronomer::SYNODIC_MONTH); 674 return static_cast<int32_t>(roundme + (roundme >= 0 ? .5 : -.5)); 675 } 676 677 /** 678 * Return the major solar term on or before a given date. This 679 * will be an integer from 1..12, with 1 corresponding to 330 degrees, 680 * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees. 681 * @param timeZone time zone for the Astro calculation. 682 * @param days days after January 1, 1970 0:00 Asia/Shanghai 683 */ 684 int32_t majorSolarTerm(const TimeZone* timeZone, int32_t days, UErrorCode& status) { 685 if (U_FAILURE(status)) { 686 return 0; 687 } 688 // Compute (floor(solarLongitude / (pi/6)) + 2) % 12 689 double ms = daysToMillis(timeZone, days, status); 690 if (U_FAILURE(status)) { 691 return 0; 692 } 693 int32_t term = ((static_cast<int32_t>(6 * CalendarAstronomer(ms) 694 .getSunLongitude() / CalendarAstronomer::PI)) + 2 ) % 12; 695 if (U_FAILURE(status)) { 696 return 0; 697 } 698 if (term < 1) { 699 term += 12; 700 } 701 return term; 702 } 703 704 /** 705 * Return true if the given month lacks a major solar term. 706 * @param timeZone time zone for the Astro calculation. 707 * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new 708 * moon 709 */ 710 UBool hasNoMajorSolarTerm(const TimeZone* timeZone, int32_t newMoon, UErrorCode& status) { 711 if (U_FAILURE(status)) { 712 return false; 713 } 714 int32_t term1 = majorSolarTerm(timeZone, newMoon, status); 715 int32_t term2 = majorSolarTerm( 716 timeZone, newMoonNear(timeZone, newMoon + SYNODIC_GAP, true, status), status); 717 if (U_FAILURE(status)) { 718 return false; 719 } 720 return term1 == term2; 721 } 722 723 724 //------------------------------------------------------------------ 725 // Time to fields 726 //------------------------------------------------------------------ 727 728 /** 729 * Return true if there is a leap month on or after month newMoon1 and 730 * at or before month newMoon2. 731 * @param timeZone time zone for the Astro calculation. 732 * @param newMoon1 days after January 1, 1970 0:00 astronomical base zone 733 * of a new moon 734 * @param newMoon2 days after January 1, 1970 0:00 astronomical base zone 735 * of a new moon 736 */ 737 UBool isLeapMonthBetween(const TimeZone* timeZone, int32_t newMoon1, int32_t newMoon2, UErrorCode& status) { 738 if (U_FAILURE(status)) { 739 return false; 740 } 741 742 #ifdef U_DEBUG_CHNSECAL 743 // This is only needed to debug the timeOfAngle divergence bug. 744 // Remove this later. Liu 11/9/00 745 if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) { 746 U_DEBUG_CHNSECAL_MSG(( 747 "isLeapMonthBetween(%d, %d): Invalid parameters", newMoon1, newMoon2 748 )); 749 } 750 #endif 751 752 while (newMoon2 >= newMoon1) { 753 if (hasNoMajorSolarTerm(timeZone, newMoon2, status)) { 754 return true; 755 } 756 newMoon2 = newMoonNear(timeZone, newMoon2 - SYNODIC_GAP, false, status); 757 if (U_FAILURE(status)) { 758 return false; 759 } 760 } 761 return false; 762 } 763 764 765 /** 766 * Compute the information about the year. 767 * @param setting setting (time zone and caches) for the Astro calculation. 768 * @param gyear the Gregorian year of the given date 769 * @param days days after January 1, 1970 0:00 astronomical base zone 770 * of the date to compute fields for 771 * @return The MonthInfo result. 772 */ 773 struct MonthInfo computeMonthInfo( 774 const icu::ChineseCalendar::Setting& setting, 775 int32_t gyear, int32_t days, UErrorCode& status) { 776 struct MonthInfo output = {0, 0, 0, false, false}; 777 if (U_FAILURE(status)) { 778 return output; 779 } 780 // Find the winter solstices before and after the target date. 781 // These define the boundaries of this Chinese year, specifically, 782 // the position of month 11, which always contains the solstice. 783 // We want solsticeBefore <= date < solsticeAfter. 784 int32_t solsticeBefore; 785 int32_t solsticeAfter = winterSolstice(setting, gyear, status); 786 if (U_FAILURE(status)) { 787 return output; 788 } 789 if (days < solsticeAfter) { 790 int32_t gprevious_year; 791 if (uprv_add32_overflow(gyear, -1, &gprevious_year)) { 792 status = U_ILLEGAL_ARGUMENT_ERROR; 793 return output; 794 } 795 solsticeBefore = winterSolstice(setting, gprevious_year, status); 796 } else { 797 solsticeBefore = solsticeAfter; 798 int32_t gnext_year; 799 if (uprv_add32_overflow(gyear, 1, &gnext_year)) { 800 status = U_ILLEGAL_ARGUMENT_ERROR; 801 return output; 802 } 803 solsticeAfter = winterSolstice(setting, gnext_year, status); 804 } 805 if (!(solsticeBefore <= days && days < solsticeAfter)) { 806 status = U_ILLEGAL_ARGUMENT_ERROR; 807 } 808 if (U_FAILURE(status)) { 809 return output; 810 } 811 812 const TimeZone* timeZone = setting.zoneAstroCalc; 813 // Find the start of the month after month 11. This will be either 814 // the prior month 12 or leap month 11 (very rare). Also find the 815 // start of the following month 11. 816 int32_t firstMoon = newMoonNear(timeZone, solsticeBefore + 1, true, status); 817 int32_t lastMoon = newMoonNear(timeZone, solsticeAfter + 1, false, status); 818 if (U_FAILURE(status)) { 819 return output; 820 } 821 output.thisMoon = newMoonNear(timeZone, days + 1, false, status); // Start of this month 822 if (U_FAILURE(status)) { 823 return output; 824 } 825 output.hasLeapMonthBetweenWinterSolstices = synodicMonthsBetween(firstMoon, lastMoon) == 12; 826 827 output.month = synodicMonthsBetween(firstMoon, output.thisMoon); 828 int32_t theNewYear = newYear(setting, gyear, status); 829 if (U_FAILURE(status)) { 830 return output; 831 } 832 if (days < theNewYear) { 833 int32_t gprevious_year; 834 if (uprv_add32_overflow(gyear, -1, &gprevious_year)) { 835 status = U_ILLEGAL_ARGUMENT_ERROR; 836 return output; 837 } 838 theNewYear = newYear(setting, gprevious_year, status); 839 if (U_FAILURE(status)) { 840 return output; 841 } 842 } 843 if (output.hasLeapMonthBetweenWinterSolstices && 844 isLeapMonthBetween(timeZone, firstMoon, output.thisMoon, status)) { 845 output.month--; 846 } 847 if (U_FAILURE(status)) { 848 return output; 849 } 850 if (output.month < 1) { 851 output.month += 12; 852 } 853 output.ordinalMonth = synodicMonthsBetween(theNewYear, output.thisMoon); 854 if (output.ordinalMonth < 0) { 855 output.ordinalMonth += 12; 856 } 857 output.isLeapMonth = output.hasLeapMonthBetweenWinterSolstices && 858 hasNoMajorSolarTerm(timeZone, output.thisMoon, status) && 859 !isLeapMonthBetween(timeZone, firstMoon, 860 newMoonNear(timeZone, output.thisMoon - SYNODIC_GAP, false, status), 861 status); 862 if (U_FAILURE(status)) { 863 return output; 864 } 865 return output; 866 } 867 868 } // namespace 869 870 /** 871 * Override Calendar to compute several fields specific to the Chinese 872 * calendar system. These are: 873 * 874 * <ul><li>ERA 875 * <li>YEAR 876 * <li>MONTH 877 * <li>DAY_OF_MONTH 878 * <li>DAY_OF_YEAR 879 * <li>EXTENDED_YEAR</ul> 880 * 881 * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this 882 * method is called. The getGregorianXxx() methods return Gregorian 883 * calendar equivalents for the given Julian day. 884 * 885 * <p>Compute the ChineseCalendar-specific field IS_LEAP_MONTH. 886 * @stable ICU 2.8 887 */ 888 void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode & status) { 889 if (U_FAILURE(status)) { 890 return; 891 } 892 int32_t days; 893 if (uprv_add32_overflow(julianDay, -kEpochStartAsJulianDay, &days)) { 894 status = U_ILLEGAL_ARGUMENT_ERROR; 895 return; 896 } 897 int32_t gyear = getGregorianYear(); 898 int32_t gmonth = getGregorianMonth(); 899 900 const Setting setting = getSetting(status); 901 if (U_FAILURE(status)) { 902 return; 903 } 904 struct MonthInfo monthInfo = computeMonthInfo(setting, gyear, days, status); 905 if (U_FAILURE(status)) { 906 return; 907 } 908 hasLeapMonthBetweenWinterSolstices = monthInfo.hasLeapMonthBetweenWinterSolstices; 909 910 // Extended year and cycle year is based on the epoch year 911 int32_t eyear; 912 int32_t cycle_year; 913 if (uprv_add32_overflow(gyear, -CHINESE_EPOCH_YEAR, &eyear) || 914 uprv_add32_overflow(gyear, -CYCLE_EPOCH, &cycle_year)) { 915 status = U_ILLEGAL_ARGUMENT_ERROR; 916 return; 917 } 918 if (monthInfo.month < 11 || 919 gmonth >= UCAL_JULY) { 920 // forward to next year 921 if (uprv_add32_overflow(eyear, 1, &eyear) || 922 uprv_add32_overflow(cycle_year, 1, &cycle_year)) { 923 status = U_ILLEGAL_ARGUMENT_ERROR; 924 return; 925 } 926 } 927 int32_t dayOfMonth = days - monthInfo.thisMoon + 1; 928 929 // 0->0,60 1->1,1 60->1,60 61->2,1 etc. 930 int32_t yearOfCycle; 931 int32_t cycle = ClockMath::floorDivide(cycle_year - 1, 60, &yearOfCycle); 932 933 // Days will be before the first new year we compute if this 934 // date is in month 11, leap 11, 12. There is never a leap 12. 935 // New year computations are cached so this should be cheap in 936 // the long run. 937 int32_t theNewYear = newYear(setting, gyear, status); 938 if (U_FAILURE(status)) { 939 return; 940 } 941 if (days < theNewYear) { 942 int32_t gprevious_year; 943 if (uprv_add32_overflow(gyear, -1, &gprevious_year)) { 944 status = U_ILLEGAL_ARGUMENT_ERROR; 945 return; 946 } 947 theNewYear = newYear(setting, gprevious_year, status); 948 } 949 if (U_FAILURE(status)) { 950 return; 951 } 952 cycle++; 953 yearOfCycle++; 954 int32_t dayOfYear = days - theNewYear + 1; 955 956 int32_t minYear = this->handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MINIMUM); 957 if (eyear < minYear) { 958 if (!isLenient()) { 959 status = U_ILLEGAL_ARGUMENT_ERROR; 960 return; 961 } 962 eyear = minYear; 963 } 964 int32_t maxYear = this->handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MAXIMUM); 965 if (maxYear < eyear) { 966 if (!isLenient()) { 967 status = U_ILLEGAL_ARGUMENT_ERROR; 968 return; 969 } 970 eyear = maxYear; 971 } 972 973 internalSet(UCAL_MONTH, monthInfo.month-1); // Convert from 1-based to 0-based 974 internalSet(UCAL_ORDINAL_MONTH, monthInfo.ordinalMonth); // Convert from 1-based to 0-based 975 internalSet(UCAL_IS_LEAP_MONTH, monthInfo.isLeapMonth?1:0); 976 977 internalSet(UCAL_EXTENDED_YEAR, eyear); 978 internalSet(UCAL_ERA, cycle); 979 internalSet(UCAL_YEAR, yearOfCycle); 980 internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); 981 internalSet(UCAL_DAY_OF_YEAR, dayOfYear); 982 } 983 984 //------------------------------------------------------------------ 985 // Fields to time 986 //------------------------------------------------------------------ 987 988 namespace { 989 990 /** 991 * Return the Chinese new year of the given Gregorian year. 992 * @param setting setting (time zone and caches) for the Astro calculation. 993 * @param gyear a Gregorian year 994 * @return days after January 1, 1970 0:00 astronomical base zone of the 995 * Chinese new year of the given year (this will be a new moon) 996 */ 997 int32_t newYear(const icu::ChineseCalendar::Setting& setting, 998 int32_t gyear, UErrorCode& status) { 999 if (U_FAILURE(status)) { 1000 return 0; 1001 } 1002 const TimeZone* timeZone = setting.zoneAstroCalc; 1003 int32_t cacheValue = CalendarCache::get(setting.newYearCache, gyear, status); 1004 if (U_FAILURE(status)) { 1005 return 0; 1006 } 1007 1008 if (cacheValue == 0) { 1009 1010 int32_t gprevious_year; 1011 if (uprv_add32_overflow(gyear, -1, &gprevious_year)) { 1012 status = U_ILLEGAL_ARGUMENT_ERROR; 1013 return 0; 1014 } 1015 int32_t solsticeBefore= winterSolstice(setting, gprevious_year, status); 1016 int32_t solsticeAfter = winterSolstice(setting, gyear, status); 1017 int32_t newMoon1 = newMoonNear(timeZone, solsticeBefore + 1, true, status); 1018 int32_t newMoon2 = newMoonNear(timeZone, newMoon1 + SYNODIC_GAP, true, status); 1019 int32_t newMoon11 = newMoonNear(timeZone, solsticeAfter + 1, false, status); 1020 if (U_FAILURE(status)) { 1021 return 0; 1022 } 1023 1024 if (synodicMonthsBetween(newMoon1, newMoon11) == 12 && 1025 (hasNoMajorSolarTerm(timeZone, newMoon1, status) || 1026 hasNoMajorSolarTerm(timeZone, newMoon2, status))) { 1027 cacheValue = newMoonNear(timeZone, newMoon2 + SYNODIC_GAP, true, status); 1028 } else { 1029 cacheValue = newMoon2; 1030 } 1031 if (U_FAILURE(status)) { 1032 return 0; 1033 } 1034 1035 CalendarCache::put(setting.newYearCache, gyear, cacheValue, status); 1036 } 1037 if(U_FAILURE(status)) { 1038 cacheValue = 0; 1039 } 1040 return cacheValue; 1041 } 1042 1043 } // namespace 1044 1045 /** 1046 * Adjust this calendar to be delta months before or after a given 1047 * start position, pinning the day of month if necessary. The start 1048 * position is given as a local days number for the start of the month 1049 * and a day-of-month. Used by add() and roll(). 1050 * @param newMoon the local days of the first day of the month of the 1051 * start position (days after January 1, 1970 0:00 Asia/Shanghai) 1052 * @param dayOfMonth the 1-based day-of-month of the start position 1053 * @param delta the number of months to move forward or backward from 1054 * the start position 1055 * @param status The status. 1056 */ 1057 void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dayOfMonth, int32_t delta, 1058 UErrorCode& status) { 1059 const Setting setting = getSetting(status); 1060 if (U_FAILURE(status)) { 1061 return; 1062 } 1063 1064 // Move to the middle of the month before our target month. 1065 double value = newMoon; 1066 value += (CalendarAstronomer::SYNODIC_MONTH * 1067 (static_cast<double>(delta) - 0.5)); 1068 if (value < INT32_MIN || value > INT32_MAX) { 1069 status = U_ILLEGAL_ARGUMENT_ERROR; 1070 return; 1071 } 1072 newMoon = static_cast<int32_t>(value); 1073 1074 // Search forward to the target month's new moon 1075 newMoon = newMoonNear(setting.zoneAstroCalc, newMoon, true, status); 1076 if (U_FAILURE(status)) { 1077 return; 1078 } 1079 1080 // Find the target dayOfMonth 1081 int32_t jd; 1082 if (uprv_add32_overflow(newMoon, kEpochStartAsJulianDay - 1, &jd) || 1083 uprv_add32_overflow(jd, dayOfMonth, &jd)) { 1084 status = U_ILLEGAL_ARGUMENT_ERROR; 1085 return; 1086 } 1087 1088 // Pin the dayOfMonth. In this calendar all months are 29 or 30 days 1089 // so pinning just means handling dayOfMonth 30. 1090 if (dayOfMonth > 29) { 1091 set(UCAL_JULIAN_DAY, jd-1); 1092 // TODO Fix this. We really shouldn't ever have to 1093 // explicitly call complete(). This is either a bug in 1094 // this method, in ChineseCalendar, or in 1095 // Calendar.getActualMaximum(). I suspect the last. 1096 complete(status); 1097 if (U_FAILURE(status)) return; 1098 if (getActualMaximum(UCAL_DAY_OF_MONTH, status) >= dayOfMonth) { 1099 if (U_FAILURE(status)) return; 1100 set(UCAL_JULIAN_DAY, jd); 1101 } 1102 } else { 1103 set(UCAL_JULIAN_DAY, jd); 1104 } 1105 } 1106 1107 IMPL_SYSTEM_DEFAULT_CENTURY(ChineseCalendar, "@calendar=chinese") 1108 1109 bool 1110 ChineseCalendar::inTemporalLeapYear(UErrorCode &status) const 1111 { 1112 int32_t days = getActualMaximum(UCAL_DAY_OF_YEAR, status); 1113 if (U_FAILURE(status)) return false; 1114 return days > 360; 1115 } 1116 1117 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar) 1118 1119 1120 static const char * const gTemporalLeapMonthCodes[] = { 1121 "M01L", "M02L", "M03L", "M04L", "M05L", "M06L", 1122 "M07L", "M08L", "M09L", "M10L", "M11L", "M12L", nullptr 1123 }; 1124 1125 const char* ChineseCalendar::getTemporalMonthCode(UErrorCode &status) const { 1126 // We need to call get, not internalGet, to force the calculation 1127 // from UCAL_ORDINAL_MONTH. 1128 int32_t is_leap = get(UCAL_IS_LEAP_MONTH, status); 1129 if (U_FAILURE(status)) return nullptr; 1130 if (is_leap != 0) { 1131 int32_t month = get(UCAL_MONTH, status); 1132 if (U_FAILURE(status)) return nullptr; 1133 return gTemporalLeapMonthCodes[month]; 1134 } 1135 return Calendar::getTemporalMonthCode(status); 1136 } 1137 1138 void 1139 ChineseCalendar::setTemporalMonthCode(const char* code, UErrorCode& status ) 1140 { 1141 if (U_FAILURE(status)) return; 1142 int32_t len = static_cast<int32_t>(uprv_strlen(code)); 1143 if (len != 4 || code[0] != 'M' || code[3] != 'L') { 1144 set(UCAL_IS_LEAP_MONTH, 0); 1145 return Calendar::setTemporalMonthCode(code, status); 1146 } 1147 for (int m = 0; gTemporalLeapMonthCodes[m] != nullptr; m++) { 1148 if (uprv_strcmp(code, gTemporalLeapMonthCodes[m]) == 0) { 1149 set(UCAL_MONTH, m); 1150 set(UCAL_IS_LEAP_MONTH, 1); 1151 return; 1152 } 1153 } 1154 status = U_ILLEGAL_ARGUMENT_ERROR; 1155 } 1156 1157 int32_t ChineseCalendar::internalGetMonth(UErrorCode& status) const { 1158 if (U_FAILURE(status)) { 1159 return 0; 1160 } 1161 if (resolveFields(kMonthPrecedence) == UCAL_MONTH) { 1162 return internalGet(UCAL_MONTH); 1163 } 1164 LocalPointer<Calendar> temp(this->clone()); 1165 temp->set(UCAL_MONTH, 0); 1166 temp->set(UCAL_IS_LEAP_MONTH, 0); 1167 temp->set(UCAL_DATE, 1); 1168 // Calculate the UCAL_MONTH and UCAL_IS_LEAP_MONTH by adding number of 1169 // months. 1170 temp->roll(UCAL_MONTH, internalGet(UCAL_ORDINAL_MONTH), status); 1171 if (U_FAILURE(status)) { 1172 return 0; 1173 } 1174 1175 ChineseCalendar* nonConstThis = const_cast<ChineseCalendar*>(this); // cast away const 1176 nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, temp->get(UCAL_IS_LEAP_MONTH, status)); 1177 int32_t month = temp->get(UCAL_MONTH, status); 1178 if (U_FAILURE(status)) { 1179 return 0; 1180 } 1181 nonConstThis->internalSet(UCAL_MONTH, month); 1182 return month; 1183 } 1184 1185 int32_t ChineseCalendar::internalGetMonth(int32_t defaultValue, UErrorCode& status) const { 1186 if (U_FAILURE(status)) { 1187 return 0; 1188 } 1189 switch (resolveFields(kMonthPrecedence)) { 1190 case UCAL_MONTH: 1191 return internalGet(UCAL_MONTH); 1192 case UCAL_ORDINAL_MONTH: 1193 return internalGetMonth(status); 1194 default: 1195 return defaultValue; 1196 } 1197 } 1198 1199 ChineseCalendar::Setting ChineseCalendar::getSetting(UErrorCode&) const { 1200 return { 1201 getAstronomerTimeZone(), 1202 &gWinterSolsticeCache, 1203 &gNewYearCache 1204 }; 1205 } 1206 1207 int32_t 1208 ChineseCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const 1209 { 1210 if (U_FAILURE(status)) { 1211 return 0; 1212 } 1213 if (field == UCAL_DATE) { 1214 LocalPointer<ChineseCalendar> cal(clone(), status); 1215 if(U_FAILURE(status)) { 1216 return 0; 1217 } 1218 cal->setLenient(true); 1219 cal->prepareGetActual(field,false,status); 1220 int32_t year = cal->get(UCAL_EXTENDED_YEAR, status); 1221 int32_t month = cal->get(UCAL_MONTH, status); 1222 bool leap = cal->get(UCAL_IS_LEAP_MONTH, status) != 0; 1223 return handleGetMonthLengthWithLeap(year, month, leap, status); 1224 } 1225 return Calendar::getActualMaximum(field, status); 1226 } 1227 1228 U_NAMESPACE_END 1229 1230 #endif