]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/chnsecal.cpp
ICU-511.32.tar.gz
[apple/icu.git] / icuSources / i18n / chnsecal.cpp
index 52cb6c86d9851e63ca4f2dc85dd31d656320861d..16a3c9a061bd4490136a2d4699705dbfb71fd00b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  ******************************************************************************
- * Copyright (C) 2007-2008, International Business Machines Corporation
+ * Copyright (C) 2007-2013, International Business Machines Corporation
  * and others. All Rights Reserved.
  ******************************************************************************
  *
@@ -21,6 +21,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 +49,12 @@ 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 UMutex astroLock = U_MUTEX_INITIALIZER;  // pod bay door lock
+static icu::CalendarAstronomer *gChineseCalendarAstro = NULL;
+static icu::CalendarCache *gChineseCalendarWinterSolsticeCache = NULL;
+static icu::CalendarCache *gChineseCalendarNewYearCache = NULL;
+static icu::TimeZone *gChineseCalendarZoneAstroCalc = NULL;
+static UBool gChineseCalendarZoneAstroCalcInitialized = FALSE;
 
 /**
  * The start year of the Chinese calendar, the 61st year of the reign
@@ -66,7 +69,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 +93,11 @@ static UBool calendar_chinese_cleanup(void) {
         delete gChineseCalendarNewYearCache;
         gChineseCalendarNewYearCache = NULL;
     }
-    umtx_destroy(&astroLock);
+    if (gChineseCalendarZoneAstroCalc) {
+        delete gChineseCalendarZoneAstroCalc;
+        gChineseCalendarZoneAstroCalc = NULL;
+    }
+    gChineseCalendarZoneAstroCalcInitialized = FALSE;
     return TRUE;
 }
 U_CDECL_END
@@ -111,14 +118,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 +150,23 @@ const char *ChineseCalendar::getType() const {
     return "chinese";
 }
 
+const TimeZone* ChineseCalendar::getChineseCalZoneAstroCalc(void) const {
+    UBool initialized;
+    UMTX_CHECK(&astroLock, gChineseCalendarZoneAstroCalcInitialized, initialized);
+    if (!initialized) {
+        umtx_lock(&astroLock);
+        {
+            if (!gChineseCalendarZoneAstroCalcInitialized) {
+                gChineseCalendarZoneAstroCalc = new SimpleTimeZone(CHINA_OFFSET, UNICODE_STRING_SIMPLE("CHINA_ZONE") );
+                gChineseCalendarZoneAstroCalcInitialized = TRUE;
+                ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
+            }
+        }
+        umtx_unlock(&astroLock);
+    }
+    return gChineseCalendarZoneAstroCalc;
+}
+
 //-------------------------------------------------------------------------
 // Minimum / Maximum access functions
 //-------------------------------------------------------------------------
@@ -188,7 +226,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 +330,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,20 +472,44 @@ 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);
 }
 
 //------------------------------------------------------------------
@@ -571,10 +634,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 +664,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 +714,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);
 
@@ -688,7 +755,7 @@ void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t
 /**
  * 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 {
@@ -837,27 +904,24 @@ 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))
     {
-        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);
+        if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
         {
-            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);
-            }
+            fgSystemDefaultCenturyStartYear = newYear;
+            fgSystemDefaultCenturyStart = newStart;
         }
-        // We have no recourse upon failure unless we want to propagate the failure
-        // out.
+        umtx_unlock(NULL);
     }
+    // We have no recourse upon failure unless we want to propagate the failure
+    // out.
 }
 
 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar)