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