X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/46f4442e9a5a4f3b98b7c1083586332f6a8a99a4..e4f10fab0c078f399c9deef476d9c9b73b47dff8:/icuSources/i18n/chnsecal.cpp diff --git a/icuSources/i18n/chnsecal.cpp b/icuSources/i18n/chnsecal.cpp index 52cb6c86..16a3c9a0 100644 --- a/icuSources/i18n/chnsecal.cpp +++ b/icuSources/i18n/chnsecal.cpp @@ -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 #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 * handleComputeMonthStart(). * *

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)