ICU4XChineseBasedCalendar.cpp (8029B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "mozilla/intl/calendar/ICU4XChineseBasedCalendar.h" 6 7 #include "mozilla/Assertions.h" 8 9 namespace mozilla::intl::calendar { 10 11 ICU4XChineseBasedCalendar::ICU4XChineseBasedCalendar( 12 icu4x::capi::CalendarKind kind, const icu::Locale& locale, 13 UErrorCode& success) 14 : ICU4XCalendar(kind, locale, success) {} 15 16 ICU4XChineseBasedCalendar::ICU4XChineseBasedCalendar( 17 icu4x::capi::CalendarKind kind, const icu::TimeZone& timeZone, 18 const icu::Locale& locale, UErrorCode& success) 19 : ICU4XCalendar(kind, timeZone, locale, success) {} 20 21 ICU4XChineseBasedCalendar::ICU4XChineseBasedCalendar( 22 const ICU4XChineseBasedCalendar& other) 23 : ICU4XCalendar(other) {} 24 25 ICU4XChineseBasedCalendar::~ICU4XChineseBasedCalendar() = default; 26 27 //////////////////////////////////////////// 28 // ICU4XCalendar implementation overrides // 29 //////////////////////////////////////////// 30 31 bool ICU4XChineseBasedCalendar::hasLeapMonths() const { return true; } 32 33 bool ICU4XChineseBasedCalendar::hasMonthCode(MonthCode monthCode) const { 34 return monthCode.ordinal() <= 12; 35 } 36 37 bool ICU4XChineseBasedCalendar::requiresFallbackForExtendedYear( 38 int32_t year) const { 39 // Same limits as in js/src/builtin/temporal/Calendar.cpp. 40 return std::abs(year) > 10'000; 41 } 42 43 bool ICU4XChineseBasedCalendar::requiresFallbackForGregorianYear( 44 int32_t year) const { 45 // Same limits as in js/src/builtin/temporal/Calendar.cpp. 46 return std::abs(year) > 10'000; 47 } 48 49 //////////////////////////////////////////// 50 // icu::Calendar implementation overrides // 51 //////////////////////////////////////////// 52 53 bool ICU4XChineseBasedCalendar::inTemporalLeapYear(UErrorCode& status) const { 54 int32_t days = getActualMaximum(UCAL_DAY_OF_YEAR, status); 55 if (U_FAILURE(status)) { 56 return false; 57 } 58 59 constexpr int32_t maxDaysInMonth = 30; 60 constexpr int32_t monthsInNonLeapYear = 12; 61 return days > (monthsInNonLeapYear * maxDaysInMonth); 62 } 63 64 void ICU4XChineseBasedCalendar::handleComputeFields(int32_t julianDay, 65 UErrorCode& status) { 66 int32_t gyear = getGregorianYear(); 67 68 // Use the fallback calendar for years outside the range supported by ICU4X. 69 if (requiresFallbackForGregorianYear(gyear)) { 70 handleComputeFieldsFromFallback(julianDay, status); 71 return; 72 } 73 74 int32_t gmonth = getGregorianMonth() + 1; 75 int32_t gday = getGregorianDayOfMonth(); 76 77 MOZ_ASSERT(1 <= gmonth && gmonth <= 12); 78 MOZ_ASSERT(1 <= gday && gday <= 31); 79 80 auto date = createICU4XDate(ISODate{gyear, gmonth, gday}, status); 81 if (U_FAILURE(status)) { 82 return; 83 } 84 MOZ_ASSERT(date); 85 86 MonthCode monthCode = monthCodeFrom(date.get()); 87 int32_t extendedYear = 88 icu4x::capi::icu4x_Date_era_year_or_related_iso_mv1(date.get()); 89 int32_t month = icu4x::capi::icu4x_Date_ordinal_month_mv1(date.get()); 90 int32_t dayOfMonth = icu4x::capi::icu4x_Date_day_of_month_mv1(date.get()); 91 int32_t dayOfYear = icu4x::capi::icu4x_Date_day_of_year_mv1(date.get()); 92 93 MOZ_ASSERT(1 <= month && month <= 13); 94 MOZ_ASSERT(1 <= dayOfMonth && dayOfMonth <= 30); 95 MOZ_ASSERT(1 <= dayOfYear && dayOfYear <= (13 * 30)); 96 97 // Difference between the Chinese calendar era (the extended year 1) and the 98 // start year used for cycle computations. This is the sixtieth year of reign 99 // of Huáng Dì. Other sources use the first year of reign, which means using 100 // -2697 instead. Both numbers result in the same year of cycle, but the 101 // latter number gives a different cycle number. To align with the ICU4C 102 // Chinese calendar implementation, we use -2637 here. 103 constexpr int32_t chineseCalendarYearDiff = -2637; 104 105 // Compute the cycle and year of cycle relative to the Chinese calendar, even 106 // when this is the Dangi calendar. 107 int32_t chineseCalendarYear = extendedYear - chineseCalendarYearDiff; 108 int32_t cycle_year = chineseCalendarYear - 1; 109 int32_t cycle = FloorDiv(cycle_year, 60); 110 int32_t yearOfCycle = cycle_year - (cycle * 60); 111 112 internalSet(UCAL_ERA, cycle + 1); 113 internalSet(UCAL_YEAR, yearOfCycle + 1); 114 internalSet(UCAL_EXTENDED_YEAR, extendedYear); 115 internalSet(UCAL_MONTH, monthCode.ordinal() - 1); 116 internalSet(UCAL_ORDINAL_MONTH, month - 1); 117 internalSet(UCAL_IS_LEAP_MONTH, monthCode.isLeapMonth() ? 1 : 0); 118 internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); 119 internalSet(UCAL_DAY_OF_YEAR, dayOfYear); 120 } 121 122 // Limits table copied from i18n/chnsecal.cpp. Licensed under: 123 // 124 // © 2016 and later: Unicode, Inc. and others. 125 // License & terms of use: http://www.unicode.org/copyright.html 126 static const int32_t CHINESE_CALENDAR_LIMITS[UCAL_FIELD_COUNT][4] = { 127 // clang-format off 128 // Minimum Greatest Least Maximum 129 // Minimum Maximum 130 { 1, 1, 83333, 83333}, // ERA 131 { 1, 1, 60, 60}, // YEAR 132 { 0, 0, 11, 11}, // MONTH 133 { 1, 1, 50, 55}, // WEEK_OF_YEAR 134 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH 135 { 1, 1, 29, 30}, // DAY_OF_MONTH 136 { 1, 1, 353, 385}, // DAY_OF_YEAR 137 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK 138 { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH 139 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM 140 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR 141 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY 142 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE 143 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND 144 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND 145 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET 146 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET 147 { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY 148 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL 149 { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR 150 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY 151 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY 152 { 0, 0, 1, 1}, // IS_LEAP_MONTH 153 { 0, 0, 11, 12}, // ORDINAL_MONTH 154 // clang-format on 155 }; 156 157 int32_t ICU4XChineseBasedCalendar::handleGetLimit(UCalendarDateFields field, 158 ELimitType limitType) const { 159 return CHINESE_CALENDAR_LIMITS[field][limitType]; 160 } 161 162 // Field resolution table copied from i18n/chnsecal.cpp. Licensed under: 163 // 164 // © 2016 and later: Unicode, Inc. and others. 165 // License & terms of use: http://www.unicode.org/copyright.html 166 const icu::UFieldResolutionTable 167 ICU4XChineseBasedCalendar::CHINESE_DATE_PRECEDENCE[] = { 168 // clang-format off 169 { 170 { UCAL_DAY_OF_MONTH, kResolveSTOP }, 171 { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP }, 172 { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 173 { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 174 { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP }, 175 { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 176 { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 177 { UCAL_DAY_OF_YEAR, kResolveSTOP }, 178 { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_IS_LEAP_MONTH, kResolveSTOP }, 179 { kResolveSTOP } 180 }, 181 { 182 { UCAL_WEEK_OF_YEAR, kResolveSTOP }, 183 { UCAL_WEEK_OF_MONTH, kResolveSTOP }, 184 { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP }, 185 { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 186 { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 187 { kResolveSTOP } 188 }, 189 {{kResolveSTOP}} 190 // clang-format on 191 }; 192 193 const icu::UFieldResolutionTable* 194 ICU4XChineseBasedCalendar::getFieldResolutionTable() const { 195 return CHINESE_DATE_PRECEDENCE; 196 } 197 198 } // namespace mozilla::intl::calendar