]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/chnsecal.cpp
ICU-64260.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / chnsecal.cpp
index 52cb6c86d9851e63ca4f2dc85dd31d656320861d..a60a74f185378ad3418898c092a8c21f2ee0a8e2 100644 (file)
@@ -1,6 +1,8 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
  ******************************************************************************
- * Copyright (C) 2007-2008, International Business Machines Corporation
+ * Copyright (C) 2007-2014, International Business Machines Corporation
  * and others. All Rights Reserved.
  ******************************************************************************
  *
@@ -21,6 +23,7 @@
 #include <float.h>
 #include "gregoimp.h" // Math
 #include "astro.h" // CalendarAstronomer
+#include "unicode/simpletz.h"
 #include "uhash.h"
 #include "ucln_in.h"
 
@@ -48,10 +51,18 @@ static void debug_chnsecal_msg(const char *pat, ...)
 
 
 // --- The cache --
-static UMTX astroLock = 0;  // pod bay door lock
-static U_NAMESPACE_QUALIFIER CalendarAstronomer *gChineseCalendarAstro = NULL;
-static U_NAMESPACE_QUALIFIER CalendarCache *gChineseCalendarWinterSolsticeCache = NULL;
-static U_NAMESPACE_QUALIFIER CalendarCache *gChineseCalendarNewYearCache = NULL;
+static icu::UMutex *astroLock() {  // Protects access to gChineseCalendarAstro.
+    static icu::UMutex *m = STATIC_NEW(icu::UMutex);
+    return m;
+}
+static icu::CalendarAstronomer *gChineseCalendarAstro = NULL;
+
+// Lazy Creation & Access synchronized by class CalendarCache with a mutex.
+static icu::CalendarCache *gChineseCalendarWinterSolsticeCache = NULL;
+static icu::CalendarCache *gChineseCalendarNewYearCache = NULL;
+
+static icu::TimeZone *gChineseCalendarZoneAstroCalc = NULL;
+static icu::UInitOnce gChineseCalendarZoneAstroCalcInitOnce = U_INITONCE_INITIALIZER;
 
 /**
  * The start year of the Chinese calendar, the 61st year of the reign
@@ -66,7 +77,7 @@ static const int32_t CHINESE_EPOCH_YEAR = -2636; // Gregorian year
  * computations.  Some sources use a different historically accurate
  * offset of GMT+7:45:40 for years before 1929; we do not do this.
  */
-static const double CHINA_OFFSET = 8 * kOneHour;
+static const int32_t CHINA_OFFSET = 8 * kOneHour;
 
 /**
  * Value to be added or subtracted from the local days of a new moon to
@@ -90,7 +101,11 @@ static UBool calendar_chinese_cleanup(void) {
         delete gChineseCalendarNewYearCache;
         gChineseCalendarNewYearCache = NULL;
     }
-    umtx_destroy(&astroLock);
+    if (gChineseCalendarZoneAstroCalc) {
+        delete gChineseCalendarZoneAstroCalc;
+        gChineseCalendarZoneAstroCalc = NULL;
+    }
+    gChineseCalendarZoneAstroCalcInitOnce.reset();
     return TRUE;
 }
 U_CDECL_END
@@ -111,14 +126,28 @@ Calendar* ChineseCalendar::clone() const {
 }
 
 ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success)
-:   Calendar(TimeZone::createDefault(), aLocale, success)
+:   Calendar(TimeZone::createDefault(), aLocale, success),
+    isLeapYear(FALSE),
+    fEpochYear(CHINESE_EPOCH_YEAR),
+    fZoneAstroCalc(getChineseCalZoneAstroCalc())
+{
+    setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
+}
+
+ChineseCalendar::ChineseCalendar(const Locale& aLocale, int32_t epochYear,
+                                const TimeZone* zoneAstroCalc, UErrorCode &success)
+:   Calendar(TimeZone::createDefault(), aLocale, success),
+    isLeapYear(FALSE),
+    fEpochYear(epochYear),
+    fZoneAstroCalc(zoneAstroCalc)
 {
-    isLeapYear = FALSE;
     setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
 }
 
 ChineseCalendar::ChineseCalendar(const ChineseCalendar& other) : Calendar(other) {
     isLeapYear = other.isLeapYear;
+    fEpochYear = other.fEpochYear;
+    fZoneAstroCalc = other.fZoneAstroCalc;
 }
 
 ChineseCalendar::~ChineseCalendar()
@@ -129,6 +158,16 @@ const char *ChineseCalendar::getType() const {
     return "chinese";
 }
 
+static void U_CALLCONV initChineseCalZoneAstroCalc() {
+    gChineseCalendarZoneAstroCalc = new SimpleTimeZone(CHINA_OFFSET, UNICODE_STRING_SIMPLE("CHINA_ZONE") );
+    ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
+}
+
+const TimeZone* ChineseCalendar::getChineseCalZoneAstroCalc(void) const {
+    umtx_initOnce(gChineseCalendarZoneAstroCalcInitOnce, &initChineseCalZoneAstroCalc);
+    return gChineseCalendarZoneAstroCalc;
+}
+
 //-------------------------------------------------------------------------
 // Minimum / Maximum access functions
 //-------------------------------------------------------------------------
@@ -188,7 +227,8 @@ int32_t ChineseCalendar::handleGetExtendedYear() {
         year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
     } else {
         int32_t cycle = internalGet(UCAL_ERA, 1) - 1; // 0-based cycle
-        year = cycle * 60 + internalGet(UCAL_YEAR, 1);
+        // adjust to the instance specific epoch
+        year = cycle * 60 + internalGet(UCAL_YEAR, 1) - (fEpochYear - CHINESE_EPOCH_YEAR);
     }
     return year;
 }
@@ -291,11 +331,11 @@ int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
     // modify the extended year value accordingly.
     if (month < 0 || month > 11) {
         double m = month;
-        eyear += (int32_t)Math::floorDivide(m, 12.0, m);
+        eyear += (int32_t)ClockMath::floorDivide(m, 12.0, m);
         month = (int32_t)m;
     }
 
-    int32_t gyear = eyear + CHINESE_EPOCH_YEAR - 1; // Gregorian year
+    int32_t gyear = eyear + fEpochYear - 1; // Gregorian year
     int32_t theNewYear = newYear(gyear);
     int32_t newMoon = newMoonNear(theNewYear + month * 29, TRUE);
     
@@ -433,26 +473,67 @@ void ChineseCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status
 
 /**
  * Convert local days to UTC epoch milliseconds.
- * @param days days after January 1, 1970 0:00 Asia/Shanghai
+ * This is not an accurate conversion in that getTimezoneOffset 
+ * takes the milliseconds in GMT (not local time). In theory, more 
+ * accurate algorithm can be implemented but practically we do not need 
+ * to go through that complication as long as the historical timezone 
+ * changes did not happen around the 'tricky' new moon (new moon around 
+ * midnight). 
+ *  
+ * @param days days after January 1, 1970 0:00 in the astronomical base zone
  * @return milliseconds after January 1, 1970 0:00 GMT
  */
-double ChineseCalendar::daysToMillis(double days) {
-    return (days * kOneDay) - CHINA_OFFSET;
+double ChineseCalendar::daysToMillis(double days) const {
+    double millis = days * (double)kOneDay;
+    if (fZoneAstroCalc != NULL) {
+        int32_t rawOffset, dstOffset;
+        UErrorCode status = U_ZERO_ERROR;
+        fZoneAstroCalc->getOffset(millis, FALSE, rawOffset, dstOffset, status);
+        if (U_SUCCESS(status)) {
+            return millis - (double)(rawOffset + dstOffset);
+        }
+    }
+    return millis - (double)CHINA_OFFSET;
 }
 
 /**
  * Convert UTC epoch milliseconds to local days.
  * @param millis milliseconds after January 1, 1970 0:00 GMT
- * @return days after January 1, 1970 0:00 Asia/Shanghai
+ * @return days after January 1, 1970 0:00 in the astronomical base zone
  */
-double ChineseCalendar::millisToDays(double millis) {
-    return Math::floorDivide(millis + CHINA_OFFSET, kOneDay);
+double ChineseCalendar::millisToDays(double millis) const {
+    if (fZoneAstroCalc != NULL) {
+        int32_t rawOffset, dstOffset;
+        UErrorCode status = U_ZERO_ERROR;
+        fZoneAstroCalc->getOffset(millis, FALSE, rawOffset, dstOffset, status);
+        if (U_SUCCESS(status)) {
+            return ClockMath::floorDivide(millis + (double)(rawOffset + dstOffset), kOneDay);
+        }
+    }
+    return ClockMath::floorDivide(millis + (double)CHINA_OFFSET, kOneDay);
 }
 
 //------------------------------------------------------------------
 // Astronomical computations
 //------------------------------------------------------------------
 
+// bit array for gregorian 1900-2100 indicating years in
+// which the linear estimate needs to be adjusted by -1
+static const uint16_t winterSolsticeAdj[] = {
+    0x0001,     // 1900-1915, deltas for 1900
+    0x0444,     // 1916-1931, deltas for 1918, 1922, 1926
+    0x0000,     // 1932-1947
+    0x8880,     // 1948-1963, deltas for 1955, 1959, 1963
+    0x0000,     // 1964-1979
+    0x1100,     // 1980-1995, deltas for 1988, 1992
+    0x0011,     // 1996-2011, deltas for 1996, 2000
+    0x2200,     // 2012-2027, deltas for 2021, 2025
+    0x0022,     // 2028-2043, deltas for 2029, 2033
+    0x4000,     // 2044-2059, deltas for 2058
+    0x0444,     // 2060-2075, deltas for 2062, 2066, 2070
+    0x8000,     // 2076-2091, deltas for 2091
+    0x0088,     // 2092-2100, deltas for 2095, 2099
+};
 
 /**
  * Return the major solar term on or after December 15 of the given
@@ -463,6 +544,19 @@ double ChineseCalendar::millisToDays(double millis) {
  * winter solstice of the given year
  */
 int32_t ChineseCalendar::winterSolstice(int32_t gyear) const {
+    if (gyear >= 1900 && gyear <= 2100) {
+        // Don't use cache, just return linear estimate + table correction
+        int32_t gyearadj = gyear - 1900;
+        int32_t result = (int32_t)(365.243*((double)gyearadj) - 0.3) - 25211;
+        uint16_t bitmap = winterSolsticeAdj[gyearadj / 16];
+        if (bitmap != 0) {
+            uint16_t bitmask = 1 << (gyearadj % 16);
+            if ((bitmask & bitmap) != 0) {
+                result--;
+            }
+        }
+        return result;
+    }
 
     UErrorCode status = U_ZERO_ERROR;
     int32_t cacheValue = CalendarCache::get(&gChineseCalendarWinterSolsticeCache, gyear, status);
@@ -474,14 +568,14 @@ int32_t ChineseCalendar::winterSolstice(int32_t gyear) const {
         // PST 1298 with a final result of Dec 14 10:31:59 PST 1299.
         double ms = daysToMillis(Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1));
 
-        umtx_lock(&astroLock);
+        umtx_lock(astroLock());
         if(gChineseCalendarAstro == NULL) {
             gChineseCalendarAstro = new CalendarAstronomer();
             ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
         }
         gChineseCalendarAstro->setTime(ms);
         UDate solarLong = gChineseCalendarAstro->getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), TRUE);
-        umtx_unlock(&astroLock);
+        umtx_unlock(astroLock());
 
         // Winter solstice is 270 degrees solar longitude aka Dongzhi
         cacheValue = (int32_t)millisToDays(solarLong);
@@ -503,16 +597,24 @@ int32_t ChineseCalendar::winterSolstice(int32_t gyear) const {
  * new moon after or before <code>days</code>
  */
 int32_t ChineseCalendar::newMoonNear(double days, UBool after) const {
-    
-    umtx_lock(&astroLock);
-    if(gChineseCalendarAstro == NULL) {
-        gChineseCalendarAstro = new CalendarAstronomer();
-        ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
+    double ms = daysToMillis(days);
+    // Try to get the new moon via static function directly from the table in
+    // CalendarAstronomer (for approx gregorian range 1900-2100) without having
+    // to use a CalendarAstronomer instance which requires a lock. This still
+    // involves extra conversion to/from millis. If static function returns 0
+    // we are out of its range and need to use the full machinery.
+    UDate newMoon = CalendarAstronomer::getNewMoonTimeInRange(ms, after);
+    if (newMoon == 0.0) {
+        umtx_lock(astroLock());
+        if(gChineseCalendarAstro == NULL) {
+            gChineseCalendarAstro = new CalendarAstronomer();
+            ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
+        }
+        gChineseCalendarAstro->setTime(ms);
+        newMoon = gChineseCalendarAstro->getMoonTime(CalendarAstronomer::NEW_MOON(), after);
+        umtx_unlock(astroLock());
     }
-    gChineseCalendarAstro->setTime(daysToMillis(days));
-    UDate newMoon = gChineseCalendarAstro->getMoonTime(CalendarAstronomer::NEW_MOON(), after);
-    umtx_unlock(&astroLock);
-    
+
     return (int32_t) millisToDays(newMoon);
 }
 
@@ -536,14 +638,16 @@ int32_t ChineseCalendar::synodicMonthsBetween(int32_t day1, int32_t day2) const
  */
 int32_t ChineseCalendar::majorSolarTerm(int32_t days) const {
     
-    umtx_lock(&astroLock);
-    if(gChineseCalendarAstro == NULL) {
-        gChineseCalendarAstro = new CalendarAstronomer();
-        ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
-    }
-    gChineseCalendarAstro->setTime(daysToMillis(days));
-    UDate solarLongitude = gChineseCalendarAstro->getSunLongitude();
-    umtx_unlock(&astroLock);
+    double ms = daysToMillis(days);
+    UDate solarLongitude = CalendarAstronomer::getSunLongitudeForTime(ms);
+
+    // There was almost never any benefit to using the CalendarAstronomer instance;
+    // it could cache intermediate results, but we rarely used it multiple times in
+    // succession for the same setTime value, so the intermediate results got
+    // discarded anyway.
+    //
+    // Deleted call to gChineseCalendarAstro->getSunLongitude() now that
+    // we use CalendarAstronomer::getSunLongitudeForTime()
 
     // Compute (floor(solarLongitude / (pi/6)) + 2) % 12
     int32_t term = ( ((int32_t)(6 * solarLongitude / CalendarAstronomer::PI)) + 2 ) % 12;
@@ -571,10 +675,10 @@ UBool ChineseCalendar::hasNoMajorSolarTerm(int32_t newMoon) const {
 /**
  * Return true if there is a leap month on or after month newMoon1 and
  * at or before month newMoon2.
- * @param newMoon1 days after January 1, 1970 0:00 Asia/Shanghai of a
- * new moon
- * @param newMoon2 days after January 1, 1970 0:00 Asia/Shanghai of a
- * new moon
+ * @param newMoon1 days after January 1, 1970 0:00 astronomical base zone
+ * of a new moon
+ * @param newMoon2 days after January 1, 1970 0:00 astronomical base zone
+ * of a new moon
  */
 UBool ChineseCalendar::isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) const {
 
@@ -601,8 +705,8 @@ UBool ChineseCalendar::isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) co
  * <code>handleComputeMonthStart()</code>.
  *
  * <p>As a side effect, this method sets {@link #isLeapYear}.
- * @param days days after January 1, 1970 0:00 Asia/Shanghai of the
- * date to compute fields for
+ * @param days days after January 1, 1970 0:00 astronomical base zone
+ * of the date to compute fields for
  * @param gyear the Gregorian year of the given date
  * @param gmonth the Gregorian month of the given date
  * @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR,
@@ -651,18 +755,22 @@ void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t
 
     if (setAllFields) {
 
-        int32_t year = gyear - CHINESE_EPOCH_YEAR;
+        // Extended year and cycle year is based on the epoch year
+        
+        int32_t extended_year = gyear - fEpochYear;
+        int cycle_year = gyear - CHINESE_EPOCH_YEAR;
         if (month < 11 ||
             gmonth >= UCAL_JULY) {
-            year++;
+            extended_year++;
+            cycle_year++;
         }
         int32_t dayOfMonth = days - thisMoon + 1;
 
-        internalSet(UCAL_EXTENDED_YEAR, year);
+        internalSet(UCAL_EXTENDED_YEAR, extended_year);
 
         // 0->0,60  1->1,1  60->1,60  61->2,1  etc.
         int32_t yearOfCycle;
-        int32_t cycle = Math::floorDivide(year - 1, 60, yearOfCycle);
+        int32_t cycle = ClockMath::floorDivide(cycle_year - 1, 60, yearOfCycle);
         internalSet(UCAL_ERA, cycle + 1);
         internalSet(UCAL_YEAR, yearOfCycle + 1);
 
@@ -685,13 +793,34 @@ void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t
 // Fields to time
 //------------------------------------------------------------------
 
+// for gyear 1900 through 2100, corrections to linear estimate of newYear
+static const int8_t newYearAdj[] = {
+     -5,  14,   3,  -7,  11,  -1, -11,   8,  -3, -14,   5,  -6,  13,   1, -10,   9,  -1, -13,   6,  -4, // 1900-1919
+     15,   3,  -8,  11,   0, -12,   8,  -3, -13,   5,  -6,  12,   1, -10,   9,  -1, -12,   6,  -5,  14, // 1920-1939
+      3,  -9,  10,   0, -11,   9,  -3, -14,   5,  -6,  12,   1,  -9,  10,  -2, -12,   7,  -4,  13,   3, // 1940-1959
+     -8,  11,   0, -11,   8,  -2, -15,   4,  -6,  13,   1,  -9,  10,  -1, -13,   6,  -5,  14,   2,  -8, // 1960-1979
+     11,   1, -11,   8,  -3,  16,   5,  -7,  12,   2,  -8,  10,  -1, -12,   6,  -5,  14,   3,  -7,  11, // 1980-1999
+      0, -11,   8,  -4, -14,   5,  -6,  13,   2,  -9,  10,  -2, -13,   6,  -4,  14,   3,  -7,  12,   0, // 2000-2019
+    -11,   8,  -3, -14,   5,  -6,  13,   2, -10,   9,  -1, -12,   6,  -4,  15,   4,  -8,  11,   0, -11, // 2020-2039
+      7,  -3, -13,   6,  -6,  13,   2,  -9,   9,  -2, -12,   7,  -4,  15,   4,  -7,  10,   0, -11,   8, // 2040-2059
+     -3, -14,   5,  -6,  12,   1,  -9,  10,  -1, -12,   7,  -4,  15,   3,  -8,  11,   1, -11,   8,  -2, // 2060-2079
+    -13,   5,  -6,  13,   2,  -9,  10,  -1, -11,   6,  -5,  14,   3,  -8,  11,   1, -10,   8,  -3, -14, // 2080-2099
+      5 // 2100
+};
+
 /**
  * Return the Chinese new year of the given Gregorian year.
  * @param gyear a Gregorian year
- * @return days after January 1, 1970 0:00 Asia/Shanghai of the
+ * @return days after January 1, 1970 0:00 astronomical base zone of the
  * Chinese new year of the given year (this will be a new moon)
  */
 int32_t ChineseCalendar::newYear(int32_t gyear) const {
+    if (gyear >= 1900 && gyear <= 2100) {
+        // Don't use cache, just return linear estimate + table correction
+        int32_t gyearadj = gyear - 1900;
+        return (int32_t)(365.244*((double)gyearadj)) - 25532 + newYearAdj[gyearadj];
+    }
+
     UErrorCode status = U_ZERO_ERROR;
     int32_t cacheValue = CalendarCache::get(&gChineseCalendarNewYearCache, gyear, status);
 
@@ -775,11 +904,10 @@ ChineseCalendar::inDaylightTime(UErrorCode& status) const
 }
 
 // default century
-const UDate     ChineseCalendar::fgSystemDefaultCentury        = DBL_MIN;
-const int32_t   ChineseCalendar::fgSystemDefaultCenturyYear    = -1;
 
-UDate           ChineseCalendar::fgSystemDefaultCenturyStart       = DBL_MIN;
-int32_t         ChineseCalendar::fgSystemDefaultCenturyStartYear   = -1;
+static UDate     gSystemDefaultCenturyStart       = DBL_MIN;
+static int32_t   gSystemDefaultCenturyStartYear   = -1;
+static icu::UInitOnce gSystemDefaultCenturyInitOnce = U_INITONCE_INITIALIZER;
 
 
 UBool ChineseCalendar::haveDefaultCentury() const
@@ -797,67 +925,37 @@ int32_t ChineseCalendar::defaultCenturyStartYear() const
     return internalGetDefaultCenturyStartYear();
 }
 
+static void U_CALLCONV initializeSystemDefaultCentury()
+{
+    // initialize systemDefaultCentury and systemDefaultCenturyYear based
+    // on the current time.  They'll be set to 80 years before
+    // the current time.
+    UErrorCode status = U_ZERO_ERROR;
+    ChineseCalendar calendar(Locale("@calendar=chinese"),status);
+    if (U_SUCCESS(status)) {
+        calendar.setTime(Calendar::getNow(), status);
+        calendar.add(UCAL_YEAR, -80, status);
+        gSystemDefaultCenturyStart     = calendar.getTime(status);
+        gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status);
+    }
+    // We have no recourse upon failure unless we want to propagate the failure
+    // out.
+}
+
 UDate
 ChineseCalendar::internalGetDefaultCenturyStart() const
 {
     // lazy-evaluate systemDefaultCenturyStart
-    UBool needsUpdate;
-    UMTX_CHECK(NULL, (fgSystemDefaultCenturyStart == fgSystemDefaultCentury), needsUpdate);
-
-    if (needsUpdate) {
-        initializeSystemDefaultCentury();
-    }
-
-    // use defaultCenturyStart unless it's the flag value;
-    // then use systemDefaultCenturyStart
-
-    return fgSystemDefaultCenturyStart;
+    umtx_initOnce(gSystemDefaultCenturyInitOnce, &initializeSystemDefaultCentury);
+    return gSystemDefaultCenturyStart;
 }
 
 int32_t
 ChineseCalendar::internalGetDefaultCenturyStartYear() const
 {
     // lazy-evaluate systemDefaultCenturyStartYear
-    UBool needsUpdate;
-    UMTX_CHECK(NULL, (fgSystemDefaultCenturyStart == fgSystemDefaultCentury), needsUpdate);
-
-    if (needsUpdate) {
-        initializeSystemDefaultCentury();
-    }
-
-    // use defaultCenturyStart unless it's the flag value;
-    // then use systemDefaultCenturyStartYear
-
-    return    fgSystemDefaultCenturyStartYear;
-}
-
-void
-ChineseCalendar::initializeSystemDefaultCentury()
-{
-    // initialize systemDefaultCentury and systemDefaultCenturyYear based
-    // on the current time.  They'll be set to 80 years before
-    // the current time.
-    // No point in locking as it should be idempotent.
-    if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
-    {
-        UErrorCode status = U_ZERO_ERROR;
-        ChineseCalendar calendar(Locale("@calendar=chinese"),status);
-        if (U_SUCCESS(status))
-        {
-            calendar.setTime(Calendar::getNow(), status);
-            calendar.add(UCAL_YEAR, -80, status);
-            UDate    newStart =  calendar.getTime(status);
-            int32_t  newYear  =  calendar.get(UCAL_YEAR, status);
-            {
-                umtx_lock(NULL);
-                fgSystemDefaultCenturyStart = newStart;
-                fgSystemDefaultCenturyStartYear = newYear;
-                umtx_unlock(NULL);
-            }
-        }
-        // We have no recourse upon failure unless we want to propagate the failure
-        // out.
-    }
+    umtx_initOnce(gSystemDefaultCenturyInitOnce, &initializeSystemDefaultCentury);
+    return    gSystemDefaultCenturyStartYear;
 }
 
 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar)