/*
******************************************************************************
- * Copyright (C) 2007-2008, International Business Machines Corporation
+ * Copyright (C) 2007-2013, International Business Machines Corporation
* and others. All Rights Reserved.
******************************************************************************
*
#include <float.h>
#include "gregoimp.h" // Math
#include "astro.h" // CalendarAstronomer
+#include "unicode/simpletz.h"
#include "uhash.h"
#include "ucln_in.h"
// --- 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
* 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
delete gChineseCalendarNewYearCache;
gChineseCalendarNewYearCache = NULL;
}
- umtx_destroy(&astroLock);
+ if (gChineseCalendarZoneAstroCalc) {
+ delete gChineseCalendarZoneAstroCalc;
+ gChineseCalendarZoneAstroCalc = NULL;
+ }
+ gChineseCalendarZoneAstroCalcInitialized = FALSE;
return TRUE;
}
U_CDECL_END
}
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()
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
//-------------------------------------------------------------------------
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;
}
// 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);
/**
* 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);
}
//------------------------------------------------------------------
/**
* 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 {
* <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,
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);
/**
* 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 {
// 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)