tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

chnsecal.cpp (42188B)


      1 // © 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 ******************************************************************************
      5 * Copyright (C) 2007-2014, International Business Machines Corporation
      6 * and others. All Rights Reserved.
      7 ******************************************************************************
      8 *
      9 * File CHNSECAL.CPP
     10 *
     11 * Modification History:
     12 *
     13 *   Date        Name        Description
     14 *   9/18/2007  ajmacher         ported from java ChineseCalendar
     15 *****************************************************************************
     16 */
     17 
     18 #include "chnsecal.h"
     19 
     20 #include <cstdint>
     21 
     22 #if !UCONFIG_NO_FORMATTING
     23 
     24 #include "umutex.h"
     25 #include <float.h>
     26 #include "gregoimp.h" // Math
     27 #include "astro.h" // CalendarAstronomer and CalendarCache
     28 #include "unicode/simpletz.h"
     29 #include "uhash.h"
     30 #include "ucln_in.h"
     31 #include "cstring.h"
     32 
     33 // Debugging
     34 #ifdef U_DEBUG_CHNSECAL
     35 # include <stdio.h>
     36 # include <stdarg.h>
     37 static void debug_chnsecal_loc(const char *f, int32_t l)
     38 {
     39    fprintf(stderr, "%s:%d: ", f, l);
     40 }
     41 
     42 static void debug_chnsecal_msg(const char *pat, ...)
     43 {
     44    va_list ap;
     45    va_start(ap, pat);
     46    vfprintf(stderr, pat, ap);
     47    fflush(stderr);
     48 }
     49 // must use double parens, i.e.:  U_DEBUG_CHNSECAL_MSG(("four is: %d",4));
     50 #define U_DEBUG_CHNSECAL_MSG(x) {debug_chnsecal_loc(__FILE__,__LINE__);debug_chnsecal_msg x;}
     51 #else
     52 #define U_DEBUG_CHNSECAL_MSG(x)
     53 #endif
     54 
     55 
     56 // Lazy Creation & Access synchronized by class CalendarCache with a mutex.
     57 static icu::CalendarCache *gWinterSolsticeCache = nullptr;
     58 static icu::CalendarCache *gNewYearCache = nullptr;
     59 
     60 static icu::TimeZone *gAstronomerTimeZone = nullptr;
     61 static icu::UInitOnce gAstronomerTimeZoneInitOnce {};
     62 
     63 /*
     64 * The start year of the Chinese calendar, 1CE.
     65 */
     66 static const int32_t CHINESE_EPOCH_YEAR = 1; // Gregorian year
     67                                             //
     68 /**
     69 * The start year of the Chinese calendar for cycle calculation,
     70 * the 61st year of the reign of Huang Di.
     71 * Some sources use the first year of his reign,
     72 * resulting in ERA (cycle) values one greater.
     73 */
     74 static const int32_t CYCLE_EPOCH = -2636; // Gregorian year
     75 
     76 /**
     77 * The offset from GMT in milliseconds at which we perform astronomical
     78 * computations.  Some sources use a different historically accurate
     79 * offset of GMT+7:45:40 for years before 1929; we do not do this.
     80 */
     81 static const int32_t CHINA_OFFSET = 8 * kOneHour;
     82 
     83 /**
     84 * Value to be added or subtracted from the local days of a new moon to
     85 * get close to the next or prior new moon, but not cross it.  Must be
     86 * >= 1 and < CalendarAstronomer.SYNODIC_MONTH.
     87 */
     88 static const int32_t SYNODIC_GAP = 25;
     89 
     90 
     91 U_CDECL_BEGIN
     92 static UBool calendar_chinese_cleanup() {
     93    if (gWinterSolsticeCache) {
     94        delete gWinterSolsticeCache;
     95        gWinterSolsticeCache = nullptr;
     96    }
     97    if (gNewYearCache) {
     98        delete gNewYearCache;
     99        gNewYearCache = nullptr;
    100    }
    101    if (gAstronomerTimeZone) {
    102        delete gAstronomerTimeZone;
    103        gAstronomerTimeZone = nullptr;
    104    }
    105    gAstronomerTimeZoneInitOnce.reset();
    106    return true;
    107 }
    108 U_CDECL_END
    109 
    110 U_NAMESPACE_BEGIN
    111 
    112 
    113 // Implementation of the ChineseCalendar class
    114 
    115 
    116 //-------------------------------------------------------------------------
    117 // Constructors...
    118 //-------------------------------------------------------------------------
    119 
    120 
    121 namespace {
    122 
    123 const TimeZone* getAstronomerTimeZone();
    124 int32_t newMoonNear(const TimeZone*, double, UBool, UErrorCode&);
    125 int32_t newYear(const icu::ChineseCalendar::Setting&, int32_t, UErrorCode&);
    126 UBool isLeapMonthBetween(const TimeZone*, int32_t, int32_t, UErrorCode&);
    127 
    128 } // namespace
    129 
    130 ChineseCalendar* ChineseCalendar::clone() const {
    131    return new ChineseCalendar(*this);
    132 }
    133 
    134 ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success)
    135 :   Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success),
    136    hasLeapMonthBetweenWinterSolstices(false)
    137 {
    138 }
    139 
    140 ChineseCalendar::ChineseCalendar(const ChineseCalendar& other) : Calendar(other) {
    141    hasLeapMonthBetweenWinterSolstices = other.hasLeapMonthBetweenWinterSolstices;
    142 }
    143 
    144 ChineseCalendar::~ChineseCalendar()
    145 {
    146 }
    147 
    148 const char *ChineseCalendar::getType() const { 
    149    return "chinese";
    150 }
    151 
    152 namespace { // anonymous
    153 
    154 static void U_CALLCONV initAstronomerTimeZone() {
    155    gAstronomerTimeZone = new SimpleTimeZone(CHINA_OFFSET, UNICODE_STRING_SIMPLE("CHINA_ZONE") );
    156    ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
    157 }
    158 
    159 const TimeZone* getAstronomerTimeZone() {
    160    umtx_initOnce(gAstronomerTimeZoneInitOnce, &initAstronomerTimeZone);
    161    return gAstronomerTimeZone;
    162 }
    163 
    164 } // namespace anonymous
    165 
    166 //-------------------------------------------------------------------------
    167 // Minimum / Maximum access functions
    168 //-------------------------------------------------------------------------
    169 
    170 
    171 static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
    172    // Minimum  Greatest     Least    Maximum
    173    //           Minimum   Maximum
    174    {        1,        1,    83333,    83333}, // ERA
    175    {        1,        1,       60,       60}, // YEAR
    176    {        0,        0,       11,       11}, // MONTH
    177    {        1,        1,       50,       55}, // WEEK_OF_YEAR
    178    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
    179    {        1,        1,       29,       30}, // DAY_OF_MONTH
    180    {        1,        1,      353,      385}, // DAY_OF_YEAR
    181    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
    182    {       -1,       -1,        5,        5}, // DAY_OF_WEEK_IN_MONTH
    183    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
    184    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
    185    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
    186    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
    187    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
    188    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
    189    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
    190    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
    191    { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY
    192    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
    193    { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR
    194    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
    195    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
    196    {        0,        0,        1,        1}, // IS_LEAP_MONTH
    197    {        0,        0,       11,       12}, // ORDINAL_MONTH
    198 };
    199 
    200 
    201 /**
    202 * @draft ICU 2.4
    203 */
    204 int32_t ChineseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
    205    return LIMITS[field][limitType];
    206 }
    207 
    208 
    209 //----------------------------------------------------------------------
    210 // Calendar framework
    211 //----------------------------------------------------------------------
    212 
    213 /**
    214 * Implement abstract Calendar method to return the extended year
    215 * defined by the current fields.  This will use either the ERA and
    216 * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR
    217 * field as the continuous year count, depending on which is newer.
    218 * @stable ICU 2.8
    219 */
    220 int32_t ChineseCalendar::handleGetExtendedYear(UErrorCode& status) {
    221    if (U_FAILURE(status)) {
    222        return 0;
    223    }
    224 
    225    int32_t year;
    226    // if UCAL_EXTENDED_YEAR is not older than UCAL_ERA nor UCAL_YEAR
    227    if (newerField(UCAL_EXTENDED_YEAR, newerField(UCAL_ERA, UCAL_YEAR)) ==
    228        UCAL_EXTENDED_YEAR) {
    229        year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
    230    } else {
    231        // adjust to the instance specific epoch
    232        int32_t cycle = internalGet(UCAL_ERA, 1);
    233        year = internalGet(UCAL_YEAR, 1);
    234        // Handle int32 overflow calculation for
    235        // year = year + (cycle-1) * 60 + CYCLE_EPOCH - CHINESE_EPOCH_YEAR
    236        if (uprv_add32_overflow(cycle, -1, &cycle) || // 0-based cycle
    237            uprv_mul32_overflow(cycle, 60, &cycle) ||
    238            uprv_add32_overflow(year, cycle, &year) ||
    239            uprv_add32_overflow(year, CYCLE_EPOCH-CHINESE_EPOCH_YEAR,
    240                                &year)) {
    241            status = U_ILLEGAL_ARGUMENT_ERROR;
    242            return 0;
    243        }
    244    }
    245    return year;
    246 }
    247 
    248 /**
    249 * Override Calendar method to return the number of days in the given
    250 * extended year and month.
    251 *
    252 * <p>Note: This method also reads the IS_LEAP_MONTH field to determine
    253 * whether or not the given month is a leap month.
    254 * @stable ICU 2.8
    255 */
    256 int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const {
    257    bool isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) == 1;
    258    return handleGetMonthLengthWithLeap(extendedYear, month, isLeapMonth, status);
    259 }
    260 
    261 int32_t ChineseCalendar::handleGetMonthLengthWithLeap(int32_t extendedYear, int32_t month, bool leap, UErrorCode& status) const {
    262    const Setting setting = getSetting(status);
    263    if (U_FAILURE(status)) {
    264        return 0;
    265    }
    266    int32_t thisStart = handleComputeMonthStartWithLeap(extendedYear, month, leap, status);
    267    if (U_FAILURE(status)) {
    268        return 0;
    269    }
    270    thisStart = thisStart -
    271        kEpochStartAsJulianDay + 1; // Julian day -> local days
    272    int32_t nextStart = newMoonNear(setting.zoneAstroCalc, thisStart + SYNODIC_GAP, true, status);
    273    return nextStart - thisStart;
    274 }
    275 
    276 /**
    277 * Field resolution table that incorporates IS_LEAP_MONTH.
    278 */
    279 const UFieldResolutionTable ChineseCalendar::CHINESE_DATE_PRECEDENCE[] =
    280 {
    281    {
    282        { UCAL_DAY_OF_MONTH, kResolveSTOP },
    283        { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP },
    284        { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
    285        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
    286        { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP },
    287        { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
    288        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
    289        { UCAL_DAY_OF_YEAR, kResolveSTOP },
    290        { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_IS_LEAP_MONTH, kResolveSTOP },
    291        { kResolveSTOP }
    292    },
    293    {
    294        { UCAL_WEEK_OF_YEAR, kResolveSTOP },
    295        { UCAL_WEEK_OF_MONTH, kResolveSTOP },
    296        { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP },
    297        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
    298        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
    299        { kResolveSTOP }
    300    },
    301    {{kResolveSTOP}}
    302 };
    303 
    304 /**
    305 * Override Calendar to add IS_LEAP_MONTH to the field resolution
    306 * table.
    307 * @stable ICU 2.8
    308 */
    309 const UFieldResolutionTable* ChineseCalendar::getFieldResolutionTable() const {
    310    return CHINESE_DATE_PRECEDENCE;
    311 }
    312 
    313 namespace {
    314 
    315 struct MonthInfo {
    316  int32_t month;
    317  int32_t ordinalMonth;
    318  int32_t thisMoon;
    319  bool isLeapMonth;
    320  bool hasLeapMonthBetweenWinterSolstices;
    321 };
    322 struct MonthInfo computeMonthInfo(
    323    const icu::ChineseCalendar::Setting& setting,
    324    int32_t gyear, int32_t days, UErrorCode& status);
    325 
    326 }  // namespace
    327 
    328 /**
    329 * Return the Julian day number of day before the first day of the
    330 * given month in the given extended year.
    331 * 
    332 * <p>Note: This method reads the IS_LEAP_MONTH field to determine
    333 * whether the given month is a leap month.
    334 * @param eyear the extended year
    335 * @param month the zero-based month.  The month is also determined
    336 * by reading the IS_LEAP_MONTH field.
    337 * @return the Julian day number of the day before the first
    338 * day of the given month and year
    339 * @stable ICU 2.8
    340 */
    341 int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth, UErrorCode& status) const {
    342    bool isLeapMonth = false;
    343    if (useMonth) {
    344        isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) != 0;
    345    }
    346    return handleComputeMonthStartWithLeap(eyear, month, isLeapMonth, status);
    347 }
    348 
    349 int64_t ChineseCalendar::handleComputeMonthStartWithLeap(int32_t eyear, int32_t month, bool isLeapMonth, UErrorCode& status) const {
    350    if (U_FAILURE(status)) {
    351       return 0;
    352    }
    353    // If the month is out of range, adjust it into range, and
    354    // modify the extended year value accordingly.
    355    if (month < 0 || month > 11) {
    356        if (uprv_add32_overflow(eyear, ClockMath::floorDivide(month, 12, &month), &eyear)) {
    357            status = U_ILLEGAL_ARGUMENT_ERROR;
    358            return 0;
    359        }
    360    }
    361 
    362    const Setting setting = getSetting(status);
    363    if (U_FAILURE(status)) {
    364       return 0;
    365    }
    366    int32_t gyear = eyear;
    367    int32_t theNewYear = newYear(setting, gyear, status);
    368    int32_t newMoon = newMoonNear(setting.zoneAstroCalc, theNewYear + month * 29, true, status);
    369    if (U_FAILURE(status)) {
    370       return 0;
    371    }
    372 
    373    int32_t newMonthYear = Grego::dayToYear(newMoon, status);
    374 
    375    struct MonthInfo monthInfo = computeMonthInfo(setting, newMonthYear, newMoon, status);
    376    if (U_FAILURE(status)) {
    377       return 0;
    378    }
    379    if (month != monthInfo.month-1 || isLeapMonth != monthInfo.isLeapMonth) {
    380        newMoon = newMoonNear(setting.zoneAstroCalc, newMoon + SYNODIC_GAP, true, status);
    381        if (U_FAILURE(status)) {
    382           return 0;
    383        }
    384    }
    385    int32_t julianDay;
    386    if (uprv_add32_overflow(newMoon-1, kEpochStartAsJulianDay, &julianDay)) {
    387        status = U_ILLEGAL_ARGUMENT_ERROR;
    388        return 0;
    389    }
    390 
    391    return julianDay;
    392 }
    393 
    394 
    395 /**
    396 * Override Calendar to handle leap months properly.
    397 * @stable ICU 2.8
    398 */
    399 void ChineseCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) {
    400    switch (field) {
    401    case UCAL_MONTH:
    402    case UCAL_ORDINAL_MONTH:
    403        if (amount != 0) {
    404            int32_t dom = get(UCAL_DAY_OF_MONTH, status);
    405            if (U_FAILURE(status)) break;
    406            int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day
    407            if (U_FAILURE(status)) break;
    408            int32_t moon = day - dom + 1; // New moon 
    409            offsetMonth(moon, dom, amount, status);
    410        }
    411        break;
    412    default:
    413        Calendar::add(field, amount, status);
    414        break;
    415    }
    416 }
    417 
    418 /**
    419 * Override Calendar to handle leap months properly.
    420 * @stable ICU 2.8
    421 */
    422 void ChineseCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) {
    423    add(static_cast<UCalendarDateFields>(field), amount, status);
    424 }
    425 
    426 namespace {
    427 
    428 struct RollMonthInfo {
    429    int32_t month;
    430    int32_t newMoon;
    431    int32_t thisMoon;
    432 };
    433 
    434 struct RollMonthInfo rollMonth(const TimeZone* timeZone, int32_t amount, int32_t day, int32_t month, int32_t dayOfMonth,
    435                               bool isLeapMonth, bool hasLeapMonthBetweenWinterSolstices,
    436                               UErrorCode& status) {
    437    struct RollMonthInfo output = {0, 0, 0};
    438    if (U_FAILURE(status)) {
    439        return output;
    440    }
    441 
    442    output.thisMoon = day - dayOfMonth + 1; // New moon (start of this month)
    443 
    444    // Note throughout the following:  Months 12 and 1 are never
    445    // followed by a leap month (D&R p. 185).
    446 
    447    // Compute the adjusted month number m.  This is zero-based
    448    // value from 0..11 in a non-leap year, and from 0..12 in a
    449    // leap year.
    450    if (hasLeapMonthBetweenWinterSolstices) { // (member variable)
    451        if (isLeapMonth) {
    452            ++month;
    453        } else {
    454            // Check for a prior leap month.  (In the
    455            // following, month 0 is the first month of the
    456            // year.)  Month 0 is never followed by a leap
    457            // month, and we know month m is not a leap month.
    458            // moon1 will be the start of month 0 if there is
    459            // no leap month between month 0 and month m;
    460            // otherwise it will be the start of month 1.
    461            int prevMoon = output.thisMoon -
    462                static_cast<int>(CalendarAstronomer::SYNODIC_MONTH * (month - 0.5));
    463            prevMoon = newMoonNear(timeZone, prevMoon, true, status);
    464            if (U_FAILURE(status)) {
    465               return output;
    466            }
    467            if (isLeapMonthBetween(timeZone, prevMoon, output.thisMoon, status)) {
    468                ++month;
    469            }
    470            if (U_FAILURE(status)) {
    471               return output;
    472            }
    473        }
    474    }
    475    // Now do the standard roll computation on month, with the
    476    // allowed range of 0..n-1, where n is 12 or 13.
    477    int32_t numberOfMonths = hasLeapMonthBetweenWinterSolstices ? 13 : 12; // Months in this year
    478    if (uprv_add32_overflow(amount, month, &amount)) {
    479        status = U_ILLEGAL_ARGUMENT_ERROR;
    480        return output;
    481    }
    482    output.newMoon = amount % numberOfMonths;
    483    if (output.newMoon < 0) {
    484        output.newMoon += numberOfMonths;
    485    }
    486    output.month = month;
    487    return output;
    488 }
    489 
    490 }  // namespace
    491 
    492 /**
    493 * Override Calendar to handle leap months properly.
    494 * @stable ICU 2.8
    495 */
    496 void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) {
    497    switch (field) {
    498    case UCAL_MONTH:
    499    case UCAL_ORDINAL_MONTH:
    500        if (amount != 0) {
    501            const Setting setting = getSetting(status);
    502            int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day
    503            int32_t month = get(UCAL_MONTH, status); // 0-based month
    504            int32_t dayOfMonth = get(UCAL_DAY_OF_MONTH, status);
    505            bool isLeapMonth = get(UCAL_IS_LEAP_MONTH, status) == 1;
    506            if (U_FAILURE(status)) break;
    507            struct RollMonthInfo r = rollMonth(
    508                setting.zoneAstroCalc, amount, day, month, dayOfMonth, isLeapMonth,
    509                hasLeapMonthBetweenWinterSolstices, status);
    510            if (U_FAILURE(status)) break;
    511            if (r.newMoon != r.month) {
    512                offsetMonth(r.thisMoon, dayOfMonth, r.newMoon - r.month, status);
    513            }
    514        }
    515        break;
    516    default:
    517        Calendar::roll(field, amount, status);
    518        break;
    519    }
    520 }
    521 
    522 void ChineseCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) {
    523    roll(static_cast<UCalendarDateFields>(field), amount, status);
    524 }
    525 
    526 
    527 //------------------------------------------------------------------
    528 // Support methods and constants
    529 //------------------------------------------------------------------
    530 
    531 namespace {
    532 /**
    533 * Convert local days to UTC epoch milliseconds.
    534 * This is not an accurate conversion in that getTimezoneOffset
    535 * takes the milliseconds in GMT (not local time). In theory, more
    536 * accurate algorithm can be implemented but practically we do not need
    537 * to go through that complication as long as the historical timezone
    538 * changes did not happen around the 'tricky' new moon (new moon around
    539 * midnight).
    540 *
    541 * @param timeZone time zone for the Astro calculation.
    542 * @param days days after January 1, 1970 0:00 in the astronomical base zone
    543 * @return milliseconds after January 1, 1970 0:00 GMT
    544 */
    545 double daysToMillis(const TimeZone* timeZone, double days, UErrorCode& status) {
    546    if (U_FAILURE(status)) {
    547        return 0;
    548    }
    549    double millis = days * kOneDay;
    550    if (timeZone != nullptr) {
    551        int32_t rawOffset, dstOffset;
    552        timeZone->getOffset(millis, false, rawOffset, dstOffset, status);
    553        if (U_FAILURE(status)) {
    554            return 0;
    555        }
    556        return millis - static_cast<double>(rawOffset + dstOffset);
    557    }
    558    return millis - static_cast<double>(CHINA_OFFSET);
    559 }
    560 
    561 /**
    562 * Convert UTC epoch milliseconds to local days.
    563 * @param timeZone time zone for the Astro calculation.
    564 * @param millis milliseconds after January 1, 1970 0:00 GMT
    565 * @return days after January 1, 1970 0:00 in the astronomical base zone
    566 */
    567 double millisToDays(const TimeZone* timeZone, double millis, UErrorCode& status) {
    568    if (U_FAILURE(status)) {
    569        return 0;
    570    }
    571    if (timeZone != nullptr) {
    572        int32_t rawOffset, dstOffset;
    573        timeZone->getOffset(millis, false, rawOffset, dstOffset, status);
    574        if (U_FAILURE(status)) {
    575            return 0;
    576        }
    577        return ClockMath::floorDivide(millis + static_cast<double>(rawOffset + dstOffset), kOneDay);
    578    }
    579    return ClockMath::floorDivide(millis + static_cast<double>(CHINA_OFFSET), kOneDay);
    580 }
    581 
    582 //------------------------------------------------------------------
    583 // Astronomical computations
    584 //------------------------------------------------------------------
    585 
    586 
    587 /**
    588 * Return the major solar term on or after December 15 of the given
    589 * Gregorian year, that is, the winter solstice of the given year.
    590 * Computations are relative to Asia/Shanghai time zone.
    591 * @param setting setting (time zone and caches) for the Astro calculation.
    592 * @param gyear a Gregorian year
    593 * @return days after January 1, 1970 0:00 Asia/Shanghai of the
    594 * winter solstice of the given year
    595 */
    596 int32_t winterSolstice(const icu::ChineseCalendar::Setting& setting,
    597                       int32_t gyear, UErrorCode& status) {
    598    if (U_FAILURE(status)) {
    599        return 0;
    600    }
    601    const TimeZone* timeZone = setting.zoneAstroCalc;
    602 
    603    int32_t cacheValue = CalendarCache::get(setting.winterSolsticeCache, gyear, status);
    604    if (U_FAILURE(status)) {
    605        return 0;
    606    }
    607 
    608    if (cacheValue == 0) {
    609        // In books December 15 is used, but it fails for some years
    610        // using our algorithms, e.g.: 1298 1391 1492 1553 1560.  That
    611        // is, winterSolstice(1298) starts search at Dec 14 08:00:00
    612        // PST 1298 with a final result of Dec 14 10:31:59 PST 1299.
    613        double ms = daysToMillis(timeZone, Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1), status);
    614        if (U_FAILURE(status)) {
    615            return 0;
    616        }
    617 
    618        // Winter solstice is 270 degrees solar longitude aka Dongzhi
    619        double days = millisToDays(timeZone,
    620                                   CalendarAstronomer(ms)
    621                                       .getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), true),
    622                                   status);
    623        if (U_FAILURE(status)) {
    624            return 0;
    625        }
    626        if (days < INT32_MIN || days > INT32_MAX) {
    627            status = U_ILLEGAL_ARGUMENT_ERROR;
    628            return 0;
    629        }
    630        cacheValue = static_cast<int32_t>(days);
    631        CalendarCache::put(setting.winterSolsticeCache, gyear, cacheValue, status);
    632    }
    633    if(U_FAILURE(status)) {
    634        cacheValue = 0;
    635    }
    636    return cacheValue;
    637 }
    638 
    639 /**
    640 * Return the closest new moon to the given date, searching either
    641 * forward or backward in time.
    642 * @param timeZone time zone for the Astro calculation.
    643 * @param days days after January 1, 1970 0:00 Asia/Shanghai
    644 * @param after if true, search for a new moon on or after the given
    645 * date; otherwise, search for a new moon before it
    646 * @param status
    647 * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest
    648 * new moon after or before <code>days</code>
    649 */
    650 int32_t newMoonNear(const TimeZone* timeZone, double days, UBool after, UErrorCode& status) {
    651    if (U_FAILURE(status)) {
    652        return 0;
    653    }
    654    double ms = daysToMillis(timeZone, days, status);
    655    if (U_FAILURE(status)) {
    656        return 0;
    657    }
    658    return static_cast<int32_t>(millisToDays(
    659        timeZone,
    660        CalendarAstronomer(ms)
    661              .getMoonTime(CalendarAstronomer::NEW_MOON(), after),
    662              status));
    663 }
    664 
    665 /**
    666 * Return the nearest integer number of synodic months between
    667 * two dates.
    668 * @param day1 days after January 1, 1970 0:00 Asia/Shanghai
    669 * @param day2 days after January 1, 1970 0:00 Asia/Shanghai
    670 * @return the nearest integer number of months between day1 and day2
    671 */
    672 int32_t synodicMonthsBetween(int32_t day1, int32_t day2) {
    673    double roundme = ((day2 - day1) / CalendarAstronomer::SYNODIC_MONTH);
    674    return static_cast<int32_t>(roundme + (roundme >= 0 ? .5 : -.5));
    675 }
    676 
    677 /**
    678 * Return the major solar term on or before a given date.  This
    679 * will be an integer from 1..12, with 1 corresponding to 330 degrees,
    680 * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees.
    681 * @param timeZone time zone for the Astro calculation.
    682 * @param days days after January 1, 1970 0:00 Asia/Shanghai
    683 */
    684 int32_t majorSolarTerm(const TimeZone* timeZone, int32_t days, UErrorCode& status) {
    685    if (U_FAILURE(status)) {
    686        return 0;
    687    }
    688    // Compute (floor(solarLongitude / (pi/6)) + 2) % 12
    689    double ms = daysToMillis(timeZone, days, status);
    690    if (U_FAILURE(status)) {
    691        return 0;
    692    }
    693    int32_t term = ((static_cast<int32_t>(6 * CalendarAstronomer(ms)
    694                                .getSunLongitude() / CalendarAstronomer::PI)) + 2 ) % 12;
    695    if (U_FAILURE(status)) {
    696        return 0;
    697    }
    698    if (term < 1) {
    699        term += 12;
    700    }
    701    return term;
    702 }
    703 
    704 /**
    705 * Return true if the given month lacks a major solar term.
    706 * @param timeZone time zone for the Astro calculation.
    707 * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new
    708 * moon
    709 */
    710 UBool hasNoMajorSolarTerm(const TimeZone* timeZone, int32_t newMoon, UErrorCode& status) {
    711    if (U_FAILURE(status)) {
    712        return false;
    713    }
    714    int32_t term1 = majorSolarTerm(timeZone, newMoon, status);
    715    int32_t term2 = majorSolarTerm(
    716        timeZone, newMoonNear(timeZone, newMoon + SYNODIC_GAP, true, status), status);
    717    if (U_FAILURE(status)) {
    718        return false;
    719    }
    720    return term1 == term2;
    721 }
    722 
    723 
    724 //------------------------------------------------------------------
    725 // Time to fields
    726 //------------------------------------------------------------------
    727 
    728 /**
    729 * Return true if there is a leap month on or after month newMoon1 and
    730 * at or before month newMoon2.
    731 * @param timeZone time zone for the Astro calculation.
    732 * @param newMoon1 days after January 1, 1970 0:00 astronomical base zone
    733 * of a new moon
    734 * @param newMoon2 days after January 1, 1970 0:00 astronomical base zone
    735 * of a new moon
    736 */
    737 UBool isLeapMonthBetween(const TimeZone* timeZone, int32_t newMoon1, int32_t newMoon2, UErrorCode& status) {
    738    if (U_FAILURE(status)) {
    739        return false;
    740    }
    741 
    742 #ifdef U_DEBUG_CHNSECAL
    743    // This is only needed to debug the timeOfAngle divergence bug.
    744    // Remove this later. Liu 11/9/00
    745    if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) {
    746        U_DEBUG_CHNSECAL_MSG((
    747            "isLeapMonthBetween(%d, %d): Invalid parameters", newMoon1, newMoon2
    748            ));
    749    }
    750 #endif
    751 
    752    while (newMoon2 >= newMoon1) {
    753        if (hasNoMajorSolarTerm(timeZone, newMoon2, status)) {
    754            return true;
    755        }
    756        newMoon2 = newMoonNear(timeZone, newMoon2 - SYNODIC_GAP, false, status);
    757        if (U_FAILURE(status)) {
    758            return false;
    759        }
    760    }
    761    return false;
    762 }
    763 
    764 
    765 /**
    766 * Compute the information about the year.
    767 * @param setting setting (time zone and caches) for the Astro calculation.
    768 * @param gyear the Gregorian year of the given date
    769 * @param days days after January 1, 1970 0:00 astronomical base zone
    770 * of the date to compute fields for
    771 * @return The MonthInfo result.
    772 */
    773 struct MonthInfo computeMonthInfo(
    774    const icu::ChineseCalendar::Setting& setting,
    775    int32_t gyear, int32_t days, UErrorCode& status) {
    776    struct MonthInfo output = {0, 0, 0, false, false};
    777    if (U_FAILURE(status)) {
    778        return output;
    779    }
    780    // Find the winter solstices before and after the target date.
    781    // These define the boundaries of this Chinese year, specifically,
    782    // the position of month 11, which always contains the solstice.
    783    // We want solsticeBefore <= date < solsticeAfter.
    784    int32_t solsticeBefore;
    785    int32_t solsticeAfter = winterSolstice(setting, gyear, status);
    786    if (U_FAILURE(status)) {
    787        return output;
    788    }
    789    if (days < solsticeAfter) {
    790        int32_t gprevious_year;
    791        if (uprv_add32_overflow(gyear, -1, &gprevious_year)) {
    792            status = U_ILLEGAL_ARGUMENT_ERROR;
    793            return output;
    794        }
    795        solsticeBefore = winterSolstice(setting, gprevious_year, status);
    796    } else {
    797        solsticeBefore = solsticeAfter;
    798        int32_t gnext_year;
    799        if (uprv_add32_overflow(gyear, 1, &gnext_year)) {
    800            status = U_ILLEGAL_ARGUMENT_ERROR;
    801            return output;
    802        }
    803        solsticeAfter = winterSolstice(setting, gnext_year, status);
    804    }
    805    if (!(solsticeBefore <= days && days < solsticeAfter)) {
    806        status = U_ILLEGAL_ARGUMENT_ERROR;
    807    }
    808    if (U_FAILURE(status)) {
    809        return output;
    810    }
    811 
    812    const TimeZone* timeZone = setting.zoneAstroCalc;
    813    // Find the start of the month after month 11.  This will be either
    814    // the prior month 12 or leap month 11 (very rare).  Also find the
    815    // start of the following month 11.
    816    int32_t firstMoon = newMoonNear(timeZone, solsticeBefore + 1, true, status);
    817    int32_t lastMoon = newMoonNear(timeZone, solsticeAfter + 1, false, status);
    818    if (U_FAILURE(status)) {
    819        return output;
    820    }
    821    output.thisMoon = newMoonNear(timeZone, days + 1, false, status); // Start of this month
    822    if (U_FAILURE(status)) {
    823        return output;
    824    }
    825    output.hasLeapMonthBetweenWinterSolstices = synodicMonthsBetween(firstMoon, lastMoon) == 12;
    826 
    827    output.month = synodicMonthsBetween(firstMoon, output.thisMoon);
    828    int32_t theNewYear = newYear(setting, gyear, status);
    829    if (U_FAILURE(status)) {
    830        return output;
    831    }
    832    if (days < theNewYear) {
    833        int32_t gprevious_year;
    834        if (uprv_add32_overflow(gyear, -1, &gprevious_year)) {
    835            status = U_ILLEGAL_ARGUMENT_ERROR;
    836            return output;
    837        }
    838        theNewYear = newYear(setting, gprevious_year, status);
    839        if (U_FAILURE(status)) {
    840            return output;
    841        }
    842    }
    843    if (output.hasLeapMonthBetweenWinterSolstices &&
    844        isLeapMonthBetween(timeZone, firstMoon, output.thisMoon, status)) {
    845        output.month--;
    846    }
    847    if (U_FAILURE(status)) {
    848        return output;
    849    }
    850    if (output.month < 1) {
    851        output.month += 12;
    852    }
    853    output.ordinalMonth = synodicMonthsBetween(theNewYear, output.thisMoon);
    854    if (output.ordinalMonth < 0) {
    855        output.ordinalMonth += 12;
    856    }
    857    output.isLeapMonth = output.hasLeapMonthBetweenWinterSolstices &&
    858        hasNoMajorSolarTerm(timeZone, output.thisMoon, status) &&
    859        !isLeapMonthBetween(timeZone, firstMoon,
    860                            newMoonNear(timeZone, output.thisMoon - SYNODIC_GAP, false, status),
    861                            status);
    862    if (U_FAILURE(status)) {
    863        return output;
    864    }
    865    return output;
    866 }
    867 
    868 }  // namespace
    869 
    870 /**
    871 * Override Calendar to compute several fields specific to the Chinese
    872 * calendar system.  These are:
    873 *
    874 * <ul><li>ERA
    875 * <li>YEAR
    876 * <li>MONTH
    877 * <li>DAY_OF_MONTH
    878 * <li>DAY_OF_YEAR
    879 * <li>EXTENDED_YEAR</ul>
    880 * 
    881 * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
    882 * method is called.  The getGregorianXxx() methods return Gregorian
    883 * calendar equivalents for the given Julian day.
    884 *
    885 * <p>Compute the ChineseCalendar-specific field IS_LEAP_MONTH.
    886 * @stable ICU 2.8
    887 */
    888 void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode & status) {
    889    if (U_FAILURE(status)) {
    890        return;
    891    }
    892    int32_t days;
    893    if (uprv_add32_overflow(julianDay, -kEpochStartAsJulianDay, &days)) {
    894        status = U_ILLEGAL_ARGUMENT_ERROR;
    895        return;
    896    }
    897    int32_t gyear = getGregorianYear();
    898    int32_t gmonth = getGregorianMonth();
    899 
    900    const Setting setting = getSetting(status);
    901    if (U_FAILURE(status)) {
    902       return;
    903    }
    904    struct MonthInfo monthInfo = computeMonthInfo(setting, gyear, days, status);
    905    if (U_FAILURE(status)) {
    906       return;
    907    }
    908    hasLeapMonthBetweenWinterSolstices = monthInfo.hasLeapMonthBetweenWinterSolstices;
    909 
    910    // Extended year and cycle year is based on the epoch year
    911    int32_t eyear;
    912    int32_t cycle_year;
    913    if (uprv_add32_overflow(gyear, -CHINESE_EPOCH_YEAR, &eyear) ||
    914        uprv_add32_overflow(gyear, -CYCLE_EPOCH, &cycle_year)) {
    915        status = U_ILLEGAL_ARGUMENT_ERROR;
    916        return;
    917    }
    918    if (monthInfo.month < 11 ||
    919        gmonth >= UCAL_JULY) {
    920        // forward to next year
    921        if (uprv_add32_overflow(eyear, 1, &eyear) ||
    922            uprv_add32_overflow(cycle_year, 1, &cycle_year)) {
    923            status = U_ILLEGAL_ARGUMENT_ERROR;
    924            return;
    925        }
    926    }
    927    int32_t dayOfMonth = days - monthInfo.thisMoon + 1;
    928 
    929    // 0->0,60  1->1,1  60->1,60  61->2,1  etc.
    930    int32_t yearOfCycle;
    931    int32_t cycle = ClockMath::floorDivide(cycle_year - 1, 60, &yearOfCycle);
    932 
    933    // Days will be before the first new year we compute if this
    934    // date is in month 11, leap 11, 12.  There is never a leap 12.
    935    // New year computations are cached so this should be cheap in
    936    // the long run.
    937    int32_t theNewYear = newYear(setting, gyear, status);
    938    if (U_FAILURE(status)) {
    939       return;
    940    }
    941    if (days < theNewYear) {
    942        int32_t gprevious_year;
    943        if (uprv_add32_overflow(gyear, -1, &gprevious_year)) {
    944            status = U_ILLEGAL_ARGUMENT_ERROR;
    945            return;
    946        }
    947        theNewYear = newYear(setting, gprevious_year, status);
    948    }
    949    if (U_FAILURE(status)) {
    950       return;
    951    }
    952    cycle++;
    953    yearOfCycle++;
    954    int32_t dayOfYear = days - theNewYear + 1;
    955 
    956    int32_t minYear = this->handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MINIMUM);
    957    if (eyear < minYear) {
    958        if (!isLenient()) {
    959            status = U_ILLEGAL_ARGUMENT_ERROR;
    960            return;
    961        }
    962        eyear = minYear;
    963    }
    964    int32_t maxYear = this->handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MAXIMUM);
    965    if (maxYear < eyear) {
    966        if (!isLenient()) {
    967            status = U_ILLEGAL_ARGUMENT_ERROR;
    968            return;
    969        }
    970        eyear = maxYear;
    971    }
    972 
    973    internalSet(UCAL_MONTH, monthInfo.month-1); // Convert from 1-based to 0-based
    974    internalSet(UCAL_ORDINAL_MONTH, monthInfo.ordinalMonth); // Convert from 1-based to 0-based
    975    internalSet(UCAL_IS_LEAP_MONTH, monthInfo.isLeapMonth?1:0);
    976 
    977    internalSet(UCAL_EXTENDED_YEAR, eyear);
    978    internalSet(UCAL_ERA, cycle);
    979    internalSet(UCAL_YEAR, yearOfCycle);
    980    internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
    981    internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
    982 }
    983 
    984 //------------------------------------------------------------------
    985 // Fields to time
    986 //------------------------------------------------------------------
    987 
    988 namespace {
    989 
    990 /**
    991 * Return the Chinese new year of the given Gregorian year.
    992 * @param setting setting (time zone and caches) for the Astro calculation.
    993 * @param gyear a Gregorian year
    994 * @return days after January 1, 1970 0:00 astronomical base zone of the
    995 * Chinese new year of the given year (this will be a new moon)
    996 */
    997 int32_t newYear(const icu::ChineseCalendar::Setting& setting,
    998                int32_t gyear, UErrorCode& status) {
    999    if (U_FAILURE(status)) {
   1000        return 0;
   1001    }
   1002    const TimeZone* timeZone = setting.zoneAstroCalc;
   1003    int32_t cacheValue = CalendarCache::get(setting.newYearCache, gyear, status);
   1004    if (U_FAILURE(status)) {
   1005        return 0;
   1006    }
   1007 
   1008    if (cacheValue == 0) {
   1009 
   1010        int32_t gprevious_year;
   1011        if (uprv_add32_overflow(gyear, -1, &gprevious_year)) {
   1012            status = U_ILLEGAL_ARGUMENT_ERROR;
   1013            return 0;
   1014        }
   1015        int32_t solsticeBefore= winterSolstice(setting, gprevious_year, status);
   1016        int32_t solsticeAfter = winterSolstice(setting, gyear, status);
   1017        int32_t newMoon1 = newMoonNear(timeZone, solsticeBefore + 1, true, status);
   1018        int32_t newMoon2 = newMoonNear(timeZone, newMoon1 + SYNODIC_GAP, true, status);
   1019        int32_t newMoon11 = newMoonNear(timeZone, solsticeAfter + 1, false, status);
   1020        if (U_FAILURE(status)) {
   1021            return 0;
   1022        }
   1023 
   1024        if (synodicMonthsBetween(newMoon1, newMoon11) == 12 &&
   1025            (hasNoMajorSolarTerm(timeZone, newMoon1, status) ||
   1026             hasNoMajorSolarTerm(timeZone, newMoon2, status))) {
   1027            cacheValue = newMoonNear(timeZone, newMoon2 + SYNODIC_GAP, true, status);
   1028        } else {
   1029            cacheValue = newMoon2;
   1030        }
   1031        if (U_FAILURE(status)) {
   1032            return 0;
   1033        }
   1034 
   1035        CalendarCache::put(setting.newYearCache, gyear, cacheValue, status);
   1036    }
   1037    if(U_FAILURE(status)) {
   1038        cacheValue = 0;
   1039    }
   1040    return cacheValue;
   1041 }
   1042 
   1043 }  // namespace
   1044 
   1045 /**
   1046 * Adjust this calendar to be delta months before or after a given
   1047 * start position, pinning the day of month if necessary.  The start
   1048 * position is given as a local days number for the start of the month
   1049 * and a day-of-month.  Used by add() and roll().
   1050 * @param newMoon the local days of the first day of the month of the
   1051 * start position (days after January 1, 1970 0:00 Asia/Shanghai)
   1052 * @param dayOfMonth the 1-based day-of-month of the start position
   1053 * @param delta the number of months to move forward or backward from
   1054 * the start position
   1055 * @param status The status.
   1056 */
   1057 void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dayOfMonth, int32_t delta,
   1058                                  UErrorCode& status) {
   1059    const Setting setting = getSetting(status);
   1060    if (U_FAILURE(status)) {
   1061        return;
   1062    }
   1063 
   1064    // Move to the middle of the month before our target month.
   1065    double value = newMoon;
   1066    value += (CalendarAstronomer::SYNODIC_MONTH *
   1067                          (static_cast<double>(delta) - 0.5));
   1068    if (value < INT32_MIN || value > INT32_MAX) {
   1069        status = U_ILLEGAL_ARGUMENT_ERROR;
   1070        return;
   1071    }
   1072    newMoon = static_cast<int32_t>(value);
   1073 
   1074    // Search forward to the target month's new moon
   1075    newMoon = newMoonNear(setting.zoneAstroCalc, newMoon, true, status);
   1076    if (U_FAILURE(status)) {
   1077        return;
   1078    }
   1079 
   1080    // Find the target dayOfMonth
   1081    int32_t jd;
   1082    if (uprv_add32_overflow(newMoon, kEpochStartAsJulianDay - 1, &jd) ||
   1083        uprv_add32_overflow(jd, dayOfMonth, &jd)) {
   1084        status = U_ILLEGAL_ARGUMENT_ERROR;
   1085        return;
   1086    }
   1087 
   1088    // Pin the dayOfMonth.  In this calendar all months are 29 or 30 days
   1089    // so pinning just means handling dayOfMonth 30.
   1090    if (dayOfMonth > 29) {
   1091        set(UCAL_JULIAN_DAY, jd-1);
   1092        // TODO Fix this.  We really shouldn't ever have to
   1093        // explicitly call complete().  This is either a bug in
   1094        // this method, in ChineseCalendar, or in
   1095        // Calendar.getActualMaximum().  I suspect the last.
   1096        complete(status);
   1097        if (U_FAILURE(status)) return;
   1098        if (getActualMaximum(UCAL_DAY_OF_MONTH, status) >= dayOfMonth) {
   1099            if (U_FAILURE(status)) return;
   1100            set(UCAL_JULIAN_DAY, jd);
   1101        }
   1102    } else {
   1103        set(UCAL_JULIAN_DAY, jd);
   1104    }
   1105 }
   1106 
   1107 IMPL_SYSTEM_DEFAULT_CENTURY(ChineseCalendar, "@calendar=chinese")
   1108 
   1109 bool
   1110 ChineseCalendar::inTemporalLeapYear(UErrorCode &status) const
   1111 {
   1112    int32_t days = getActualMaximum(UCAL_DAY_OF_YEAR, status);
   1113    if (U_FAILURE(status)) return false;
   1114    return days > 360;
   1115 }
   1116 
   1117 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar)
   1118 
   1119 
   1120 static const char * const gTemporalLeapMonthCodes[] = {
   1121    "M01L", "M02L", "M03L", "M04L", "M05L", "M06L",
   1122    "M07L", "M08L", "M09L", "M10L", "M11L", "M12L", nullptr
   1123 };
   1124 
   1125 const char* ChineseCalendar::getTemporalMonthCode(UErrorCode &status) const {
   1126    // We need to call get, not internalGet, to force the calculation
   1127    // from UCAL_ORDINAL_MONTH.
   1128    int32_t is_leap = get(UCAL_IS_LEAP_MONTH, status);
   1129    if (U_FAILURE(status)) return nullptr;
   1130    if (is_leap != 0) {
   1131        int32_t month = get(UCAL_MONTH, status);
   1132        if (U_FAILURE(status)) return nullptr;
   1133        return gTemporalLeapMonthCodes[month];
   1134    }
   1135    return Calendar::getTemporalMonthCode(status);
   1136 }
   1137 
   1138 void
   1139 ChineseCalendar::setTemporalMonthCode(const char* code, UErrorCode& status )
   1140 {
   1141    if (U_FAILURE(status)) return;
   1142    int32_t len = static_cast<int32_t>(uprv_strlen(code));
   1143    if (len != 4 || code[0] != 'M' || code[3] != 'L') {
   1144        set(UCAL_IS_LEAP_MONTH, 0);
   1145        return Calendar::setTemporalMonthCode(code, status);
   1146    }
   1147    for (int m = 0; gTemporalLeapMonthCodes[m] != nullptr; m++) {
   1148        if (uprv_strcmp(code, gTemporalLeapMonthCodes[m]) == 0) {
   1149            set(UCAL_MONTH, m);
   1150            set(UCAL_IS_LEAP_MONTH, 1);
   1151            return;
   1152        }
   1153    }
   1154    status = U_ILLEGAL_ARGUMENT_ERROR;
   1155 }
   1156 
   1157 int32_t ChineseCalendar::internalGetMonth(UErrorCode& status) const {
   1158    if (U_FAILURE(status)) {
   1159        return 0;
   1160    }
   1161    if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
   1162        return internalGet(UCAL_MONTH);
   1163    }
   1164    LocalPointer<Calendar> temp(this->clone());
   1165    temp->set(UCAL_MONTH, 0);
   1166    temp->set(UCAL_IS_LEAP_MONTH, 0);
   1167    temp->set(UCAL_DATE, 1);
   1168    // Calculate the UCAL_MONTH and UCAL_IS_LEAP_MONTH by adding number of
   1169    // months.
   1170    temp->roll(UCAL_MONTH, internalGet(UCAL_ORDINAL_MONTH), status);
   1171    if (U_FAILURE(status)) {
   1172        return 0;
   1173    }
   1174 
   1175    ChineseCalendar* nonConstThis = const_cast<ChineseCalendar*>(this); // cast away const
   1176    nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, temp->get(UCAL_IS_LEAP_MONTH, status));
   1177    int32_t month = temp->get(UCAL_MONTH, status);
   1178    if (U_FAILURE(status)) {
   1179        return 0;
   1180    }
   1181    nonConstThis->internalSet(UCAL_MONTH, month);
   1182    return month;
   1183 }
   1184 
   1185 int32_t ChineseCalendar::internalGetMonth(int32_t defaultValue, UErrorCode& status) const {
   1186    if (U_FAILURE(status)) {
   1187        return 0;
   1188    }
   1189    switch (resolveFields(kMonthPrecedence)) {
   1190        case UCAL_MONTH:
   1191            return internalGet(UCAL_MONTH);
   1192        case UCAL_ORDINAL_MONTH:
   1193            return internalGetMonth(status);
   1194        default:
   1195            return defaultValue;
   1196    }
   1197 }
   1198 
   1199 ChineseCalendar::Setting ChineseCalendar::getSetting(UErrorCode&) const {
   1200  return {
   1201        getAstronomerTimeZone(),
   1202        &gWinterSolsticeCache,
   1203        &gNewYearCache
   1204  };
   1205 }
   1206 
   1207 int32_t
   1208 ChineseCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
   1209 {
   1210    if (U_FAILURE(status)) {
   1211       return 0;
   1212    }
   1213    if (field == UCAL_DATE) {
   1214        LocalPointer<ChineseCalendar> cal(clone(), status);
   1215        if(U_FAILURE(status)) {
   1216            return 0;
   1217        }
   1218        cal->setLenient(true);
   1219        cal->prepareGetActual(field,false,status);
   1220        int32_t year = cal->get(UCAL_EXTENDED_YEAR, status);
   1221        int32_t month = cal->get(UCAL_MONTH, status);
   1222        bool leap = cal->get(UCAL_IS_LEAP_MONTH, status) != 0;
   1223        return handleGetMonthLengthWithLeap(year, month, leap, status);
   1224    }
   1225    return Calendar::getActualMaximum(field, status);
   1226 }
   1227 
   1228 U_NAMESPACE_END
   1229 
   1230 #endif