intl/icu/source/i18n/chnsecal.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial