intl/icu/source/i18n/chnsecal.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/intl/icu/source/i18n/chnsecal.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,897 @@
     1.4 +/*
     1.5 + ******************************************************************************
     1.6 + * Copyright (C) 2007-2013, International Business Machines Corporation
     1.7 + * and others. All Rights Reserved.
     1.8 + ******************************************************************************
     1.9 + *
    1.10 + * File CHNSECAL.CPP
    1.11 + *
    1.12 + * Modification History:
    1.13 + *
    1.14 + *   Date        Name        Description
    1.15 + *   9/18/2007  ajmacher         ported from java ChineseCalendar
    1.16 + *****************************************************************************
    1.17 + */
    1.18 +
    1.19 +#include "chnsecal.h"
    1.20 +
    1.21 +#if !UCONFIG_NO_FORMATTING
    1.22 +
    1.23 +#include "umutex.h"
    1.24 +#include <float.h>
    1.25 +#include "gregoimp.h" // Math
    1.26 +#include "astro.h" // CalendarAstronomer
    1.27 +#include "unicode/simpletz.h"
    1.28 +#include "uhash.h"
    1.29 +#include "ucln_in.h"
    1.30 +
    1.31 +// Debugging
    1.32 +#ifdef U_DEBUG_CHNSECAL
    1.33 +# include <stdio.h>
    1.34 +# include <stdarg.h>
    1.35 +static void debug_chnsecal_loc(const char *f, int32_t l)
    1.36 +{
    1.37 +    fprintf(stderr, "%s:%d: ", f, l);
    1.38 +}
    1.39 +
    1.40 +static void debug_chnsecal_msg(const char *pat, ...)
    1.41 +{
    1.42 +    va_list ap;
    1.43 +    va_start(ap, pat);
    1.44 +    vfprintf(stderr, pat, ap);
    1.45 +    fflush(stderr);
    1.46 +}
    1.47 +// must use double parens, i.e.:  U_DEBUG_CHNSECAL_MSG(("four is: %d",4));
    1.48 +#define U_DEBUG_CHNSECAL_MSG(x) {debug_chnsecal_loc(__FILE__,__LINE__);debug_chnsecal_msg x;}
    1.49 +#else
    1.50 +#define U_DEBUG_CHNSECAL_MSG(x)
    1.51 +#endif
    1.52 +
    1.53 +
    1.54 +// --- The cache --
    1.55 +static UMutex astroLock = U_MUTEX_INITIALIZER;  // pod bay door lock
    1.56 +static icu::CalendarAstronomer *gChineseCalendarAstro = NULL;
    1.57 +static icu::CalendarCache *gChineseCalendarWinterSolsticeCache = NULL;
    1.58 +static icu::CalendarCache *gChineseCalendarNewYearCache = NULL;
    1.59 +static icu::TimeZone *gChineseCalendarZoneAstroCalc = NULL;
    1.60 +static icu::UInitOnce gChineseCalendarZoneAstroCalcInitOnce = U_INITONCE_INITIALIZER;
    1.61 +
    1.62 +/**
    1.63 + * The start year of the Chinese calendar, the 61st year of the reign
    1.64 + * of Huang Di.  Some sources use the first year of his reign,
    1.65 + * resulting in EXTENDED_YEAR values 60 years greater and ERA (cycle)
    1.66 + * values one greater.
    1.67 + */
    1.68 +static const int32_t CHINESE_EPOCH_YEAR = -2636; // Gregorian year
    1.69 +
    1.70 +/**
    1.71 + * The offset from GMT in milliseconds at which we perform astronomical
    1.72 + * computations.  Some sources use a different historically accurate
    1.73 + * offset of GMT+7:45:40 for years before 1929; we do not do this.
    1.74 + */
    1.75 +static const int32_t CHINA_OFFSET = 8 * kOneHour;
    1.76 +
    1.77 +/**
    1.78 + * Value to be added or subtracted from the local days of a new moon to
    1.79 + * get close to the next or prior new moon, but not cross it.  Must be
    1.80 + * >= 1 and < CalendarAstronomer.SYNODIC_MONTH.
    1.81 + */
    1.82 +static const int32_t SYNODIC_GAP = 25;
    1.83 +
    1.84 +
    1.85 +U_CDECL_BEGIN
    1.86 +static UBool calendar_chinese_cleanup(void) {
    1.87 +    if (gChineseCalendarAstro) {
    1.88 +        delete gChineseCalendarAstro;
    1.89 +        gChineseCalendarAstro = NULL;
    1.90 +    }
    1.91 +    if (gChineseCalendarWinterSolsticeCache) {
    1.92 +        delete gChineseCalendarWinterSolsticeCache;
    1.93 +        gChineseCalendarWinterSolsticeCache = NULL;
    1.94 +    }
    1.95 +    if (gChineseCalendarNewYearCache) {
    1.96 +        delete gChineseCalendarNewYearCache;
    1.97 +        gChineseCalendarNewYearCache = NULL;
    1.98 +    }
    1.99 +    if (gChineseCalendarZoneAstroCalc) {
   1.100 +        delete gChineseCalendarZoneAstroCalc;
   1.101 +        gChineseCalendarZoneAstroCalc = NULL;
   1.102 +    }
   1.103 +    gChineseCalendarZoneAstroCalcInitOnce.reset();
   1.104 +    return TRUE;
   1.105 +}
   1.106 +U_CDECL_END
   1.107 +
   1.108 +U_NAMESPACE_BEGIN
   1.109 +
   1.110 +
   1.111 +// Implementation of the ChineseCalendar class
   1.112 +
   1.113 +
   1.114 +//-------------------------------------------------------------------------
   1.115 +// Constructors...
   1.116 +//-------------------------------------------------------------------------
   1.117 +
   1.118 +
   1.119 +Calendar* ChineseCalendar::clone() const {
   1.120 +    return new ChineseCalendar(*this);
   1.121 +}
   1.122 +
   1.123 +ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success)
   1.124 +:   Calendar(TimeZone::createDefault(), aLocale, success),
   1.125 +    isLeapYear(FALSE),
   1.126 +    fEpochYear(CHINESE_EPOCH_YEAR),
   1.127 +    fZoneAstroCalc(getChineseCalZoneAstroCalc())
   1.128 +{
   1.129 +    setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
   1.130 +}
   1.131 +
   1.132 +ChineseCalendar::ChineseCalendar(const Locale& aLocale, int32_t epochYear,
   1.133 +                                const TimeZone* zoneAstroCalc, UErrorCode &success)
   1.134 +:   Calendar(TimeZone::createDefault(), aLocale, success),
   1.135 +    isLeapYear(FALSE),
   1.136 +    fEpochYear(epochYear),
   1.137 +    fZoneAstroCalc(zoneAstroCalc)
   1.138 +{
   1.139 +    setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
   1.140 +}
   1.141 +
   1.142 +ChineseCalendar::ChineseCalendar(const ChineseCalendar& other) : Calendar(other) {
   1.143 +    isLeapYear = other.isLeapYear;
   1.144 +    fEpochYear = other.fEpochYear;
   1.145 +    fZoneAstroCalc = other.fZoneAstroCalc;
   1.146 +}
   1.147 +
   1.148 +ChineseCalendar::~ChineseCalendar()
   1.149 +{
   1.150 +}
   1.151 +
   1.152 +const char *ChineseCalendar::getType() const { 
   1.153 +    return "chinese";
   1.154 +}
   1.155 +
   1.156 +static void U_CALLCONV initChineseCalZoneAstroCalc() {
   1.157 +    gChineseCalendarZoneAstroCalc = new SimpleTimeZone(CHINA_OFFSET, UNICODE_STRING_SIMPLE("CHINA_ZONE") );
   1.158 +    ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
   1.159 +}
   1.160 +
   1.161 +const TimeZone* ChineseCalendar::getChineseCalZoneAstroCalc(void) const {
   1.162 +    umtx_initOnce(gChineseCalendarZoneAstroCalcInitOnce, &initChineseCalZoneAstroCalc);
   1.163 +    return gChineseCalendarZoneAstroCalc;
   1.164 +}
   1.165 +
   1.166 +//-------------------------------------------------------------------------
   1.167 +// Minimum / Maximum access functions
   1.168 +//-------------------------------------------------------------------------
   1.169 +
   1.170 +
   1.171 +static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
   1.172 +    // Minimum  Greatest     Least    Maximum
   1.173 +    //           Minimum   Maximum
   1.174 +    {        1,        1,    83333,    83333}, // ERA
   1.175 +    {        1,        1,       60,       60}, // YEAR
   1.176 +    {        0,        0,       11,       11}, // MONTH
   1.177 +    {        1,        1,       50,       55}, // WEEK_OF_YEAR
   1.178 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
   1.179 +    {        1,        1,       29,       30}, // DAY_OF_MONTH
   1.180 +    {        1,        1,      353,      385}, // DAY_OF_YEAR
   1.181 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
   1.182 +    {       -1,       -1,        5,        5}, // DAY_OF_WEEK_IN_MONTH
   1.183 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
   1.184 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
   1.185 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
   1.186 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
   1.187 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
   1.188 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
   1.189 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
   1.190 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
   1.191 +    { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY
   1.192 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
   1.193 +    { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR
   1.194 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
   1.195 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
   1.196 +    {        0,        0,        1,        1}, // IS_LEAP_MONTH
   1.197 +};
   1.198 +
   1.199 +
   1.200 +/**
   1.201 +* @draft ICU 2.4
   1.202 +*/
   1.203 +int32_t ChineseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
   1.204 +    return LIMITS[field][limitType];
   1.205 +}
   1.206 +
   1.207 +
   1.208 +//----------------------------------------------------------------------
   1.209 +// Calendar framework
   1.210 +//----------------------------------------------------------------------
   1.211 +
   1.212 +/**
   1.213 + * Implement abstract Calendar method to return the extended year
   1.214 + * defined by the current fields.  This will use either the ERA and
   1.215 + * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR
   1.216 + * field as the continuous year count, depending on which is newer.
   1.217 + * @stable ICU 2.8
   1.218 + */
   1.219 +int32_t ChineseCalendar::handleGetExtendedYear() {
   1.220 +    int32_t year;
   1.221 +    if (newestStamp(UCAL_ERA, UCAL_YEAR, kUnset) <= fStamp[UCAL_EXTENDED_YEAR]) {
   1.222 +        year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
   1.223 +    } else {
   1.224 +        int32_t cycle = internalGet(UCAL_ERA, 1) - 1; // 0-based cycle
   1.225 +        // adjust to the instance specific epoch
   1.226 +        year = cycle * 60 + internalGet(UCAL_YEAR, 1) - (fEpochYear - CHINESE_EPOCH_YEAR);
   1.227 +    }
   1.228 +    return year;
   1.229 +}
   1.230 +
   1.231 +/**
   1.232 + * Override Calendar method to return the number of days in the given
   1.233 + * extended year and month.
   1.234 + *
   1.235 + * <p>Note: This method also reads the IS_LEAP_MONTH field to determine
   1.236 + * whether or not the given month is a leap month.
   1.237 + * @stable ICU 2.8
   1.238 + */
   1.239 +int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
   1.240 +    int32_t thisStart = handleComputeMonthStart(extendedYear, month, TRUE) -
   1.241 +        kEpochStartAsJulianDay + 1; // Julian day -> local days
   1.242 +    int32_t nextStart = newMoonNear(thisStart + SYNODIC_GAP, TRUE);
   1.243 +    return nextStart - thisStart;
   1.244 +}
   1.245 +
   1.246 +/**
   1.247 + * Override Calendar to compute several fields specific to the Chinese
   1.248 + * calendar system.  These are:
   1.249 + *
   1.250 + * <ul><li>ERA
   1.251 + * <li>YEAR
   1.252 + * <li>MONTH
   1.253 + * <li>DAY_OF_MONTH
   1.254 + * <li>DAY_OF_YEAR
   1.255 + * <li>EXTENDED_YEAR</ul>
   1.256 + * 
   1.257 + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
   1.258 + * method is called.  The getGregorianXxx() methods return Gregorian
   1.259 + * calendar equivalents for the given Julian day.
   1.260 + *
   1.261 + * <p>Compute the ChineseCalendar-specific field IS_LEAP_MONTH.
   1.262 + * @stable ICU 2.8
   1.263 + */
   1.264 +void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) {
   1.265 +
   1.266 +    computeChineseFields(julianDay - kEpochStartAsJulianDay, // local days
   1.267 +                         getGregorianYear(), getGregorianMonth(),
   1.268 +                         TRUE); // set all fields
   1.269 +}
   1.270 +
   1.271 +/**
   1.272 + * Field resolution table that incorporates IS_LEAP_MONTH.
   1.273 + */
   1.274 +const UFieldResolutionTable ChineseCalendar::CHINESE_DATE_PRECEDENCE[] =
   1.275 +{
   1.276 +    {
   1.277 +        { UCAL_DAY_OF_MONTH, kResolveSTOP },
   1.278 +        { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP },
   1.279 +        { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
   1.280 +        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
   1.281 +        { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP },
   1.282 +        { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
   1.283 +        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
   1.284 +        { UCAL_DAY_OF_YEAR, kResolveSTOP },
   1.285 +        { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_IS_LEAP_MONTH, kResolveSTOP },
   1.286 +        { kResolveSTOP }
   1.287 +    },
   1.288 +    {
   1.289 +        { UCAL_WEEK_OF_YEAR, kResolveSTOP },
   1.290 +        { UCAL_WEEK_OF_MONTH, kResolveSTOP },
   1.291 +        { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP },
   1.292 +        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
   1.293 +        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
   1.294 +        { kResolveSTOP }
   1.295 +    },
   1.296 +    {{kResolveSTOP}}
   1.297 +};
   1.298 +
   1.299 +/**
   1.300 + * Override Calendar to add IS_LEAP_MONTH to the field resolution
   1.301 + * table.
   1.302 + * @stable ICU 2.8
   1.303 + */
   1.304 +const UFieldResolutionTable* ChineseCalendar::getFieldResolutionTable() const {
   1.305 +    return CHINESE_DATE_PRECEDENCE;
   1.306 +}
   1.307 +
   1.308 +/**
   1.309 + * Return the Julian day number of day before the first day of the
   1.310 + * given month in the given extended year.
   1.311 + * 
   1.312 + * <p>Note: This method reads the IS_LEAP_MONTH field to determine
   1.313 + * whether the given month is a leap month.
   1.314 + * @param eyear the extended year
   1.315 + * @param month the zero-based month.  The month is also determined
   1.316 + * by reading the IS_LEAP_MONTH field.
   1.317 + * @return the Julian day number of the day before the first
   1.318 + * day of the given month and year
   1.319 + * @stable ICU 2.8
   1.320 + */
   1.321 +int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const {
   1.322 +
   1.323 +    ChineseCalendar *nonConstThis = (ChineseCalendar*)this; // cast away const
   1.324 +
   1.325 +    // If the month is out of range, adjust it into range, and
   1.326 +    // modify the extended year value accordingly.
   1.327 +    if (month < 0 || month > 11) {
   1.328 +        double m = month;
   1.329 +        eyear += (int32_t)ClockMath::floorDivide(m, 12.0, m);
   1.330 +        month = (int32_t)m;
   1.331 +    }
   1.332 +
   1.333 +    int32_t gyear = eyear + fEpochYear - 1; // Gregorian year
   1.334 +    int32_t theNewYear = newYear(gyear);
   1.335 +    int32_t newMoon = newMoonNear(theNewYear + month * 29, TRUE);
   1.336 +    
   1.337 +    int32_t julianDay = newMoon + kEpochStartAsJulianDay;
   1.338 +
   1.339 +    // Save fields for later restoration
   1.340 +    int32_t saveMonth = internalGet(UCAL_MONTH);
   1.341 +    int32_t saveIsLeapMonth = internalGet(UCAL_IS_LEAP_MONTH);
   1.342 +
   1.343 +    // Ignore IS_LEAP_MONTH field if useMonth is false
   1.344 +    int32_t isLeapMonth = useMonth ? saveIsLeapMonth : 0;
   1.345 +
   1.346 +    UErrorCode status = U_ZERO_ERROR;
   1.347 +    nonConstThis->computeGregorianFields(julianDay, status);
   1.348 +    if (U_FAILURE(status))
   1.349 +        return 0;
   1.350 +    
   1.351 +    // This will modify the MONTH and IS_LEAP_MONTH fields (only)
   1.352 +    nonConstThis->computeChineseFields(newMoon, getGregorianYear(),
   1.353 +                         getGregorianMonth(), FALSE);        
   1.354 +
   1.355 +    if (month != internalGet(UCAL_MONTH) ||
   1.356 +        isLeapMonth != internalGet(UCAL_IS_LEAP_MONTH)) {
   1.357 +        newMoon = newMoonNear(newMoon + SYNODIC_GAP, TRUE);
   1.358 +        julianDay = newMoon + kEpochStartAsJulianDay;
   1.359 +    }
   1.360 +
   1.361 +    nonConstThis->internalSet(UCAL_MONTH, saveMonth);
   1.362 +    nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, saveIsLeapMonth);
   1.363 +
   1.364 +    return julianDay - 1;
   1.365 +}
   1.366 +
   1.367 +
   1.368 +/**
   1.369 + * Override Calendar to handle leap months properly.
   1.370 + * @stable ICU 2.8
   1.371 + */
   1.372 +void ChineseCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) {
   1.373 +    switch (field) {
   1.374 +    case UCAL_MONTH:
   1.375 +        if (amount != 0) {
   1.376 +            int32_t dom = get(UCAL_DAY_OF_MONTH, status);
   1.377 +            if (U_FAILURE(status)) break;
   1.378 +            int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day
   1.379 +            if (U_FAILURE(status)) break;
   1.380 +            int32_t moon = day - dom + 1; // New moon 
   1.381 +            offsetMonth(moon, dom, amount);
   1.382 +        }
   1.383 +        break;
   1.384 +    default:
   1.385 +        Calendar::add(field, amount, status);
   1.386 +        break;
   1.387 +    }
   1.388 +}
   1.389 +
   1.390 +/**
   1.391 + * Override Calendar to handle leap months properly.
   1.392 + * @stable ICU 2.8
   1.393 + */
   1.394 +void ChineseCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) {
   1.395 +    add((UCalendarDateFields)field, amount, status);
   1.396 +}
   1.397 +
   1.398 +/**
   1.399 + * Override Calendar to handle leap months properly.
   1.400 + * @stable ICU 2.8
   1.401 + */
   1.402 +void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) {
   1.403 +    switch (field) {
   1.404 +    case UCAL_MONTH:
   1.405 +        if (amount != 0) {
   1.406 +            int32_t dom = get(UCAL_DAY_OF_MONTH, status);
   1.407 +            if (U_FAILURE(status)) break;
   1.408 +            int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day
   1.409 +            if (U_FAILURE(status)) break;
   1.410 +            int32_t moon = day - dom + 1; // New moon (start of this month)
   1.411 +
   1.412 +            // Note throughout the following:  Months 12 and 1 are never
   1.413 +            // followed by a leap month (D&R p. 185).
   1.414 +
   1.415 +            // Compute the adjusted month number m.  This is zero-based
   1.416 +            // value from 0..11 in a non-leap year, and from 0..12 in a
   1.417 +            // leap year.
   1.418 +            int32_t m = get(UCAL_MONTH, status); // 0-based month
   1.419 +            if (U_FAILURE(status)) break;
   1.420 +            if (isLeapYear) { // (member variable)
   1.421 +                if (get(UCAL_IS_LEAP_MONTH, status) == 1) {
   1.422 +                    ++m;
   1.423 +                } else {
   1.424 +                    // Check for a prior leap month.  (In the
   1.425 +                    // following, month 0 is the first month of the
   1.426 +                    // year.)  Month 0 is never followed by a leap
   1.427 +                    // month, and we know month m is not a leap month.
   1.428 +                    // moon1 will be the start of month 0 if there is
   1.429 +                    // no leap month between month 0 and month m;
   1.430 +                    // otherwise it will be the start of month 1.
   1.431 +                    int moon1 = moon -
   1.432 +                        (int) (CalendarAstronomer::SYNODIC_MONTH * (m - 0.5));
   1.433 +                    moon1 = newMoonNear(moon1, TRUE);
   1.434 +                    if (isLeapMonthBetween(moon1, moon)) {
   1.435 +                        ++m;
   1.436 +                    }
   1.437 +                }
   1.438 +                if (U_FAILURE(status)) break;
   1.439 +            }
   1.440 +
   1.441 +            // Now do the standard roll computation on m, with the
   1.442 +            // allowed range of 0..n-1, where n is 12 or 13.
   1.443 +            int32_t n = isLeapYear ? 13 : 12; // Months in this year
   1.444 +            int32_t newM = (m + amount) % n;
   1.445 +            if (newM < 0) {
   1.446 +                newM += n;
   1.447 +            }
   1.448 +
   1.449 +            if (newM != m) {
   1.450 +                offsetMonth(moon, dom, newM - m);
   1.451 +            }
   1.452 +        }
   1.453 +        break;
   1.454 +    default:
   1.455 +        Calendar::roll(field, amount, status);
   1.456 +        break;
   1.457 +    }
   1.458 +}
   1.459 +
   1.460 +void ChineseCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) {
   1.461 +    roll((UCalendarDateFields)field, amount, status);
   1.462 +}
   1.463 +
   1.464 +
   1.465 +//------------------------------------------------------------------
   1.466 +// Support methods and constants
   1.467 +//------------------------------------------------------------------
   1.468 +
   1.469 +/**
   1.470 + * Convert local days to UTC epoch milliseconds.
   1.471 + * This is not an accurate conversion in that getTimezoneOffset 
   1.472 + * takes the milliseconds in GMT (not local time). In theory, more 
   1.473 + * accurate algorithm can be implemented but practically we do not need 
   1.474 + * to go through that complication as long as the historical timezone 
   1.475 + * changes did not happen around the 'tricky' new moon (new moon around 
   1.476 + * midnight). 
   1.477 + *  
   1.478 + * @param days days after January 1, 1970 0:00 in the astronomical base zone
   1.479 + * @return milliseconds after January 1, 1970 0:00 GMT
   1.480 + */
   1.481 +double ChineseCalendar::daysToMillis(double days) const {
   1.482 +    double millis = days * (double)kOneDay;
   1.483 +    if (fZoneAstroCalc != NULL) {
   1.484 +        int32_t rawOffset, dstOffset;
   1.485 +        UErrorCode status = U_ZERO_ERROR;
   1.486 +        fZoneAstroCalc->getOffset(millis, FALSE, rawOffset, dstOffset, status);
   1.487 +        if (U_SUCCESS(status)) {
   1.488 +        	return millis - (double)(rawOffset + dstOffset);
   1.489 +        }
   1.490 +    }
   1.491 +    return millis - (double)CHINA_OFFSET;
   1.492 +}
   1.493 +
   1.494 +/**
   1.495 + * Convert UTC epoch milliseconds to local days.
   1.496 + * @param millis milliseconds after January 1, 1970 0:00 GMT
   1.497 + * @return days after January 1, 1970 0:00 in the astronomical base zone
   1.498 + */
   1.499 +double ChineseCalendar::millisToDays(double millis) const {
   1.500 +    if (fZoneAstroCalc != NULL) {
   1.501 +        int32_t rawOffset, dstOffset;
   1.502 +        UErrorCode status = U_ZERO_ERROR;
   1.503 +        fZoneAstroCalc->getOffset(millis, FALSE, rawOffset, dstOffset, status);
   1.504 +        if (U_SUCCESS(status)) {
   1.505 +        	return ClockMath::floorDivide(millis + (double)(rawOffset + dstOffset), kOneDay);
   1.506 +        }
   1.507 +    }
   1.508 +    return ClockMath::floorDivide(millis + (double)CHINA_OFFSET, kOneDay);
   1.509 +}
   1.510 +
   1.511 +//------------------------------------------------------------------
   1.512 +// Astronomical computations
   1.513 +//------------------------------------------------------------------
   1.514 +
   1.515 +
   1.516 +/**
   1.517 + * Return the major solar term on or after December 15 of the given
   1.518 + * Gregorian year, that is, the winter solstice of the given year.
   1.519 + * Computations are relative to Asia/Shanghai time zone.
   1.520 + * @param gyear a Gregorian year
   1.521 + * @return days after January 1, 1970 0:00 Asia/Shanghai of the
   1.522 + * winter solstice of the given year
   1.523 + */
   1.524 +int32_t ChineseCalendar::winterSolstice(int32_t gyear) const {
   1.525 +
   1.526 +    UErrorCode status = U_ZERO_ERROR;
   1.527 +    int32_t cacheValue = CalendarCache::get(&gChineseCalendarWinterSolsticeCache, gyear, status);
   1.528 +
   1.529 +    if (cacheValue == 0) {
   1.530 +        // In books December 15 is used, but it fails for some years
   1.531 +        // using our algorithms, e.g.: 1298 1391 1492 1553 1560.  That
   1.532 +        // is, winterSolstice(1298) starts search at Dec 14 08:00:00
   1.533 +        // PST 1298 with a final result of Dec 14 10:31:59 PST 1299.
   1.534 +        double ms = daysToMillis(Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1));
   1.535 +
   1.536 +        umtx_lock(&astroLock);
   1.537 +        if(gChineseCalendarAstro == NULL) {
   1.538 +            gChineseCalendarAstro = new CalendarAstronomer();
   1.539 +            ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
   1.540 +        }
   1.541 +        gChineseCalendarAstro->setTime(ms);
   1.542 +        UDate solarLong = gChineseCalendarAstro->getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), TRUE);
   1.543 +        umtx_unlock(&astroLock);
   1.544 +
   1.545 +        // Winter solstice is 270 degrees solar longitude aka Dongzhi
   1.546 +        cacheValue = (int32_t)millisToDays(solarLong);
   1.547 +        CalendarCache::put(&gChineseCalendarWinterSolsticeCache, gyear, cacheValue, status);
   1.548 +    }
   1.549 +    if(U_FAILURE(status)) {
   1.550 +        cacheValue = 0;
   1.551 +    }
   1.552 +    return cacheValue;
   1.553 +}
   1.554 +
   1.555 +/**
   1.556 + * Return the closest new moon to the given date, searching either
   1.557 + * forward or backward in time.
   1.558 + * @param days days after January 1, 1970 0:00 Asia/Shanghai
   1.559 + * @param after if true, search for a new moon on or after the given
   1.560 + * date; otherwise, search for a new moon before it
   1.561 + * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest
   1.562 + * new moon after or before <code>days</code>
   1.563 + */
   1.564 +int32_t ChineseCalendar::newMoonNear(double days, UBool after) const {
   1.565 +    
   1.566 +    umtx_lock(&astroLock);
   1.567 +    if(gChineseCalendarAstro == NULL) {
   1.568 +        gChineseCalendarAstro = new CalendarAstronomer();
   1.569 +        ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
   1.570 +    }
   1.571 +    gChineseCalendarAstro->setTime(daysToMillis(days));
   1.572 +    UDate newMoon = gChineseCalendarAstro->getMoonTime(CalendarAstronomer::NEW_MOON(), after);
   1.573 +    umtx_unlock(&astroLock);
   1.574 +    
   1.575 +    return (int32_t) millisToDays(newMoon);
   1.576 +}
   1.577 +
   1.578 +/**
   1.579 + * Return the nearest integer number of synodic months between
   1.580 + * two dates.
   1.581 + * @param day1 days after January 1, 1970 0:00 Asia/Shanghai
   1.582 + * @param day2 days after January 1, 1970 0:00 Asia/Shanghai
   1.583 + * @return the nearest integer number of months between day1 and day2
   1.584 + */
   1.585 +int32_t ChineseCalendar::synodicMonthsBetween(int32_t day1, int32_t day2) const {
   1.586 +    double roundme = ((day2 - day1) / CalendarAstronomer::SYNODIC_MONTH);
   1.587 +    return (int32_t) (roundme + (roundme >= 0 ? .5 : -.5));
   1.588 +}
   1.589 +
   1.590 +/**
   1.591 + * Return the major solar term on or before a given date.  This
   1.592 + * will be an integer from 1..12, with 1 corresponding to 330 degrees,
   1.593 + * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees.
   1.594 + * @param days days after January 1, 1970 0:00 Asia/Shanghai
   1.595 + */
   1.596 +int32_t ChineseCalendar::majorSolarTerm(int32_t days) const {
   1.597 +    
   1.598 +    umtx_lock(&astroLock);
   1.599 +    if(gChineseCalendarAstro == NULL) {
   1.600 +        gChineseCalendarAstro = new CalendarAstronomer();
   1.601 +        ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
   1.602 +    }
   1.603 +    gChineseCalendarAstro->setTime(daysToMillis(days));
   1.604 +    UDate solarLongitude = gChineseCalendarAstro->getSunLongitude();
   1.605 +    umtx_unlock(&astroLock);
   1.606 +
   1.607 +    // Compute (floor(solarLongitude / (pi/6)) + 2) % 12
   1.608 +    int32_t term = ( ((int32_t)(6 * solarLongitude / CalendarAstronomer::PI)) + 2 ) % 12;
   1.609 +    if (term < 1) {
   1.610 +        term += 12;
   1.611 +    }
   1.612 +    return term;
   1.613 +}
   1.614 +
   1.615 +/**
   1.616 + * Return true if the given month lacks a major solar term.
   1.617 + * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new
   1.618 + * moon
   1.619 + */
   1.620 +UBool ChineseCalendar::hasNoMajorSolarTerm(int32_t newMoon) const {
   1.621 +    return majorSolarTerm(newMoon) ==
   1.622 +        majorSolarTerm(newMoonNear(newMoon + SYNODIC_GAP, TRUE));
   1.623 +}
   1.624 +
   1.625 +
   1.626 +//------------------------------------------------------------------
   1.627 +// Time to fields
   1.628 +//------------------------------------------------------------------
   1.629 +
   1.630 +/**
   1.631 + * Return true if there is a leap month on or after month newMoon1 and
   1.632 + * at or before month newMoon2.
   1.633 + * @param newMoon1 days after January 1, 1970 0:00 astronomical base zone
   1.634 + * of a new moon
   1.635 + * @param newMoon2 days after January 1, 1970 0:00 astronomical base zone
   1.636 + * of a new moon
   1.637 + */
   1.638 +UBool ChineseCalendar::isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) const {
   1.639 +
   1.640 +#ifdef U_DEBUG_CHNSECAL
   1.641 +    // This is only needed to debug the timeOfAngle divergence bug.
   1.642 +    // Remove this later. Liu 11/9/00
   1.643 +    if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) {
   1.644 +        U_DEBUG_CHNSECAL_MSG((
   1.645 +            "isLeapMonthBetween(%d, %d): Invalid parameters", newMoon1, newMoon2
   1.646 +            ));
   1.647 +    }
   1.648 +#endif
   1.649 +
   1.650 +    return (newMoon2 >= newMoon1) &&
   1.651 +        (isLeapMonthBetween(newMoon1, newMoonNear(newMoon2 - SYNODIC_GAP, FALSE)) ||
   1.652 +         hasNoMajorSolarTerm(newMoon2));
   1.653 +}
   1.654 +
   1.655 +/**
   1.656 + * Compute fields for the Chinese calendar system.  This method can
   1.657 + * either set all relevant fields, as required by
   1.658 + * <code>handleComputeFields()</code>, or it can just set the MONTH and
   1.659 + * IS_LEAP_MONTH fields, as required by
   1.660 + * <code>handleComputeMonthStart()</code>.
   1.661 + *
   1.662 + * <p>As a side effect, this method sets {@link #isLeapYear}.
   1.663 + * @param days days after January 1, 1970 0:00 astronomical base zone
   1.664 + * of the date to compute fields for
   1.665 + * @param gyear the Gregorian year of the given date
   1.666 + * @param gmonth the Gregorian month of the given date
   1.667 + * @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR,
   1.668 + * DAY_OF_MONTH, and DAY_OF_YEAR fields.  In either case set the MONTH
   1.669 + * and IS_LEAP_MONTH fields.
   1.670 + */
   1.671 +void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t gmonth,
   1.672 +                                  UBool setAllFields) {
   1.673 +
   1.674 +    // Find the winter solstices before and after the target date.
   1.675 +    // These define the boundaries of this Chinese year, specifically,
   1.676 +    // the position of month 11, which always contains the solstice.
   1.677 +    // We want solsticeBefore <= date < solsticeAfter.
   1.678 +    int32_t solsticeBefore;
   1.679 +    int32_t solsticeAfter = winterSolstice(gyear);
   1.680 +    if (days < solsticeAfter) {
   1.681 +        solsticeBefore = winterSolstice(gyear - 1);
   1.682 +    } else {
   1.683 +        solsticeBefore = solsticeAfter;
   1.684 +        solsticeAfter = winterSolstice(gyear + 1);
   1.685 +    }
   1.686 +
   1.687 +    // Find the start of the month after month 11.  This will be either
   1.688 +    // the prior month 12 or leap month 11 (very rare).  Also find the
   1.689 +    // start of the following month 11.
   1.690 +    int32_t firstMoon = newMoonNear(solsticeBefore + 1, TRUE);
   1.691 +    int32_t lastMoon = newMoonNear(solsticeAfter + 1, FALSE);
   1.692 +    int32_t thisMoon = newMoonNear(days + 1, FALSE); // Start of this month
   1.693 +    // Note: isLeapYear is a member variable
   1.694 +    isLeapYear = synodicMonthsBetween(firstMoon, lastMoon) == 12;
   1.695 +
   1.696 +    int32_t month = synodicMonthsBetween(firstMoon, thisMoon);
   1.697 +    if (isLeapYear && isLeapMonthBetween(firstMoon, thisMoon)) {
   1.698 +        month--;
   1.699 +    }
   1.700 +    if (month < 1) {
   1.701 +        month += 12;
   1.702 +    }
   1.703 +
   1.704 +    UBool isLeapMonth = isLeapYear &&
   1.705 +        hasNoMajorSolarTerm(thisMoon) &&
   1.706 +        !isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, FALSE));
   1.707 +
   1.708 +    internalSet(UCAL_MONTH, month-1); // Convert from 1-based to 0-based
   1.709 +    internalSet(UCAL_IS_LEAP_MONTH, isLeapMonth?1:0);
   1.710 +
   1.711 +    if (setAllFields) {
   1.712 +
   1.713 +        // Extended year and cycle year is based on the epoch year
   1.714 +        
   1.715 +        int32_t extended_year = gyear - fEpochYear;
   1.716 +        int cycle_year = gyear - CHINESE_EPOCH_YEAR;
   1.717 +        if (month < 11 ||
   1.718 +            gmonth >= UCAL_JULY) {
   1.719 +            extended_year++;
   1.720 +            cycle_year++;
   1.721 +        }
   1.722 +        int32_t dayOfMonth = days - thisMoon + 1;
   1.723 +
   1.724 +        internalSet(UCAL_EXTENDED_YEAR, extended_year);
   1.725 +
   1.726 +        // 0->0,60  1->1,1  60->1,60  61->2,1  etc.
   1.727 +        int32_t yearOfCycle;
   1.728 +        int32_t cycle = ClockMath::floorDivide(cycle_year - 1, 60, yearOfCycle);
   1.729 +        internalSet(UCAL_ERA, cycle + 1);
   1.730 +        internalSet(UCAL_YEAR, yearOfCycle + 1);
   1.731 +
   1.732 +        internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
   1.733 +
   1.734 +        // Days will be before the first new year we compute if this
   1.735 +        // date is in month 11, leap 11, 12.  There is never a leap 12.
   1.736 +        // New year computations are cached so this should be cheap in
   1.737 +        // the long run.
   1.738 +        int32_t theNewYear = newYear(gyear);
   1.739 +        if (days < theNewYear) {
   1.740 +            theNewYear = newYear(gyear-1);
   1.741 +        }
   1.742 +        internalSet(UCAL_DAY_OF_YEAR, days - theNewYear + 1);
   1.743 +    }
   1.744 +}
   1.745 +
   1.746 +
   1.747 +//------------------------------------------------------------------
   1.748 +// Fields to time
   1.749 +//------------------------------------------------------------------
   1.750 +
   1.751 +/**
   1.752 + * Return the Chinese new year of the given Gregorian year.
   1.753 + * @param gyear a Gregorian year
   1.754 + * @return days after January 1, 1970 0:00 astronomical base zone of the
   1.755 + * Chinese new year of the given year (this will be a new moon)
   1.756 + */
   1.757 +int32_t ChineseCalendar::newYear(int32_t gyear) const {
   1.758 +    UErrorCode status = U_ZERO_ERROR;
   1.759 +    int32_t cacheValue = CalendarCache::get(&gChineseCalendarNewYearCache, gyear, status);
   1.760 +
   1.761 +    if (cacheValue == 0) {
   1.762 +
   1.763 +        int32_t solsticeBefore= winterSolstice(gyear - 1);
   1.764 +        int32_t solsticeAfter = winterSolstice(gyear);
   1.765 +        int32_t newMoon1 = newMoonNear(solsticeBefore + 1, TRUE);
   1.766 +        int32_t newMoon2 = newMoonNear(newMoon1 + SYNODIC_GAP, TRUE);
   1.767 +        int32_t newMoon11 = newMoonNear(solsticeAfter + 1, FALSE);
   1.768 +        
   1.769 +        if (synodicMonthsBetween(newMoon1, newMoon11) == 12 &&
   1.770 +            (hasNoMajorSolarTerm(newMoon1) || hasNoMajorSolarTerm(newMoon2))) {
   1.771 +            cacheValue = newMoonNear(newMoon2 + SYNODIC_GAP, TRUE);
   1.772 +        } else {
   1.773 +            cacheValue = newMoon2;
   1.774 +        }
   1.775 +
   1.776 +        CalendarCache::put(&gChineseCalendarNewYearCache, gyear, cacheValue, status);
   1.777 +    }
   1.778 +    if(U_FAILURE(status)) {
   1.779 +        cacheValue = 0;
   1.780 +    }
   1.781 +    return cacheValue;
   1.782 +}
   1.783 +
   1.784 +/**
   1.785 + * Adjust this calendar to be delta months before or after a given
   1.786 + * start position, pinning the day of month if necessary.  The start
   1.787 + * position is given as a local days number for the start of the month
   1.788 + * and a day-of-month.  Used by add() and roll().
   1.789 + * @param newMoon the local days of the first day of the month of the
   1.790 + * start position (days after January 1, 1970 0:00 Asia/Shanghai)
   1.791 + * @param dom the 1-based day-of-month of the start position
   1.792 + * @param delta the number of months to move forward or backward from
   1.793 + * the start position
   1.794 + */
   1.795 +void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dom, int32_t delta) {
   1.796 +    UErrorCode status = U_ZERO_ERROR;
   1.797 +
   1.798 +    // Move to the middle of the month before our target month.
   1.799 +    newMoon += (int32_t) (CalendarAstronomer::SYNODIC_MONTH * (delta - 0.5));
   1.800 +
   1.801 +    // Search forward to the target month's new moon
   1.802 +    newMoon = newMoonNear(newMoon, TRUE);
   1.803 +
   1.804 +    // Find the target dom
   1.805 +    int32_t jd = newMoon + kEpochStartAsJulianDay - 1 + dom;
   1.806 +
   1.807 +    // Pin the dom.  In this calendar all months are 29 or 30 days
   1.808 +    // so pinning just means handling dom 30.
   1.809 +    if (dom > 29) {
   1.810 +        set(UCAL_JULIAN_DAY, jd-1);
   1.811 +        // TODO Fix this.  We really shouldn't ever have to
   1.812 +        // explicitly call complete().  This is either a bug in
   1.813 +        // this method, in ChineseCalendar, or in
   1.814 +        // Calendar.getActualMaximum().  I suspect the last.
   1.815 +        complete(status);
   1.816 +        if (U_FAILURE(status)) return;
   1.817 +        if (getActualMaximum(UCAL_DAY_OF_MONTH, status) >= dom) {
   1.818 +            if (U_FAILURE(status)) return;
   1.819 +            set(UCAL_JULIAN_DAY, jd);
   1.820 +        }
   1.821 +    } else {
   1.822 +        set(UCAL_JULIAN_DAY, jd);
   1.823 +    }
   1.824 +}
   1.825 +
   1.826 +
   1.827 +UBool
   1.828 +ChineseCalendar::inDaylightTime(UErrorCode& status) const
   1.829 +{
   1.830 +    // copied from GregorianCalendar
   1.831 +    if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) 
   1.832 +        return FALSE;
   1.833 +
   1.834 +    // Force an update of the state of the Calendar.
   1.835 +    ((ChineseCalendar*)this)->complete(status); // cast away const
   1.836 +
   1.837 +    return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
   1.838 +}
   1.839 +
   1.840 +// default century
   1.841 +
   1.842 +static UDate     gSystemDefaultCenturyStart       = DBL_MIN;
   1.843 +static int32_t   gSystemDefaultCenturyStartYear   = -1;
   1.844 +static icu::UInitOnce gSystemDefaultCenturyInitOnce = U_INITONCE_INITIALIZER;
   1.845 +
   1.846 +
   1.847 +UBool ChineseCalendar::haveDefaultCentury() const
   1.848 +{
   1.849 +    return TRUE;
   1.850 +}
   1.851 +
   1.852 +UDate ChineseCalendar::defaultCenturyStart() const
   1.853 +{
   1.854 +    return internalGetDefaultCenturyStart();
   1.855 +}
   1.856 +
   1.857 +int32_t ChineseCalendar::defaultCenturyStartYear() const
   1.858 +{
   1.859 +    return internalGetDefaultCenturyStartYear();
   1.860 +}
   1.861 +
   1.862 +static void U_CALLCONV initializeSystemDefaultCentury()
   1.863 +{
   1.864 +    // initialize systemDefaultCentury and systemDefaultCenturyYear based
   1.865 +    // on the current time.  They'll be set to 80 years before
   1.866 +    // the current time.
   1.867 +    UErrorCode status = U_ZERO_ERROR;
   1.868 +    ChineseCalendar calendar(Locale("@calendar=chinese"),status);
   1.869 +    if (U_SUCCESS(status)) {
   1.870 +        calendar.setTime(Calendar::getNow(), status);
   1.871 +        calendar.add(UCAL_YEAR, -80, status);
   1.872 +        gSystemDefaultCenturyStart     = calendar.getTime(status);
   1.873 +        gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status);
   1.874 +    }
   1.875 +    // We have no recourse upon failure unless we want to propagate the failure
   1.876 +    // out.
   1.877 +}
   1.878 +
   1.879 +UDate
   1.880 +ChineseCalendar::internalGetDefaultCenturyStart() const
   1.881 +{
   1.882 +    // lazy-evaluate systemDefaultCenturyStart
   1.883 +    umtx_initOnce(gSystemDefaultCenturyInitOnce, &initializeSystemDefaultCentury);
   1.884 +    return gSystemDefaultCenturyStart;
   1.885 +}
   1.886 +
   1.887 +int32_t
   1.888 +ChineseCalendar::internalGetDefaultCenturyStartYear() const
   1.889 +{
   1.890 +    // lazy-evaluate systemDefaultCenturyStartYear
   1.891 +    umtx_initOnce(gSystemDefaultCenturyInitOnce, &initializeSystemDefaultCentury);
   1.892 +    return    gSystemDefaultCenturyStartYear;
   1.893 +}
   1.894 +
   1.895 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar)
   1.896 +
   1.897 +U_NAMESPACE_END
   1.898 +
   1.899 +#endif
   1.900 +

mercurial