1/* 2 ****************************************************************************** 3 * Copyright (C) 2007-2014, International Business Machines Corporation 4 * and others. All Rights Reserved. 5 ****************************************************************************** 6 * 7 * File CHNSECAL.CPP 8 * 9 * Modification History: 10 * 11 * Date Name Description 12 * 9/18/2007 ajmacher ported from java ChineseCalendar 13 ***************************************************************************** 14 */ 15 16#include "chnsecal.h" 17 18#if !UCONFIG_NO_FORMATTING 19 20#include "umutex.h" 21#include <float.h> 22#include "gregoimp.h" // Math 23#include "astro.h" // CalendarAstronomer 24#include "unicode/simpletz.h" 25#include "uhash.h" 26#include "ucln_in.h" 27 28// Debugging 29#ifdef U_DEBUG_CHNSECAL 30# include <stdio.h> 31# include <stdarg.h> 32static void debug_chnsecal_loc(const char *f, int32_t l) 33{ 34 fprintf(stderr, "%s:%d: ", f, l); 35} 36 37static void debug_chnsecal_msg(const char *pat, ...) 38{ 39 va_list ap; 40 va_start(ap, pat); 41 vfprintf(stderr, pat, ap); 42 fflush(stderr); 43} 44// must use double parens, i.e.: U_DEBUG_CHNSECAL_MSG(("four is: %d",4)); 45#define U_DEBUG_CHNSECAL_MSG(x) {debug_chnsecal_loc(__FILE__,__LINE__);debug_chnsecal_msg x;} 46#else 47#define U_DEBUG_CHNSECAL_MSG(x) 48#endif 49 50 51// --- The cache -- 52static UMutex astroLock = U_MUTEX_INITIALIZER; // pod bay door lock 53static icu::CalendarAstronomer *gChineseCalendarAstro = NULL; 54static icu::CalendarCache *gChineseCalendarWinterSolsticeCache = NULL; 55static icu::CalendarCache *gChineseCalendarNewYearCache = NULL; 56static icu::TimeZone *gChineseCalendarZoneAstroCalc = NULL; 57static icu::UInitOnce gChineseCalendarZoneAstroCalcInitOnce = U_INITONCE_INITIALIZER; 58 59/** 60 * The start year of the Chinese calendar, the 61st year of the reign 61 * of Huang Di. Some sources use the first year of his reign, 62 * resulting in EXTENDED_YEAR values 60 years greater and ERA (cycle) 63 * values one greater. 64 */ 65static const int32_t CHINESE_EPOCH_YEAR = -2636; // Gregorian year 66 67/** 68 * The offset from GMT in milliseconds at which we perform astronomical 69 * computations. Some sources use a different historically accurate 70 * offset of GMT+7:45:40 for years before 1929; we do not do this. 71 */ 72static const int32_t CHINA_OFFSET = 8 * kOneHour; 73 74/** 75 * Value to be added or subtracted from the local days of a new moon to 76 * get close to the next or prior new moon, but not cross it. Must be 77 * >= 1 and < CalendarAstronomer.SYNODIC_MONTH. 78 */ 79static const int32_t SYNODIC_GAP = 25; 80 81 82U_CDECL_BEGIN 83static UBool calendar_chinese_cleanup(void) { 84 if (gChineseCalendarAstro) { 85 delete gChineseCalendarAstro; 86 gChineseCalendarAstro = NULL; 87 } 88 if (gChineseCalendarWinterSolsticeCache) { 89 delete gChineseCalendarWinterSolsticeCache; 90 gChineseCalendarWinterSolsticeCache = NULL; 91 } 92 if (gChineseCalendarNewYearCache) { 93 delete gChineseCalendarNewYearCache; 94 gChineseCalendarNewYearCache = NULL; 95 } 96 if (gChineseCalendarZoneAstroCalc) { 97 delete gChineseCalendarZoneAstroCalc; 98 gChineseCalendarZoneAstroCalc = NULL; 99 } 100 gChineseCalendarZoneAstroCalcInitOnce.reset(); 101 return TRUE; 102} 103U_CDECL_END 104 105U_NAMESPACE_BEGIN 106 107 108// Implementation of the ChineseCalendar class 109 110 111//------------------------------------------------------------------------- 112// Constructors... 113//------------------------------------------------------------------------- 114 115 116Calendar* ChineseCalendar::clone() const { 117 return new ChineseCalendar(*this); 118} 119 120ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success) 121: Calendar(TimeZone::createDefault(), aLocale, success), 122 isLeapYear(FALSE), 123 fEpochYear(CHINESE_EPOCH_YEAR), 124 fZoneAstroCalc(getChineseCalZoneAstroCalc()) 125{ 126 setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. 127} 128 129ChineseCalendar::ChineseCalendar(const Locale& aLocale, int32_t epochYear, 130 const TimeZone* zoneAstroCalc, UErrorCode &success) 131: Calendar(TimeZone::createDefault(), aLocale, success), 132 isLeapYear(FALSE), 133 fEpochYear(epochYear), 134 fZoneAstroCalc(zoneAstroCalc) 135{ 136 setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. 137} 138 139ChineseCalendar::ChineseCalendar(const ChineseCalendar& other) : Calendar(other) { 140 isLeapYear = other.isLeapYear; 141 fEpochYear = other.fEpochYear; 142 fZoneAstroCalc = other.fZoneAstroCalc; 143} 144 145ChineseCalendar::~ChineseCalendar() 146{ 147} 148 149const char *ChineseCalendar::getType() const { 150 return "chinese"; 151} 152 153static void U_CALLCONV initChineseCalZoneAstroCalc() { 154 gChineseCalendarZoneAstroCalc = new SimpleTimeZone(CHINA_OFFSET, UNICODE_STRING_SIMPLE("CHINA_ZONE") ); 155 ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); 156} 157 158const TimeZone* ChineseCalendar::getChineseCalZoneAstroCalc(void) const { 159 umtx_initOnce(gChineseCalendarZoneAstroCalcInitOnce, &initChineseCalZoneAstroCalc); 160 return gChineseCalendarZoneAstroCalc; 161} 162 163//------------------------------------------------------------------------- 164// Minimum / Maximum access functions 165//------------------------------------------------------------------------- 166 167 168static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { 169 // Minimum Greatest Least Maximum 170 // Minimum Maximum 171 { 1, 1, 83333, 83333}, // ERA 172 { 1, 1, 60, 60}, // YEAR 173 { 0, 0, 11, 11}, // MONTH 174 { 1, 1, 50, 55}, // WEEK_OF_YEAR 175 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH 176 { 1, 1, 29, 30}, // DAY_OF_MONTH 177 { 1, 1, 353, 385}, // DAY_OF_YEAR 178 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK 179 { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH 180 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM 181 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR 182 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY 183 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE 184 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND 185 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND 186 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET 187 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET 188 { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY 189 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL 190 { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR 191 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY 192 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY 193 { 0, 0, 1, 1}, // IS_LEAP_MONTH 194}; 195 196 197/** 198* @draft ICU 2.4 199*/ 200int32_t ChineseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { 201 return LIMITS[field][limitType]; 202} 203 204 205//---------------------------------------------------------------------- 206// Calendar framework 207//---------------------------------------------------------------------- 208 209/** 210 * Implement abstract Calendar method to return the extended year 211 * defined by the current fields. This will use either the ERA and 212 * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR 213 * field as the continuous year count, depending on which is newer. 214 * @stable ICU 2.8 215 */ 216int32_t ChineseCalendar::handleGetExtendedYear() { 217 int32_t year; 218 if (newestStamp(UCAL_ERA, UCAL_YEAR, kUnset) <= fStamp[UCAL_EXTENDED_YEAR]) { 219 year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 220 } else { 221 int32_t cycle = internalGet(UCAL_ERA, 1) - 1; // 0-based cycle 222 // adjust to the instance specific epoch 223 year = cycle * 60 + internalGet(UCAL_YEAR, 1) - (fEpochYear - CHINESE_EPOCH_YEAR); 224 } 225 return year; 226} 227 228/** 229 * Override Calendar method to return the number of days in the given 230 * extended year and month. 231 * 232 * <p>Note: This method also reads the IS_LEAP_MONTH field to determine 233 * whether or not the given month is a leap month. 234 * @stable ICU 2.8 235 */ 236int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { 237 int32_t thisStart = handleComputeMonthStart(extendedYear, month, TRUE) - 238 kEpochStartAsJulianDay + 1; // Julian day -> local days 239 int32_t nextStart = newMoonNear(thisStart + SYNODIC_GAP, TRUE); 240 return nextStart - thisStart; 241} 242 243/** 244 * Override Calendar to compute several fields specific to the Chinese 245 * calendar system. These are: 246 * 247 * <ul><li>ERA 248 * <li>YEAR 249 * <li>MONTH 250 * <li>DAY_OF_MONTH 251 * <li>DAY_OF_YEAR 252 * <li>EXTENDED_YEAR</ul> 253 * 254 * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this 255 * method is called. The getGregorianXxx() methods return Gregorian 256 * calendar equivalents for the given Julian day. 257 * 258 * <p>Compute the ChineseCalendar-specific field IS_LEAP_MONTH. 259 * @stable ICU 2.8 260 */ 261void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) { 262 263 computeChineseFields(julianDay - kEpochStartAsJulianDay, // local days 264 getGregorianYear(), getGregorianMonth(), 265 TRUE); // set all fields 266} 267 268/** 269 * Field resolution table that incorporates IS_LEAP_MONTH. 270 */ 271const UFieldResolutionTable ChineseCalendar::CHINESE_DATE_PRECEDENCE[] = 272{ 273 { 274 { UCAL_DAY_OF_MONTH, kResolveSTOP }, 275 { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP }, 276 { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 277 { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 278 { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP }, 279 { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 280 { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 281 { UCAL_DAY_OF_YEAR, kResolveSTOP }, 282 { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_IS_LEAP_MONTH, kResolveSTOP }, 283 { kResolveSTOP } 284 }, 285 { 286 { UCAL_WEEK_OF_YEAR, kResolveSTOP }, 287 { UCAL_WEEK_OF_MONTH, kResolveSTOP }, 288 { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP }, 289 { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 290 { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 291 { kResolveSTOP } 292 }, 293 {{kResolveSTOP}} 294}; 295 296/** 297 * Override Calendar to add IS_LEAP_MONTH to the field resolution 298 * table. 299 * @stable ICU 2.8 300 */ 301const UFieldResolutionTable* ChineseCalendar::getFieldResolutionTable() const { 302 return CHINESE_DATE_PRECEDENCE; 303} 304 305/** 306 * Return the Julian day number of day before the first day of the 307 * given month in the given extended year. 308 * 309 * <p>Note: This method reads the IS_LEAP_MONTH field to determine 310 * whether the given month is a leap month. 311 * @param eyear the extended year 312 * @param month the zero-based month. The month is also determined 313 * by reading the IS_LEAP_MONTH field. 314 * @return the Julian day number of the day before the first 315 * day of the given month and year 316 * @stable ICU 2.8 317 */ 318int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const { 319 320 ChineseCalendar *nonConstThis = (ChineseCalendar*)this; // cast away const 321 322 // If the month is out of range, adjust it into range, and 323 // modify the extended year value accordingly. 324 if (month < 0 || month > 11) { 325 double m = month; 326 eyear += (int32_t)ClockMath::floorDivide(m, 12.0, m); 327 month = (int32_t)m; 328 } 329 330 int32_t gyear = eyear + fEpochYear - 1; // Gregorian year 331 int32_t theNewYear = newYear(gyear); 332 int32_t newMoon = newMoonNear(theNewYear + month * 29, TRUE); 333 334 int32_t julianDay = newMoon + kEpochStartAsJulianDay; 335 336 // Save fields for later restoration 337 int32_t saveMonth = internalGet(UCAL_MONTH); 338 int32_t saveIsLeapMonth = internalGet(UCAL_IS_LEAP_MONTH); 339 340 // Ignore IS_LEAP_MONTH field if useMonth is false 341 int32_t isLeapMonth = useMonth ? saveIsLeapMonth : 0; 342 343 UErrorCode status = U_ZERO_ERROR; 344 nonConstThis->computeGregorianFields(julianDay, status); 345 if (U_FAILURE(status)) 346 return 0; 347 348 // This will modify the MONTH and IS_LEAP_MONTH fields (only) 349 nonConstThis->computeChineseFields(newMoon, getGregorianYear(), 350 getGregorianMonth(), FALSE); 351 352 if (month != internalGet(UCAL_MONTH) || 353 isLeapMonth != internalGet(UCAL_IS_LEAP_MONTH)) { 354 newMoon = newMoonNear(newMoon + SYNODIC_GAP, TRUE); 355 julianDay = newMoon + kEpochStartAsJulianDay; 356 } 357 358 nonConstThis->internalSet(UCAL_MONTH, saveMonth); 359 nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, saveIsLeapMonth); 360 361 return julianDay - 1; 362} 363 364 365/** 366 * Override Calendar to handle leap months properly. 367 * @stable ICU 2.8 368 */ 369void ChineseCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) { 370 switch (field) { 371 case UCAL_MONTH: 372 if (amount != 0) { 373 int32_t dom = get(UCAL_DAY_OF_MONTH, status); 374 if (U_FAILURE(status)) break; 375 int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day 376 if (U_FAILURE(status)) break; 377 int32_t moon = day - dom + 1; // New moon 378 offsetMonth(moon, dom, amount); 379 } 380 break; 381 default: 382 Calendar::add(field, amount, status); 383 break; 384 } 385} 386 387/** 388 * Override Calendar to handle leap months properly. 389 * @stable ICU 2.8 390 */ 391void ChineseCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) { 392 add((UCalendarDateFields)field, amount, status); 393} 394 395/** 396 * Override Calendar to handle leap months properly. 397 * @stable ICU 2.8 398 */ 399void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) { 400 switch (field) { 401 case UCAL_MONTH: 402 if (amount != 0) { 403 int32_t dom = get(UCAL_DAY_OF_MONTH, status); 404 if (U_FAILURE(status)) break; 405 int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day 406 if (U_FAILURE(status)) break; 407 int32_t moon = day - dom + 1; // New moon (start of this month) 408 409 // Note throughout the following: Months 12 and 1 are never 410 // followed by a leap month (D&R p. 185). 411 412 // Compute the adjusted month number m. This is zero-based 413 // value from 0..11 in a non-leap year, and from 0..12 in a 414 // leap year. 415 int32_t m = get(UCAL_MONTH, status); // 0-based month 416 if (U_FAILURE(status)) break; 417 if (isLeapYear) { // (member variable) 418 if (get(UCAL_IS_LEAP_MONTH, status) == 1) { 419 ++m; 420 } else { 421 // Check for a prior leap month. (In the 422 // following, month 0 is the first month of the 423 // year.) Month 0 is never followed by a leap 424 // month, and we know month m is not a leap month. 425 // moon1 will be the start of month 0 if there is 426 // no leap month between month 0 and month m; 427 // otherwise it will be the start of month 1. 428 int moon1 = moon - 429 (int) (CalendarAstronomer::SYNODIC_MONTH * (m - 0.5)); 430 moon1 = newMoonNear(moon1, TRUE); 431 if (isLeapMonthBetween(moon1, moon)) { 432 ++m; 433 } 434 } 435 if (U_FAILURE(status)) break; 436 } 437 438 // Now do the standard roll computation on m, with the 439 // allowed range of 0..n-1, where n is 12 or 13. 440 int32_t n = isLeapYear ? 13 : 12; // Months in this year 441 int32_t newM = (m + amount) % n; 442 if (newM < 0) { 443 newM += n; 444 } 445 446 if (newM != m) { 447 offsetMonth(moon, dom, newM - m); 448 } 449 } 450 break; 451 default: 452 Calendar::roll(field, amount, status); 453 break; 454 } 455} 456 457void ChineseCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { 458 roll((UCalendarDateFields)field, amount, status); 459} 460 461 462//------------------------------------------------------------------ 463// Support methods and constants 464//------------------------------------------------------------------ 465 466/** 467 * Convert local days to UTC epoch milliseconds. 468 * This is not an accurate conversion in that getTimezoneOffset 469 * takes the milliseconds in GMT (not local time). In theory, more 470 * accurate algorithm can be implemented but practically we do not need 471 * to go through that complication as long as the historical timezone 472 * changes did not happen around the 'tricky' new moon (new moon around 473 * midnight). 474 * 475 * @param days days after January 1, 1970 0:00 in the astronomical base zone 476 * @return milliseconds after January 1, 1970 0:00 GMT 477 */ 478double ChineseCalendar::daysToMillis(double days) const { 479 double millis = days * (double)kOneDay; 480 if (fZoneAstroCalc != NULL) { 481 int32_t rawOffset, dstOffset; 482 UErrorCode status = U_ZERO_ERROR; 483 fZoneAstroCalc->getOffset(millis, FALSE, rawOffset, dstOffset, status); 484 if (U_SUCCESS(status)) { 485 return millis - (double)(rawOffset + dstOffset); 486 } 487 } 488 return millis - (double)CHINA_OFFSET; 489} 490 491/** 492 * Convert UTC epoch milliseconds to local days. 493 * @param millis milliseconds after January 1, 1970 0:00 GMT 494 * @return days after January 1, 1970 0:00 in the astronomical base zone 495 */ 496double ChineseCalendar::millisToDays(double millis) const { 497 if (fZoneAstroCalc != NULL) { 498 int32_t rawOffset, dstOffset; 499 UErrorCode status = U_ZERO_ERROR; 500 fZoneAstroCalc->getOffset(millis, FALSE, rawOffset, dstOffset, status); 501 if (U_SUCCESS(status)) { 502 return ClockMath::floorDivide(millis + (double)(rawOffset + dstOffset), kOneDay); 503 } 504 } 505 return ClockMath::floorDivide(millis + (double)CHINA_OFFSET, kOneDay); 506} 507 508//------------------------------------------------------------------ 509// Astronomical computations 510//------------------------------------------------------------------ 511 512// bit array for gregorian 1900-2100 indicating years in 513// which the linear estimate needs to be adjusted by -1 514static const uint16_t winterSolsticeAdj[] = { 515 0x0001, // 1900-1915, deltas for 1900 516 0x0444, // 1916-1931, deltas for 1918, 1922, 1926 517 0x0000, // 1932-1947 518 0x8880, // 1948-1963, deltas for 1955, 1959, 1963 519 0x0000, // 1964-1979 520 0x1100, // 1980-1995, deltas for 1988, 1992 521 0x0011, // 1996-2011, deltas for 1996, 2000 522 0x2200, // 2012-2027, deltas for 2021, 2025 523 0x0022, // 2028-2043, deltas for 2029, 2033 524 0x4000, // 2044-2059, deltas for 2058 525 0x0444, // 2060-2075, deltas for 2062, 2066, 2070 526 0x8000, // 2076-2091, deltas for 2091 527 0x0088, // 2092-2100, deltas for 2095, 2099 528}; 529 530/** 531 * Return the major solar term on or after December 15 of the given 532 * Gregorian year, that is, the winter solstice of the given year. 533 * Computations are relative to Asia/Shanghai time zone. 534 * @param gyear a Gregorian year 535 * @return days after January 1, 1970 0:00 Asia/Shanghai of the 536 * winter solstice of the given year 537 */ 538int32_t ChineseCalendar::winterSolstice(int32_t gyear) const { 539 if (gyear >= 1900 && gyear <= 2100) { 540 // Don't use cache, just return linear estimate + table correction 541 int32_t gyearadj = gyear - 1900; 542 int32_t result = (int32_t)(365.243*((double)gyearadj) - 0.3) - 25211; 543 uint16_t bitmap = winterSolsticeAdj[gyearadj / 16]; 544 if (bitmap != 0) { 545 uint16_t bitmask = 1 << (gyearadj % 16); 546 if ((bitmask & bitmap) != 0) { 547 result--; 548 } 549 } 550 return result; 551 } 552 553 UErrorCode status = U_ZERO_ERROR; 554 int32_t cacheValue = CalendarCache::get(&gChineseCalendarWinterSolsticeCache, gyear, status); 555 556 if (cacheValue == 0) { 557 // In books December 15 is used, but it fails for some years 558 // using our algorithms, e.g.: 1298 1391 1492 1553 1560. That 559 // is, winterSolstice(1298) starts search at Dec 14 08:00:00 560 // PST 1298 with a final result of Dec 14 10:31:59 PST 1299. 561 double ms = daysToMillis(Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1)); 562 563 umtx_lock(&astroLock); 564 if(gChineseCalendarAstro == NULL) { 565 gChineseCalendarAstro = new CalendarAstronomer(); 566 ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); 567 } 568 gChineseCalendarAstro->setTime(ms); 569 UDate solarLong = gChineseCalendarAstro->getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), TRUE); 570 umtx_unlock(&astroLock); 571 572 // Winter solstice is 270 degrees solar longitude aka Dongzhi 573 cacheValue = (int32_t)millisToDays(solarLong); 574 CalendarCache::put(&gChineseCalendarWinterSolsticeCache, gyear, cacheValue, status); 575 } 576 if(U_FAILURE(status)) { 577 cacheValue = 0; 578 } 579 return cacheValue; 580} 581 582/** 583 * Return the closest new moon to the given date, searching either 584 * forward or backward in time. 585 * @param days days after January 1, 1970 0:00 Asia/Shanghai 586 * @param after if true, search for a new moon on or after the given 587 * date; otherwise, search for a new moon before it 588 * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest 589 * new moon after or before <code>days</code> 590 */ 591int32_t ChineseCalendar::newMoonNear(double days, UBool after) const { 592 double ms = daysToMillis(days); 593 // Try to get the new moon via static function directly from the table in 594 // CalendarAstronomer (for approx gregorian range 1900-2100) without having 595 // to use a CalendarAstronomer instance which requires a lock. This still 596 // involves extra conversion to/from millis. If static function returns 0 597 // we are out of its range and need to use the full machinery. 598 UDate newMoon = CalendarAstronomer::getNewMoonTimeInRange(ms, after); 599 if (newMoon == 0.0) { 600 umtx_lock(&astroLock); 601 if(gChineseCalendarAstro == NULL) { 602 gChineseCalendarAstro = new CalendarAstronomer(); 603 ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); 604 } 605 gChineseCalendarAstro->setTime(ms); 606 newMoon = gChineseCalendarAstro->getMoonTime(CalendarAstronomer::NEW_MOON(), after); 607 umtx_unlock(&astroLock); 608 } 609 610 return (int32_t) millisToDays(newMoon); 611} 612 613/** 614 * Return the nearest integer number of synodic months between 615 * two dates. 616 * @param day1 days after January 1, 1970 0:00 Asia/Shanghai 617 * @param day2 days after January 1, 1970 0:00 Asia/Shanghai 618 * @return the nearest integer number of months between day1 and day2 619 */ 620int32_t ChineseCalendar::synodicMonthsBetween(int32_t day1, int32_t day2) const { 621 double roundme = ((day2 - day1) / CalendarAstronomer::SYNODIC_MONTH); 622 return (int32_t) (roundme + (roundme >= 0 ? .5 : -.5)); 623} 624 625/** 626 * Return the major solar term on or before a given date. This 627 * will be an integer from 1..12, with 1 corresponding to 330 degrees, 628 * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees. 629 * @param days days after January 1, 1970 0:00 Asia/Shanghai 630 */ 631int32_t ChineseCalendar::majorSolarTerm(int32_t days) const { 632 633 double ms = daysToMillis(days); 634 UDate solarLongitude = CalendarAstronomer::getSunLongitudeForTime(ms); 635 636 // There was almost never any benefit to using the CalendarAstronomer instance; 637 // it could cache intermediate results, but we rarely used it multiple times in 638 // succession for the same setTime value, so the intermediate results got 639 // discarded anyway. 640 // 641 // Deleted call to gChineseCalendarAstro->getSunLongitude() now that 642 // we use CalendarAstronomer::getSunLongitudeForTime() 643 644 // Compute (floor(solarLongitude / (pi/6)) + 2) % 12 645 int32_t term = ( ((int32_t)(6 * solarLongitude / CalendarAstronomer::PI)) + 2 ) % 12; 646 if (term < 1) { 647 term += 12; 648 } 649 return term; 650} 651 652/** 653 * Return true if the given month lacks a major solar term. 654 * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new 655 * moon 656 */ 657UBool ChineseCalendar::hasNoMajorSolarTerm(int32_t newMoon) const { 658 return majorSolarTerm(newMoon) == 659 majorSolarTerm(newMoonNear(newMoon + SYNODIC_GAP, TRUE)); 660} 661 662 663//------------------------------------------------------------------ 664// Time to fields 665//------------------------------------------------------------------ 666 667/** 668 * Return true if there is a leap month on or after month newMoon1 and 669 * at or before month newMoon2. 670 * @param newMoon1 days after January 1, 1970 0:00 astronomical base zone 671 * of a new moon 672 * @param newMoon2 days after January 1, 1970 0:00 astronomical base zone 673 * of a new moon 674 */ 675UBool ChineseCalendar::isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) const { 676 677#ifdef U_DEBUG_CHNSECAL 678 // This is only needed to debug the timeOfAngle divergence bug. 679 // Remove this later. Liu 11/9/00 680 if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) { 681 U_DEBUG_CHNSECAL_MSG(( 682 "isLeapMonthBetween(%d, %d): Invalid parameters", newMoon1, newMoon2 683 )); 684 } 685#endif 686 687 return (newMoon2 >= newMoon1) && 688 (isLeapMonthBetween(newMoon1, newMoonNear(newMoon2 - SYNODIC_GAP, FALSE)) || 689 hasNoMajorSolarTerm(newMoon2)); 690} 691 692/** 693 * Compute fields for the Chinese calendar system. This method can 694 * either set all relevant fields, as required by 695 * <code>handleComputeFields()</code>, or it can just set the MONTH and 696 * IS_LEAP_MONTH fields, as required by 697 * <code>handleComputeMonthStart()</code>. 698 * 699 * <p>As a side effect, this method sets {@link #isLeapYear}. 700 * @param days days after January 1, 1970 0:00 astronomical base zone 701 * of the date to compute fields for 702 * @param gyear the Gregorian year of the given date 703 * @param gmonth the Gregorian month of the given date 704 * @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR, 705 * DAY_OF_MONTH, and DAY_OF_YEAR fields. In either case set the MONTH 706 * and IS_LEAP_MONTH fields. 707 */ 708void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t gmonth, 709 UBool setAllFields) { 710 711 // Find the winter solstices before and after the target date. 712 // These define the boundaries of this Chinese year, specifically, 713 // the position of month 11, which always contains the solstice. 714 // We want solsticeBefore <= date < solsticeAfter. 715 int32_t solsticeBefore; 716 int32_t solsticeAfter = winterSolstice(gyear); 717 if (days < solsticeAfter) { 718 solsticeBefore = winterSolstice(gyear - 1); 719 } else { 720 solsticeBefore = solsticeAfter; 721 solsticeAfter = winterSolstice(gyear + 1); 722 } 723 724 // Find the start of the month after month 11. This will be either 725 // the prior month 12 or leap month 11 (very rare). Also find the 726 // start of the following month 11. 727 int32_t firstMoon = newMoonNear(solsticeBefore + 1, TRUE); 728 int32_t lastMoon = newMoonNear(solsticeAfter + 1, FALSE); 729 int32_t thisMoon = newMoonNear(days + 1, FALSE); // Start of this month 730 // Note: isLeapYear is a member variable 731 isLeapYear = synodicMonthsBetween(firstMoon, lastMoon) == 12; 732 733 int32_t month = synodicMonthsBetween(firstMoon, thisMoon); 734 if (isLeapYear && isLeapMonthBetween(firstMoon, thisMoon)) { 735 month--; 736 } 737 if (month < 1) { 738 month += 12; 739 } 740 741 UBool isLeapMonth = isLeapYear && 742 hasNoMajorSolarTerm(thisMoon) && 743 !isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, FALSE)); 744 745 internalSet(UCAL_MONTH, month-1); // Convert from 1-based to 0-based 746 internalSet(UCAL_IS_LEAP_MONTH, isLeapMonth?1:0); 747 748 if (setAllFields) { 749 750 // Extended year and cycle year is based on the epoch year 751 752 int32_t extended_year = gyear - fEpochYear; 753 int cycle_year = gyear - CHINESE_EPOCH_YEAR; 754 if (month < 11 || 755 gmonth >= UCAL_JULY) { 756 extended_year++; 757 cycle_year++; 758 } 759 int32_t dayOfMonth = days - thisMoon + 1; 760 761 internalSet(UCAL_EXTENDED_YEAR, extended_year); 762 763 // 0->0,60 1->1,1 60->1,60 61->2,1 etc. 764 int32_t yearOfCycle; 765 int32_t cycle = ClockMath::floorDivide(cycle_year - 1, 60, yearOfCycle); 766 internalSet(UCAL_ERA, cycle + 1); 767 internalSet(UCAL_YEAR, yearOfCycle + 1); 768 769 internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); 770 771 // Days will be before the first new year we compute if this 772 // date is in month 11, leap 11, 12. There is never a leap 12. 773 // New year computations are cached so this should be cheap in 774 // the long run. 775 int32_t theNewYear = newYear(gyear); 776 if (days < theNewYear) { 777 theNewYear = newYear(gyear-1); 778 } 779 internalSet(UCAL_DAY_OF_YEAR, days - theNewYear + 1); 780 } 781} 782 783 784//------------------------------------------------------------------ 785// Fields to time 786//------------------------------------------------------------------ 787 788// for gyear 1900 through 2100, corrections to linear estimate of newYear 789static const int8_t newYearAdj[] = { 790 -5, 14, 3, -7, 11, -1, -11, 8, -3, -14, 5, -6, 13, 1, -10, 9, -1, -13, 6, -4, // 1900-1919 791 15, 3, -8, 11, 0, -12, 8, -3, -13, 5, -6, 12, 1, -10, 9, -1, -12, 6, -5, 14, // 1920-1939 792 3, -9, 10, 0, -11, 9, -3, -14, 5, -6, 12, 1, -9, 10, -2, -12, 7, -4, 13, 3, // 1940-1959 793 -8, 11, 0, -11, 8, -2, -15, 4, -6, 13, 1, -9, 10, -1, -13, 6, -5, 14, 2, -8, // 1960-1979 794 11, 1, -11, 8, -3, 16, 5, -7, 12, 2, -8, 10, -1, -12, 6, -5, 14, 3, -7, 11, // 1980-1999 795 0, -11, 8, -4, -14, 5, -6, 13, 2, -9, 10, -2, -13, 6, -4, 14, 3, -7, 12, 0, // 2000-2019 796 -11, 8, -3, -14, 5, -6, 13, 2, -10, 9, -1, -12, 6, -4, 15, 4, -8, 11, 0, -11, // 2020-2039 797 7, -3, -13, 6, -6, 13, 2, -9, 9, -2, -12, 7, -4, 15, 4, -7, 10, 0, -11, 8, // 2040-2059 798 -3, -14, 5, -6, 12, 1, -9, 10, -1, -12, 7, -4, 15, 3, -8, 11, 1, -11, 8, -2, // 2060-2079 799 -13, 5, -6, 13, 2, -9, 10, -1, -11, 6, -5, 14, 3, -8, 11, 1, -10, 8, -3, -14, // 2080-2099 800 5 // 2100 801}; 802 803/** 804 * Return the Chinese new year of the given Gregorian year. 805 * @param gyear a Gregorian year 806 * @return days after January 1, 1970 0:00 astronomical base zone of the 807 * Chinese new year of the given year (this will be a new moon) 808 */ 809int32_t ChineseCalendar::newYear(int32_t gyear) const { 810 if (gyear >= 1900 && gyear <= 2100) { 811 // Don't use cache, just return linear estimate + table correction 812 int32_t gyearadj = gyear - 1900; 813 return (int32_t)(365.244*((double)gyearadj)) - 25532 + newYearAdj[gyearadj]; 814 } 815 816 UErrorCode status = U_ZERO_ERROR; 817 int32_t cacheValue = CalendarCache::get(&gChineseCalendarNewYearCache, gyear, status); 818 819 if (cacheValue == 0) { 820 821 int32_t solsticeBefore= winterSolstice(gyear - 1); 822 int32_t solsticeAfter = winterSolstice(gyear); 823 int32_t newMoon1 = newMoonNear(solsticeBefore + 1, TRUE); 824 int32_t newMoon2 = newMoonNear(newMoon1 + SYNODIC_GAP, TRUE); 825 int32_t newMoon11 = newMoonNear(solsticeAfter + 1, FALSE); 826 827 if (synodicMonthsBetween(newMoon1, newMoon11) == 12 && 828 (hasNoMajorSolarTerm(newMoon1) || hasNoMajorSolarTerm(newMoon2))) { 829 cacheValue = newMoonNear(newMoon2 + SYNODIC_GAP, TRUE); 830 } else { 831 cacheValue = newMoon2; 832 } 833 834 CalendarCache::put(&gChineseCalendarNewYearCache, gyear, cacheValue, status); 835 } 836 if(U_FAILURE(status)) { 837 cacheValue = 0; 838 } 839 return cacheValue; 840} 841 842/** 843 * Adjust this calendar to be delta months before or after a given 844 * start position, pinning the day of month if necessary. The start 845 * position is given as a local days number for the start of the month 846 * and a day-of-month. Used by add() and roll(). 847 * @param newMoon the local days of the first day of the month of the 848 * start position (days after January 1, 1970 0:00 Asia/Shanghai) 849 * @param dom the 1-based day-of-month of the start position 850 * @param delta the number of months to move forward or backward from 851 * the start position 852 */ 853void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dom, int32_t delta) { 854 UErrorCode status = U_ZERO_ERROR; 855 856 // Move to the middle of the month before our target month. 857 newMoon += (int32_t) (CalendarAstronomer::SYNODIC_MONTH * (delta - 0.5)); 858 859 // Search forward to the target month's new moon 860 newMoon = newMoonNear(newMoon, TRUE); 861 862 // Find the target dom 863 int32_t jd = newMoon + kEpochStartAsJulianDay - 1 + dom; 864 865 // Pin the dom. In this calendar all months are 29 or 30 days 866 // so pinning just means handling dom 30. 867 if (dom > 29) { 868 set(UCAL_JULIAN_DAY, jd-1); 869 // TODO Fix this. We really shouldn't ever have to 870 // explicitly call complete(). This is either a bug in 871 // this method, in ChineseCalendar, or in 872 // Calendar.getActualMaximum(). I suspect the last. 873 complete(status); 874 if (U_FAILURE(status)) return; 875 if (getActualMaximum(UCAL_DAY_OF_MONTH, status) >= dom) { 876 if (U_FAILURE(status)) return; 877 set(UCAL_JULIAN_DAY, jd); 878 } 879 } else { 880 set(UCAL_JULIAN_DAY, jd); 881 } 882} 883 884 885UBool 886ChineseCalendar::inDaylightTime(UErrorCode& status) const 887{ 888 // copied from GregorianCalendar 889 if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) 890 return FALSE; 891 892 // Force an update of the state of the Calendar. 893 ((ChineseCalendar*)this)->complete(status); // cast away const 894 895 return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE); 896} 897 898// default century 899 900static UDate gSystemDefaultCenturyStart = DBL_MIN; 901static int32_t gSystemDefaultCenturyStartYear = -1; 902static icu::UInitOnce gSystemDefaultCenturyInitOnce = U_INITONCE_INITIALIZER; 903 904 905UBool ChineseCalendar::haveDefaultCentury() const 906{ 907 return TRUE; 908} 909 910UDate ChineseCalendar::defaultCenturyStart() const 911{ 912 return internalGetDefaultCenturyStart(); 913} 914 915int32_t ChineseCalendar::defaultCenturyStartYear() const 916{ 917 return internalGetDefaultCenturyStartYear(); 918} 919 920static void U_CALLCONV initializeSystemDefaultCentury() 921{ 922 // initialize systemDefaultCentury and systemDefaultCenturyYear based 923 // on the current time. They'll be set to 80 years before 924 // the current time. 925 UErrorCode status = U_ZERO_ERROR; 926 ChineseCalendar calendar(Locale("@calendar=chinese"),status); 927 if (U_SUCCESS(status)) { 928 calendar.setTime(Calendar::getNow(), status); 929 calendar.add(UCAL_YEAR, -80, status); 930 gSystemDefaultCenturyStart = calendar.getTime(status); 931 gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); 932 } 933 // We have no recourse upon failure unless we want to propagate the failure 934 // out. 935} 936 937UDate 938ChineseCalendar::internalGetDefaultCenturyStart() const 939{ 940 // lazy-evaluate systemDefaultCenturyStart 941 umtx_initOnce(gSystemDefaultCenturyInitOnce, &initializeSystemDefaultCentury); 942 return gSystemDefaultCenturyStart; 943} 944 945int32_t 946ChineseCalendar::internalGetDefaultCenturyStartYear() const 947{ 948 // lazy-evaluate systemDefaultCenturyStartYear 949 umtx_initOnce(gSystemDefaultCenturyInitOnce, &initializeSystemDefaultCentury); 950 return gSystemDefaultCenturyStartYear; 951} 952 953UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar) 954 955U_NAMESPACE_END 956 957#endif 958 959