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