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