Bug 1954138 - Part 3: Add ICU4X-based Chinese and Dangun calendars. r=dminor,sylvestre
Add ICU4X based calendars for Chinese and Dangun to ensure consistent behaviour across `Intl.DateTimeFormat` and `Temporal`. This requires using the ICU4C C++ API, so it's not possible to use when `MOZ_SYSTEM_ICU` is defined. And ICU4X's calendar FFI needs to be available, which is currently guarded by `JS_HAS_TEMPORAL_API`. Differential Revision: https://phabricator.services.mozilla.com/D241645
This commit is contained in:
@@ -77,6 +77,9 @@ LOCAL_INCLUDES += [
|
||||
"/intl/icu_capi/bindings/c",
|
||||
]
|
||||
|
||||
if not CONFIG["MOZ_SYSTEM_ICU"]:
|
||||
DIRS += ["src/calendar"]
|
||||
|
||||
# At the time of this writing the MOZ_HAS_MOZGLUE define must be true in order to
|
||||
# correctly include ConvertUtf8toUtf16 in certain include paths, otherwise it results
|
||||
# in a compile time "undeclared identifier" error. See:
|
||||
|
||||
637
intl/components/src/calendar/ICU4XCalendar.cpp
Normal file
637
intl/components/src/calendar/ICU4XCalendar.cpp
Normal file
@@ -0,0 +1,637 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/intl/calendar/ICU4XCalendar.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/TextUtils.h"
|
||||
#include "mozilla/intl/ICU4XGeckoDataProvider.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <stdint.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include "unicode/timezone.h"
|
||||
|
||||
#include "diplomat_runtime.h"
|
||||
#include "ICU4XDataProvider.h"
|
||||
#include "ICU4XError.h"
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
// Copied from js/src/util/Text.h
|
||||
template <typename CharT>
|
||||
static constexpr uint8_t AsciiDigitToNumber(CharT c) {
|
||||
using UnsignedCharT = std::make_unsigned_t<CharT>;
|
||||
auto uc = static_cast<UnsignedCharT>(c);
|
||||
return uc - '0';
|
||||
}
|
||||
|
||||
static UniqueICU4XCalendar CreateICU4XCalendar(
|
||||
capi::ICU4XAnyCalendarKind kind) {
|
||||
auto result = capi::ICU4XCalendar_create_for_kind(GetDataProvider(), kind);
|
||||
if (!result.is_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
return UniqueICU4XCalendar{result.ok};
|
||||
}
|
||||
|
||||
static UniqueICU4XDate CreateICU4XDate(const ISODate& date,
|
||||
const capi::ICU4XCalendar* calendar) {
|
||||
auto result = capi::ICU4XDate_create_from_iso_in_calendar(
|
||||
date.year, date.month, date.day, calendar);
|
||||
if (!result.is_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
return UniqueICU4XDate{result.ok};
|
||||
}
|
||||
|
||||
static UniqueICU4XDate CreateDateFromCodes(const capi::ICU4XCalendar* calendar,
|
||||
std::string_view era,
|
||||
int32_t eraYear, MonthCode monthCode,
|
||||
int32_t day) {
|
||||
auto monthCodeView = std::string_view{monthCode};
|
||||
auto date = capi::ICU4XDate_create_from_codes_in_calendar(
|
||||
era.data(), era.length(), eraYear, monthCodeView.data(),
|
||||
monthCodeView.length(), day, calendar);
|
||||
if (date.is_ok) {
|
||||
return UniqueICU4XDate{date.ok};
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Copied from js/src/builtin/temporal/Calendar.cpp
|
||||
static UniqueICU4XDate CreateDateFrom(const capi::ICU4XCalendar* calendar,
|
||||
std::string_view era, int32_t eraYear,
|
||||
int32_t month, int32_t day) {
|
||||
MOZ_ASSERT(1 <= month && month <= 13);
|
||||
|
||||
// Create date with month number replaced by month-code.
|
||||
auto monthCode = MonthCode{std::min(month, 12)};
|
||||
auto date = CreateDateFromCodes(calendar, era, eraYear, monthCode, day);
|
||||
if (!date) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If the ordinal month of |date| matches the input month, no additional
|
||||
// changes are necessary and we can directly return |date|.
|
||||
int32_t ordinal = capi::ICU4XDate_ordinal_month(date.get());
|
||||
if (ordinal == month) {
|
||||
return date;
|
||||
}
|
||||
|
||||
// Otherwise we need to handle three cases:
|
||||
// 1. The input year contains a leap month and we need to adjust the
|
||||
// month-code.
|
||||
// 2. The thirteenth month of a year without leap months was requested.
|
||||
// 3. The thirteenth month of a year with leap months was requested.
|
||||
if (ordinal > month) {
|
||||
MOZ_ASSERT(1 < month && month <= 12);
|
||||
|
||||
// This case can only happen in leap years.
|
||||
MOZ_ASSERT(capi::ICU4XDate_months_in_year(date.get()) == 13);
|
||||
|
||||
// Leap months can occur after any month in the Chinese calendar.
|
||||
//
|
||||
// Example when the fourth month is a leap month between M03 and M04.
|
||||
//
|
||||
// Month code: M01 M02 M03 M03L M04 M05 M06 ...
|
||||
// Ordinal month: 1 2 3 4 5 6 7
|
||||
|
||||
// The month can be off by exactly one.
|
||||
MOZ_ASSERT((ordinal - month) == 1);
|
||||
|
||||
// First try the case when the previous month isn't a leap month. This
|
||||
// case can only occur when |month > 2|, because otherwise we know that
|
||||
// "M01L" is the correct answer.
|
||||
if (month > 2) {
|
||||
auto previousMonthCode = MonthCode{month - 1};
|
||||
date =
|
||||
CreateDateFromCodes(calendar, era, eraYear, previousMonthCode, day);
|
||||
if (!date) {
|
||||
return nullptr;
|
||||
}
|
||||
int32_t ordinal = capi::ICU4XDate_ordinal_month(date.get());
|
||||
if (ordinal == month) {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall-through when the previous month is a leap month.
|
||||
} else {
|
||||
MOZ_ASSERT(month == 13);
|
||||
MOZ_ASSERT(ordinal == 12);
|
||||
|
||||
// Years with leap months contain thirteen months.
|
||||
if (capi::ICU4XDate_months_in_year(date.get()) != 13) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Fall-through to return leap month "M12L" at the end of the year.
|
||||
}
|
||||
|
||||
// Finally handle the case when the previous month is a leap month.
|
||||
auto leapMonthCode = MonthCode{month - 1, /* isLeapMonth= */ true};
|
||||
return CreateDateFromCodes(calendar, era, eraYear, leapMonthCode, day);
|
||||
}
|
||||
|
||||
static ISODate ToISODate(const capi::ICU4XDate* date) {
|
||||
UniqueICU4XIsoDate isoDate{capi::ICU4XDate_to_iso(date)};
|
||||
|
||||
int32_t isoYear = capi::ICU4XIsoDate_year(isoDate.get());
|
||||
int32_t isoMonth = capi::ICU4XIsoDate_month(isoDate.get());
|
||||
int32_t isoDay = capi::ICU4XIsoDate_day_of_month(isoDate.get());
|
||||
|
||||
return {isoYear, isoMonth, isoDay};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ICU4XCalendar::ICU4XCalendar(capi::ICU4XAnyCalendarKind kind,
|
||||
const icu::Locale& locale, UErrorCode& success)
|
||||
: icu::Calendar(icu::TimeZone::forLocaleOrDefault(locale), locale, success),
|
||||
kind_(kind) {}
|
||||
|
||||
ICU4XCalendar::ICU4XCalendar(capi::ICU4XAnyCalendarKind kind,
|
||||
const icu::TimeZone& timeZone,
|
||||
const icu::Locale& locale, UErrorCode& success)
|
||||
: icu::Calendar(timeZone, locale, success), kind_(kind) {}
|
||||
|
||||
ICU4XCalendar::ICU4XCalendar(const ICU4XCalendar& other)
|
||||
: icu::Calendar(other), kind_(other.kind_) {}
|
||||
|
||||
ICU4XCalendar::~ICU4XCalendar() = default;
|
||||
|
||||
/**
|
||||
* Get or create the underlying ICU4X calendar.
|
||||
*/
|
||||
capi::ICU4XCalendar* ICU4XCalendar::getICU4XCalendar(UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!calendar_) {
|
||||
auto result = CreateICU4XCalendar(kind_);
|
||||
if (!result) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
calendar_ = std::move(result);
|
||||
}
|
||||
return calendar_.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create the fallback ICU4C calendar. Used for dates outside the range
|
||||
* supported by ICU4X.
|
||||
*/
|
||||
icu::Calendar* ICU4XCalendar::getFallbackCalendar(UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!fallback_) {
|
||||
icu::Locale locale = getLocale(ULOC_ACTUAL_LOCALE, status);
|
||||
locale.setKeywordValue("calendar", getType(), status);
|
||||
fallback_.reset(
|
||||
icu::Calendar::createInstance(getTimeZone(), locale, status));
|
||||
}
|
||||
return fallback_.get();
|
||||
}
|
||||
|
||||
UniqueICU4XDate ICU4XCalendar::createICU4XDate(const ISODate& date,
|
||||
UErrorCode& status) const {
|
||||
MOZ_ASSERT(U_SUCCESS(status));
|
||||
|
||||
auto* calendar = getICU4XCalendar(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto dt = CreateICU4XDate(date, calendar);
|
||||
if (!dt) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
}
|
||||
return dt;
|
||||
}
|
||||
|
||||
UniqueICU4XDate ICU4XCalendar::createICU4XDate(const CalendarDate& date,
|
||||
UErrorCode& status) const {
|
||||
MOZ_ASSERT(U_SUCCESS(status));
|
||||
|
||||
auto* calendar = getICU4XCalendar(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto era = eraName(date.year);
|
||||
|
||||
auto dt =
|
||||
CreateDateFromCodes(calendar, era, date.year, date.monthCode, date.day);
|
||||
if (!dt) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
}
|
||||
return dt;
|
||||
}
|
||||
|
||||
MonthCode ICU4XCalendar::monthCodeFrom(const capi::ICU4XDate* date,
|
||||
UErrorCode& status) {
|
||||
MOZ_ASSERT(U_SUCCESS(status));
|
||||
|
||||
// Storage for the largest valid month code and the terminating NUL-character.
|
||||
char buf[4 + 1] = {};
|
||||
auto writable = capi::diplomat_simple_writeable(buf, std::size(buf));
|
||||
|
||||
if (!capi::ICU4XDate_month_code(date, &writable).is_ok) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto view = std::string_view{writable.buf, writable.len};
|
||||
|
||||
MOZ_ASSERT(view.length() >= 3);
|
||||
MOZ_ASSERT(view[0] == 'M');
|
||||
MOZ_ASSERT(mozilla::IsAsciiDigit(view[1]));
|
||||
MOZ_ASSERT(mozilla::IsAsciiDigit(view[2]));
|
||||
MOZ_ASSERT_IF(view.length() > 3, view[3] == 'L');
|
||||
|
||||
int32_t ordinal =
|
||||
AsciiDigitToNumber(view[1]) * 10 + AsciiDigitToNumber(view[2]);
|
||||
bool isLeapMonth = view.length() > 3;
|
||||
|
||||
return MonthCode{ordinal, isLeapMonth};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// icu::Calendar implementation overrides //
|
||||
////////////////////////////////////////////
|
||||
|
||||
const char* ICU4XCalendar::getTemporalMonthCode(UErrorCode& status) const {
|
||||
int32_t month = get(UCAL_MONTH, status);
|
||||
int32_t isLeapMonth = get(UCAL_IS_LEAP_MONTH, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char* MonthCodes[] = {
|
||||
// Non-leap months.
|
||||
"M01",
|
||||
"M02",
|
||||
"M03",
|
||||
"M04",
|
||||
"M05",
|
||||
"M06",
|
||||
"M07",
|
||||
"M08",
|
||||
"M09",
|
||||
"M10",
|
||||
"M11",
|
||||
"M12",
|
||||
"M13",
|
||||
|
||||
// Leap months. (Note: There's no thirteenth leap month.)
|
||||
"M01L",
|
||||
"M02L",
|
||||
"M03L",
|
||||
"M04L",
|
||||
"M05L",
|
||||
"M06L",
|
||||
"M07L",
|
||||
"M08L",
|
||||
"M09L",
|
||||
"M10L",
|
||||
"M11L",
|
||||
"M12L",
|
||||
};
|
||||
|
||||
size_t index = month + (isLeapMonth ? 12 : 0);
|
||||
if (index >= std::size(MonthCodes)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
return MonthCodes[index];
|
||||
}
|
||||
|
||||
void ICU4XCalendar::setTemporalMonthCode(const char* code, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t len = std::strlen(code);
|
||||
if (len < 3 || len > 4 || code[0] != 'M' || !IsAsciiDigit(code[1]) ||
|
||||
!IsAsciiDigit(code[2]) || (len == 4 && code[3] != 'L')) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t month =
|
||||
AsciiDigitToNumber(code[1]) * 10 + AsciiDigitToNumber(code[2]);
|
||||
bool isLeapMonth = len == 4;
|
||||
|
||||
if (month < 1 || month > 13 || (month == 13 && isLeapMonth)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this calendar supports the requested month code.
|
||||
auto monthCode = MonthCode{month, isLeapMonth};
|
||||
if (!hasMonthCode(monthCode)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
set(UCAL_MONTH, monthCode.ordinal() - 1);
|
||||
set(UCAL_IS_LEAP_MONTH, int32_t(monthCode.isLeapMonth()));
|
||||
}
|
||||
|
||||
int32_t ICU4XCalendar::internalGetMonth(int32_t defaultValue,
|
||||
UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
|
||||
return internalGet(UCAL_MONTH, defaultValue);
|
||||
}
|
||||
if (!hasLeapMonths()) {
|
||||
return internalGet(UCAL_ORDINAL_MONTH);
|
||||
}
|
||||
return internalGetMonth(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current month, possibly by computing it from |UCAL_ORDINAL_MONTH|.
|
||||
*/
|
||||
int32_t ICU4XCalendar::internalGetMonth(UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
|
||||
return internalGet(UCAL_MONTH);
|
||||
}
|
||||
if (!hasLeapMonths()) {
|
||||
return internalGet(UCAL_ORDINAL_MONTH);
|
||||
}
|
||||
|
||||
int32_t extendedYear = internalGet(UCAL_EXTENDED_YEAR);
|
||||
int32_t ordinalMonth = internalGet(UCAL_ORDINAL_MONTH);
|
||||
|
||||
int32_t month;
|
||||
int32_t isLeapMonth;
|
||||
if (requiresFallbackForExtendedYear(extendedYear)) {
|
||||
// Use the fallback calendar for years outside the range supported by ICU4X.
|
||||
auto* fallback = getFallbackCalendar(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
fallback->clear();
|
||||
fallback->set(UCAL_EXTENDED_YEAR, extendedYear);
|
||||
fallback->set(UCAL_ORDINAL_MONTH, ordinalMonth);
|
||||
fallback->set(UCAL_DAY_OF_MONTH, 1);
|
||||
|
||||
month = fallback->get(UCAL_MONTH, status);
|
||||
isLeapMonth = fallback->get(UCAL_IS_LEAP_MONTH, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
auto* cal = getICU4XCalendar(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
UniqueICU4XDate date = CreateDateFrom(cal, eraName(extendedYear),
|
||||
extendedYear, ordinalMonth + 1, 1);
|
||||
if (!date) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
MonthCode monthCode = monthCodeFrom(date.get(), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
month = monthCode.ordinal() - 1;
|
||||
isLeapMonth = monthCode.isLeapMonth();
|
||||
}
|
||||
|
||||
auto* nonConstThis = const_cast<ICU4XCalendar*>(this);
|
||||
nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, isLeapMonth);
|
||||
nonConstThis->internalSet(UCAL_MONTH, month);
|
||||
|
||||
return month;
|
||||
}
|
||||
|
||||
void ICU4XCalendar::add(UCalendarDateFields field, int32_t amount,
|
||||
UErrorCode& status) {
|
||||
switch (field) {
|
||||
case UCAL_MONTH:
|
||||
case UCAL_ORDINAL_MONTH:
|
||||
if (amount != 0) {
|
||||
// Our implementation doesn't yet support this action.
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Calendar::add(field, amount, status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ICU4XCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) {
|
||||
add(static_cast<UCalendarDateFields>(field), amount, status);
|
||||
}
|
||||
|
||||
void ICU4XCalendar::roll(UCalendarDateFields field, int32_t amount,
|
||||
UErrorCode& status) {
|
||||
switch (field) {
|
||||
case UCAL_MONTH:
|
||||
case UCAL_ORDINAL_MONTH:
|
||||
if (amount != 0) {
|
||||
// Our implementation doesn't yet support this action.
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Calendar::roll(field, amount, status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ICU4XCalendar::roll(EDateFields field, int32_t amount,
|
||||
UErrorCode& status) {
|
||||
roll(static_cast<UCalendarDateFields>(field), amount, status);
|
||||
}
|
||||
|
||||
int32_t ICU4XCalendar::handleGetExtendedYear(UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
|
||||
return internalGet(UCAL_EXTENDED_YEAR, 1);
|
||||
}
|
||||
|
||||
// We don't yet support the case when UCAL_YEAR is newer.
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t ICU4XCalendar::handleGetYearLength(int32_t extendedYear,
|
||||
UErrorCode& status) const {
|
||||
// Use the (slower) default implementation for years outside the range
|
||||
// supported by ICU4X.
|
||||
if (requiresFallbackForExtendedYear(extendedYear)) {
|
||||
return icu::Calendar::handleGetYearLength(extendedYear, status);
|
||||
}
|
||||
|
||||
auto* cal = getICU4XCalendar(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
UniqueICU4XDate date =
|
||||
CreateDateFrom(cal, eraName(extendedYear), extendedYear, 1, 1);
|
||||
if (!date) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return 0;
|
||||
}
|
||||
return capi::ICU4XDate_days_in_year(date.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of days in a month.
|
||||
*/
|
||||
int32_t ICU4XCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
|
||||
UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ICU4C supports wrap around. We don't support this case.
|
||||
if (month < 0 || month > 11) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Use the fallback calendar for years outside the range supported by ICU4X.
|
||||
if (requiresFallbackForExtendedYear(extendedYear)) {
|
||||
auto* fallback = getFallbackCalendar(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
fallback->clear();
|
||||
fallback->set(UCAL_EXTENDED_YEAR, extendedYear);
|
||||
fallback->set(UCAL_MONTH, month);
|
||||
fallback->set(UCAL_DAY_OF_MONTH, 1);
|
||||
|
||||
return fallback->getActualMaximum(UCAL_DAY_OF_MONTH, status);
|
||||
}
|
||||
|
||||
auto* cal = getICU4XCalendar(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) != 0;
|
||||
auto monthCode = MonthCode{month + 1, isLeapMonth};
|
||||
UniqueICU4XDate date = CreateDateFromCodes(cal, eraName(extendedYear),
|
||||
extendedYear, monthCode, 1);
|
||||
if (!date) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return capi::ICU4XDate_days_in_month(date.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the start of the month as a Julian date.
|
||||
*/
|
||||
int64_t ICU4XCalendar::handleComputeMonthStart(int32_t extendedYear,
|
||||
int32_t month, UBool useMonth,
|
||||
UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ICU4C supports wrap around. We don't support this case.
|
||||
if (month < 0 || month > 11) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Use the fallback calendar for years outside the range supported by ICU4X.
|
||||
if (requiresFallbackForExtendedYear(extendedYear)) {
|
||||
auto* fallback = getFallbackCalendar(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
fallback->clear();
|
||||
fallback->set(UCAL_EXTENDED_YEAR, extendedYear);
|
||||
if (useMonth) {
|
||||
fallback->set(UCAL_MONTH, month);
|
||||
fallback->set(UCAL_IS_LEAP_MONTH, internalGet(UCAL_IS_LEAP_MONTH));
|
||||
} else {
|
||||
fallback->set(UCAL_ORDINAL_MONTH, month);
|
||||
}
|
||||
fallback->set(UCAL_DAY_OF_MONTH, 1);
|
||||
|
||||
int32_t newMoon = fallback->get(UCAL_JULIAN_DAY, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
return newMoon - 1;
|
||||
}
|
||||
|
||||
auto* cal = getICU4XCalendar(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
UniqueICU4XDate date{};
|
||||
if (useMonth) {
|
||||
bool isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) != 0;
|
||||
auto monthCode = MonthCode{month + 1, isLeapMonth};
|
||||
date = CreateDateFromCodes(cal, eraName(extendedYear), extendedYear,
|
||||
monthCode, 1);
|
||||
} else {
|
||||
date =
|
||||
CreateDateFrom(cal, eraName(extendedYear), extendedYear, month + 1, 1);
|
||||
}
|
||||
if (!date) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto isoDate = ToISODate(date.get());
|
||||
int32_t newMoon = MakeDay(isoDate);
|
||||
|
||||
return (newMoon - 1) + kEpochStartAsJulianDay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of handleComputeFields when using the fallback
|
||||
* calendar.
|
||||
*/
|
||||
void ICU4XCalendar::handleComputeFieldsFromFallback(int32_t julianDay,
|
||||
UErrorCode& status) {
|
||||
auto* fallback = getFallbackCalendar(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
fallback->clear();
|
||||
fallback->set(UCAL_JULIAN_DAY, julianDay);
|
||||
|
||||
internalSet(UCAL_ERA, fallback->get(UCAL_ERA, status));
|
||||
internalSet(UCAL_YEAR, fallback->get(UCAL_YEAR, status));
|
||||
internalSet(UCAL_EXTENDED_YEAR, fallback->get(UCAL_EXTENDED_YEAR, status));
|
||||
internalSet(UCAL_MONTH, fallback->get(UCAL_MONTH, status));
|
||||
internalSet(UCAL_ORDINAL_MONTH, fallback->get(UCAL_ORDINAL_MONTH, status));
|
||||
internalSet(UCAL_IS_LEAP_MONTH, fallback->get(UCAL_IS_LEAP_MONTH, status));
|
||||
internalSet(UCAL_DAY_OF_MONTH, fallback->get(UCAL_DAY_OF_MONTH, status));
|
||||
internalSet(UCAL_DAY_OF_YEAR, fallback->get(UCAL_DAY_OF_YEAR, status));
|
||||
}
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
168
intl/components/src/calendar/ICU4XCalendar.h
Normal file
168
intl/components/src/calendar/ICU4XCalendar.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef intl_components_calendar_ICU4XCalendar_h_
|
||||
#define intl_components_calendar_ICU4XCalendar_h_
|
||||
|
||||
#include "mozilla/intl/calendar/ICU4XUniquePtr.h"
|
||||
#include "mozilla/intl/calendar/ISODate.h"
|
||||
#include "mozilla/intl/calendar/MonthCode.h"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdint.h>
|
||||
#include <string_view>
|
||||
|
||||
#include "unicode/calendar.h"
|
||||
#include "unicode/locid.h"
|
||||
#include "unicode/timezone.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#include "ICU4XAnyCalendarKind.h"
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
/**
|
||||
* Abstract class to implement icu::Calendar using ICU4X.
|
||||
*/
|
||||
class ICU4XCalendar : public icu::Calendar {
|
||||
mutable UniqueICU4XCalendar calendar_{};
|
||||
mutable std::unique_ptr<icu::Calendar> fallback_{};
|
||||
capi::ICU4XAnyCalendarKind kind_;
|
||||
|
||||
protected:
|
||||
ICU4XCalendar(capi::ICU4XAnyCalendarKind kind, const icu::Locale& locale,
|
||||
UErrorCode& success);
|
||||
ICU4XCalendar(capi::ICU4XAnyCalendarKind kind, const icu::TimeZone& timeZone,
|
||||
const icu::Locale& locale, UErrorCode& success);
|
||||
ICU4XCalendar(const ICU4XCalendar& other);
|
||||
|
||||
/**
|
||||
* Get or create the underlying ICU4X calendar.
|
||||
*/
|
||||
capi::ICU4XCalendar* getICU4XCalendar(UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Get or create the ICU4C fallback calendar implementation.
|
||||
*/
|
||||
icu::Calendar* getFallbackCalendar(UErrorCode& status) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Return the ICU4X era name for the given extended year.
|
||||
*/
|
||||
virtual std::string_view eraName(int32_t extendedYear) const = 0;
|
||||
|
||||
/**
|
||||
* Return true if this calendar contains any leap months.
|
||||
*/
|
||||
virtual bool hasLeapMonths() const = 0;
|
||||
|
||||
/**
|
||||
* Return true if this calendar contains the requested month code.
|
||||
*/
|
||||
virtual bool hasMonthCode(MonthCode monthCode) const = 0;
|
||||
|
||||
/**
|
||||
* Subclasses can request to use the ICU4C fallback calendar.
|
||||
*
|
||||
* Can be removed when <https://github.com/unicode-org/icu4x/issues/4917> is
|
||||
* fixed.
|
||||
*/
|
||||
virtual bool requiresFallbackForExtendedYear(int32_t year) const = 0;
|
||||
virtual bool requiresFallbackForGregorianYear(int32_t year) const = 0;
|
||||
|
||||
protected:
|
||||
static constexpr int32_t kEpochStartAsJulianDay =
|
||||
2440588; // January 1, 1970 (Gregorian)
|
||||
|
||||
/**
|
||||
* Return the month code of |date|.
|
||||
*/
|
||||
static MonthCode monthCodeFrom(const capi::ICU4XDate* date,
|
||||
UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Create a new ICU4X date object from an ISO date.
|
||||
*/
|
||||
UniqueICU4XDate createICU4XDate(const ISODate& date,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Create a new ICU4X date object from a calendar date.
|
||||
*/
|
||||
UniqueICU4XDate createICU4XDate(const CalendarDate& date,
|
||||
UErrorCode& status) const;
|
||||
|
||||
public:
|
||||
ICU4XCalendar() = delete;
|
||||
virtual ~ICU4XCalendar();
|
||||
|
||||
const char* getTemporalMonthCode(UErrorCode& status) const override;
|
||||
void setTemporalMonthCode(const char* code, UErrorCode& status) override;
|
||||
|
||||
void add(UCalendarDateFields field, int32_t amount,
|
||||
UErrorCode& status) override;
|
||||
void add(EDateFields field, int32_t amount, UErrorCode& status) override;
|
||||
void roll(UCalendarDateFields field, int32_t amount,
|
||||
UErrorCode& status) override;
|
||||
void roll(EDateFields field, int32_t amount, UErrorCode& status) override;
|
||||
|
||||
protected:
|
||||
int32_t internalGetMonth(int32_t defaultValue,
|
||||
UErrorCode& status) const override;
|
||||
int32_t internalGetMonth(UErrorCode& status) const override;
|
||||
|
||||
int64_t handleComputeMonthStart(int32_t extendedYear, int32_t month,
|
||||
UBool useMonth,
|
||||
UErrorCode& status) const override;
|
||||
int32_t handleGetMonthLength(int32_t extendedYear, int32_t month,
|
||||
UErrorCode& status) const override;
|
||||
int32_t handleGetYearLength(int32_t extendedYear,
|
||||
UErrorCode& status) const override;
|
||||
|
||||
int32_t handleGetExtendedYear(UErrorCode& status) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* handleComputeFields implementation using the ICU4C fallback calendar.
|
||||
*/
|
||||
void handleComputeFieldsFromFallback(int32_t julianDay, UErrorCode& status);
|
||||
};
|
||||
|
||||
/**
|
||||
* `IMPL_SYSTEM_DEFAULT_CENTURY` is internal to "i18n/gregoimp.h", so we have
|
||||
* to provider our own helper class to implement default centuries.
|
||||
*/
|
||||
template <class Calendar, class Locale>
|
||||
class SystemDefaultCentury {
|
||||
mutable UDate start_ = DBL_MIN;
|
||||
mutable int32_t startYear_ = -1;
|
||||
mutable std::once_flag init_{};
|
||||
|
||||
void initialize() const {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Calendar calendar(Locale::identifier, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
calendar.setTime(icu::Calendar::getNow(), status);
|
||||
calendar.add(UCAL_EXTENDED_YEAR, -80, status);
|
||||
start_ = calendar.getTime(status);
|
||||
startYear_ = calendar.get(UCAL_YEAR, status);
|
||||
}
|
||||
|
||||
public:
|
||||
UDate start() const {
|
||||
std::call_once(init_, [this] { initialize(); });
|
||||
return start_;
|
||||
}
|
||||
int32_t startYear() const {
|
||||
std::call_once(init_, [this] { initialize(); });
|
||||
return startYear_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
|
||||
#endif
|
||||
204
intl/components/src/calendar/ICU4XChineseBasedCalendar.cpp
Normal file
204
intl/components/src/calendar/ICU4XChineseBasedCalendar.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/intl/calendar/ICU4XChineseBasedCalendar.h"
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
ICU4XChineseBasedCalendar::ICU4XChineseBasedCalendar(
|
||||
capi::ICU4XAnyCalendarKind kind, const icu::Locale& locale,
|
||||
UErrorCode& success)
|
||||
: ICU4XCalendar(kind, locale, success) {}
|
||||
|
||||
ICU4XChineseBasedCalendar::ICU4XChineseBasedCalendar(
|
||||
capi::ICU4XAnyCalendarKind kind, const icu::TimeZone& timeZone,
|
||||
const icu::Locale& locale, UErrorCode& success)
|
||||
: ICU4XCalendar(kind, timeZone, locale, success) {}
|
||||
|
||||
ICU4XChineseBasedCalendar::ICU4XChineseBasedCalendar(
|
||||
const ICU4XChineseBasedCalendar& other)
|
||||
: ICU4XCalendar(other) {}
|
||||
|
||||
ICU4XChineseBasedCalendar::~ICU4XChineseBasedCalendar() = default;
|
||||
|
||||
////////////////////////////////////////////
|
||||
// ICU4XCalendar implementation overrides //
|
||||
////////////////////////////////////////////
|
||||
|
||||
bool ICU4XChineseBasedCalendar::hasLeapMonths() const { return true; }
|
||||
|
||||
bool ICU4XChineseBasedCalendar::hasMonthCode(MonthCode monthCode) const {
|
||||
return monthCode.ordinal() <= 12;
|
||||
}
|
||||
|
||||
bool ICU4XChineseBasedCalendar::requiresFallbackForExtendedYear(
|
||||
int32_t year) const {
|
||||
// Same limits as in js/src/builtin/temporal/Calendar.cpp.
|
||||
return std::abs(year) > 10'000;
|
||||
}
|
||||
|
||||
bool ICU4XChineseBasedCalendar::requiresFallbackForGregorianYear(
|
||||
int32_t year) const {
|
||||
// Same limits as in js/src/builtin/temporal/Calendar.cpp.
|
||||
return std::abs(year) > 10'000;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// icu::Calendar implementation overrides //
|
||||
////////////////////////////////////////////
|
||||
|
||||
bool ICU4XChineseBasedCalendar::inTemporalLeapYear(UErrorCode& status) const {
|
||||
int32_t days = getActualMaximum(UCAL_DAY_OF_YEAR, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr int32_t maxDaysInMonth = 30;
|
||||
constexpr int32_t monthsInNonLeapYear = 12;
|
||||
return days > (monthsInNonLeapYear * maxDaysInMonth);
|
||||
}
|
||||
|
||||
int32_t ICU4XChineseBasedCalendar::getRelatedYear(UErrorCode& status) const {
|
||||
int32_t year = get(UCAL_EXTENDED_YEAR, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
return year + relatedYearDifference();
|
||||
}
|
||||
|
||||
void ICU4XChineseBasedCalendar::setRelatedYear(int32_t year) {
|
||||
set(UCAL_EXTENDED_YEAR, year - relatedYearDifference());
|
||||
}
|
||||
|
||||
void ICU4XChineseBasedCalendar::handleComputeFields(int32_t julianDay,
|
||||
UErrorCode& status) {
|
||||
int32_t gyear = getGregorianYear();
|
||||
|
||||
// Use the fallback calendar for years outside the range supported by ICU4X.
|
||||
if (requiresFallbackForGregorianYear(gyear)) {
|
||||
handleComputeFieldsFromFallback(julianDay, status);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t gmonth = getGregorianMonth() + 1;
|
||||
int32_t gday = getGregorianDayOfMonth();
|
||||
|
||||
MOZ_ASSERT(1 <= gmonth && gmonth <= 12);
|
||||
MOZ_ASSERT(1 <= gday && gday <= 31);
|
||||
|
||||
auto date = createICU4XDate(ISODate{gyear, gmonth, gday}, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(date);
|
||||
|
||||
MonthCode monthCode = monthCodeFrom(date.get(), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t extendedYear = capi::ICU4XDate_year_in_era(date.get());
|
||||
int32_t month = capi::ICU4XDate_ordinal_month(date.get());
|
||||
int32_t dayOfMonth = capi::ICU4XDate_day_of_month(date.get());
|
||||
int32_t dayOfYear = capi::ICU4XDate_day_of_year(date.get());
|
||||
|
||||
MOZ_ASSERT(1 <= month && month <= 13);
|
||||
MOZ_ASSERT(1 <= dayOfMonth && dayOfMonth <= 30);
|
||||
MOZ_ASSERT(1 <= dayOfYear && dayOfYear <= (13 * 30));
|
||||
|
||||
// Compute the cycle and year of cycle relative to the Chinese calendar, even
|
||||
// when this is the Dangi calendar.
|
||||
int32_t chineseExtendedYear =
|
||||
extendedYear + relatedYearDifference() - chineseRelatedYearDiff;
|
||||
int32_t cycle_year = chineseExtendedYear - 1;
|
||||
int32_t cycle = FloorDiv(cycle_year, 60);
|
||||
int32_t yearOfCycle = cycle_year - (cycle * 60);
|
||||
|
||||
internalSet(UCAL_ERA, cycle + 1);
|
||||
internalSet(UCAL_YEAR, yearOfCycle + 1);
|
||||
internalSet(UCAL_EXTENDED_YEAR, extendedYear);
|
||||
internalSet(UCAL_MONTH, monthCode.ordinal() - 1);
|
||||
internalSet(UCAL_ORDINAL_MONTH, month - 1);
|
||||
internalSet(UCAL_IS_LEAP_MONTH, monthCode.isLeapMonth() ? 1 : 0);
|
||||
internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
|
||||
internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
|
||||
}
|
||||
|
||||
// Limits table copied from i18n/chnsecal.cpp. Licensed under:
|
||||
//
|
||||
// © 2016 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
static const int32_t CHINESE_CALENDAR_LIMITS[UCAL_FIELD_COUNT][4] = {
|
||||
// clang-format off
|
||||
// Minimum Greatest Least Maximum
|
||||
// Minimum Maximum
|
||||
{ 1, 1, 83333, 83333}, // ERA
|
||||
{ 1, 1, 60, 60}, // YEAR
|
||||
{ 0, 0, 11, 11}, // MONTH
|
||||
{ 1, 1, 50, 55}, // WEEK_OF_YEAR
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
|
||||
{ 1, 1, 29, 30}, // DAY_OF_MONTH
|
||||
{ 1, 1, 353, 385}, // DAY_OF_YEAR
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
|
||||
{ -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
|
||||
{ -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
|
||||
{ -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
|
||||
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
|
||||
{ 0, 0, 1, 1}, // IS_LEAP_MONTH
|
||||
{ 0, 0, 11, 12}, // ORDINAL_MONTH
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
int32_t ICU4XChineseBasedCalendar::handleGetLimit(UCalendarDateFields field,
|
||||
ELimitType limitType) const {
|
||||
return CHINESE_CALENDAR_LIMITS[field][limitType];
|
||||
}
|
||||
|
||||
// Field resolution table copied from i18n/chnsecal.cpp. Licensed under:
|
||||
//
|
||||
// © 2016 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
const icu::UFieldResolutionTable
|
||||
ICU4XChineseBasedCalendar::CHINESE_DATE_PRECEDENCE[] = {
|
||||
// clang-format off
|
||||
{
|
||||
{ UCAL_DAY_OF_MONTH, kResolveSTOP },
|
||||
{ UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP },
|
||||
{ UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
|
||||
{ UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
|
||||
{ UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP },
|
||||
{ UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
|
||||
{ UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
|
||||
{ UCAL_DAY_OF_YEAR, kResolveSTOP },
|
||||
{ kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_IS_LEAP_MONTH, kResolveSTOP },
|
||||
{ kResolveSTOP }
|
||||
},
|
||||
{
|
||||
{ UCAL_WEEK_OF_YEAR, kResolveSTOP },
|
||||
{ UCAL_WEEK_OF_MONTH, kResolveSTOP },
|
||||
{ UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP },
|
||||
{ kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
|
||||
{ kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
|
||||
{ kResolveSTOP }
|
||||
},
|
||||
{{kResolveSTOP}}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
const icu::UFieldResolutionTable*
|
||||
ICU4XChineseBasedCalendar::getFieldResolutionTable() const {
|
||||
return CHINESE_DATE_PRECEDENCE;
|
||||
}
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
63
intl/components/src/calendar/ICU4XChineseBasedCalendar.h
Normal file
63
intl/components/src/calendar/ICU4XChineseBasedCalendar.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef intl_components_calendar_ICU4XChineseBasedCalendar_h_
|
||||
#define intl_components_calendar_ICU4XChineseBasedCalendar_h_
|
||||
|
||||
#include "mozilla/intl/calendar/ICU4XCalendar.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string_view>
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
/**
|
||||
* Abstract base class for Chinese-based calendars.
|
||||
*
|
||||
* Overrides the same methods as icu::ChineseCalendar to ensure compatible
|
||||
* behavior even when using ICU4X as the underlying calendar implementation.
|
||||
*/
|
||||
class ICU4XChineseBasedCalendar : public ICU4XCalendar {
|
||||
protected:
|
||||
ICU4XChineseBasedCalendar(capi::ICU4XAnyCalendarKind kind,
|
||||
const icu::Locale& locale, UErrorCode& success);
|
||||
ICU4XChineseBasedCalendar(capi::ICU4XAnyCalendarKind kind,
|
||||
const icu::TimeZone& timeZone,
|
||||
const icu::Locale& locale, UErrorCode& success);
|
||||
ICU4XChineseBasedCalendar(const ICU4XChineseBasedCalendar& other);
|
||||
|
||||
public:
|
||||
ICU4XChineseBasedCalendar() = delete;
|
||||
virtual ~ICU4XChineseBasedCalendar();
|
||||
|
||||
protected:
|
||||
bool hasLeapMonths() const override;
|
||||
bool hasMonthCode(MonthCode monthCode) const override;
|
||||
bool requiresFallbackForExtendedYear(int32_t year) const override;
|
||||
bool requiresFallbackForGregorianYear(int32_t year) const override;
|
||||
|
||||
/**
|
||||
* Difference to the related Gregorian year.
|
||||
*/
|
||||
virtual int32_t relatedYearDifference() const = 0;
|
||||
|
||||
static constexpr int32_t chineseRelatedYearDiff = -2637;
|
||||
|
||||
public:
|
||||
bool inTemporalLeapYear(UErrorCode& status) const override;
|
||||
int32_t getRelatedYear(UErrorCode& status) const override;
|
||||
void setRelatedYear(int32_t year) override;
|
||||
|
||||
protected:
|
||||
void handleComputeFields(int32_t julianDay, UErrorCode& status) override;
|
||||
int32_t handleGetLimit(UCalendarDateFields field,
|
||||
ELimitType limitType) const override;
|
||||
const icu::UFieldResolutionTable* getFieldResolutionTable() const override;
|
||||
|
||||
private:
|
||||
static const icu::UFieldResolutionTable CHINESE_DATE_PRECEDENCE[];
|
||||
};
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
|
||||
#endif
|
||||
55
intl/components/src/calendar/ICU4XChineseCalendar.cpp
Normal file
55
intl/components/src/calendar/ICU4XChineseCalendar.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/intl/calendar/ICU4XChineseCalendar.h"
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
ICU4XChineseCalendar::ICU4XChineseCalendar(const icu::Locale& locale,
|
||||
UErrorCode& success)
|
||||
: ICU4XChineseBasedCalendar(capi::ICU4XAnyCalendarKind_Chinese, locale,
|
||||
success) {}
|
||||
|
||||
ICU4XChineseCalendar::ICU4XChineseCalendar(const icu::TimeZone& timeZone,
|
||||
const icu::Locale& locale,
|
||||
UErrorCode& success)
|
||||
: ICU4XChineseBasedCalendar(capi::ICU4XAnyCalendarKind_Chinese, timeZone,
|
||||
locale, success) {}
|
||||
|
||||
ICU4XChineseCalendar::ICU4XChineseCalendar(const ICU4XChineseCalendar& other)
|
||||
: ICU4XChineseBasedCalendar(other) {}
|
||||
|
||||
ICU4XChineseCalendar::~ICU4XChineseCalendar() = default;
|
||||
|
||||
ICU4XChineseCalendar* ICU4XChineseCalendar::clone() const {
|
||||
return new ICU4XChineseCalendar(*this);
|
||||
}
|
||||
|
||||
const char* ICU4XChineseCalendar::getType() const { return "chinese"; }
|
||||
|
||||
////////////////////////////////////////////
|
||||
// ICU4XCalendar implementation overrides //
|
||||
////////////////////////////////////////////
|
||||
|
||||
std::string_view ICU4XChineseCalendar::eraName(int32_t extendedYear) const {
|
||||
return "chinese";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// icu::Calendar implementation overrides //
|
||||
////////////////////////////////////////////
|
||||
|
||||
UDate ICU4XChineseCalendar::defaultCenturyStart() const {
|
||||
return defaultCentury_.start();
|
||||
}
|
||||
|
||||
int32_t ICU4XChineseCalendar::defaultCenturyStartYear() const {
|
||||
return defaultCentury_.startYear();
|
||||
}
|
||||
|
||||
UBool ICU4XChineseCalendar::haveDefaultCentury() const { return true; }
|
||||
|
||||
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICU4XChineseCalendar)
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
60
intl/components/src/calendar/ICU4XChineseCalendar.h
Normal file
60
intl/components/src/calendar/ICU4XChineseCalendar.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef intl_components_calendar_ICU4XChineseCalendar_h_
|
||||
#define intl_components_calendar_ICU4XChineseCalendar_h_
|
||||
|
||||
#include "mozilla/intl/calendar/ICU4XChineseBasedCalendar.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string_view>
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
/**
|
||||
* Chinese calendar implementation.
|
||||
*
|
||||
* Overrides the same methods as icu::ChineseCalendar to ensure compatible
|
||||
* behavior even when using ICU4X as the underlying calendar implementation.
|
||||
*/
|
||||
class ICU4XChineseCalendar : public ICU4XChineseBasedCalendar {
|
||||
public:
|
||||
ICU4XChineseCalendar() = delete;
|
||||
ICU4XChineseCalendar(const icu::Locale& locale, UErrorCode& success);
|
||||
ICU4XChineseCalendar(const icu::TimeZone& timeZone, const icu::Locale& locale,
|
||||
UErrorCode& success);
|
||||
ICU4XChineseCalendar(const ICU4XChineseCalendar& other);
|
||||
|
||||
virtual ~ICU4XChineseCalendar();
|
||||
|
||||
ICU4XChineseCalendar* clone() const override;
|
||||
|
||||
const char* getType() const override;
|
||||
|
||||
protected:
|
||||
std::string_view eraName(int32_t extendedYear) const override;
|
||||
|
||||
int32_t relatedYearDifference() const override {
|
||||
return chineseRelatedYearDiff;
|
||||
}
|
||||
|
||||
public:
|
||||
UClassID getDynamicClassID() const override;
|
||||
static UClassID U_EXPORT2 getStaticClassID();
|
||||
|
||||
protected:
|
||||
DECLARE_OVERRIDE_SYSTEM_DEFAULT_CENTURY
|
||||
|
||||
struct SystemDefaultCenturyLocale {
|
||||
static inline const char* identifier = "@calendar=chinese";
|
||||
};
|
||||
static inline SystemDefaultCentury<ICU4XChineseCalendar,
|
||||
SystemDefaultCenturyLocale>
|
||||
defaultCentury_{};
|
||||
};
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
|
||||
#endif
|
||||
55
intl/components/src/calendar/ICU4XDangiCalendar.cpp
Normal file
55
intl/components/src/calendar/ICU4XDangiCalendar.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/intl/calendar/ICU4XDangiCalendar.h"
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
ICU4XDangiCalendar::ICU4XDangiCalendar(const icu::Locale& locale,
|
||||
UErrorCode& success)
|
||||
: ICU4XChineseBasedCalendar(capi::ICU4XAnyCalendarKind_Dangi, locale,
|
||||
success) {}
|
||||
|
||||
ICU4XDangiCalendar::ICU4XDangiCalendar(const icu::TimeZone& timeZone,
|
||||
const icu::Locale& locale,
|
||||
UErrorCode& success)
|
||||
: ICU4XChineseBasedCalendar(capi::ICU4XAnyCalendarKind_Dangi, timeZone,
|
||||
locale, success) {}
|
||||
|
||||
ICU4XDangiCalendar::ICU4XDangiCalendar(const ICU4XDangiCalendar& other)
|
||||
: ICU4XChineseBasedCalendar(other) {}
|
||||
|
||||
ICU4XDangiCalendar::~ICU4XDangiCalendar() = default;
|
||||
|
||||
ICU4XDangiCalendar* ICU4XDangiCalendar::clone() const {
|
||||
return new ICU4XDangiCalendar(*this);
|
||||
}
|
||||
|
||||
const char* ICU4XDangiCalendar::getType() const { return "dangi"; }
|
||||
|
||||
////////////////////////////////////////////
|
||||
// ICU4XCalendar implementation overrides //
|
||||
////////////////////////////////////////////
|
||||
|
||||
std::string_view ICU4XDangiCalendar::eraName(int32_t extendedYear) const {
|
||||
return "dangi";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// icu::Calendar implementation overrides //
|
||||
////////////////////////////////////////////
|
||||
|
||||
UDate ICU4XDangiCalendar::defaultCenturyStart() const {
|
||||
return defaultCentury_.start();
|
||||
}
|
||||
|
||||
int32_t ICU4XDangiCalendar::defaultCenturyStartYear() const {
|
||||
return defaultCentury_.startYear();
|
||||
}
|
||||
|
||||
UBool ICU4XDangiCalendar::haveDefaultCentury() const { return true; }
|
||||
|
||||
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICU4XDangiCalendar)
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
62
intl/components/src/calendar/ICU4XDangiCalendar.h
Normal file
62
intl/components/src/calendar/ICU4XDangiCalendar.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef intl_components_calendar_ICU4XDangiCalendar_h_
|
||||
#define intl_components_calendar_ICU4XDangiCalendar_h_
|
||||
|
||||
#include "mozilla/intl/calendar/ICU4XChineseBasedCalendar.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string_view>
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
/**
|
||||
* Dangi (traditional Korean) calendar implementation.
|
||||
*
|
||||
* Overrides the same methods as icu::DangiCalendar to ensure compatible
|
||||
* behavior even when using ICU4X as the underlying calendar implementation.
|
||||
*/
|
||||
class ICU4XDangiCalendar : public ICU4XChineseBasedCalendar {
|
||||
public:
|
||||
ICU4XDangiCalendar() = delete;
|
||||
ICU4XDangiCalendar(const icu::Locale& locale, UErrorCode& success);
|
||||
ICU4XDangiCalendar(const icu::TimeZone& timeZone, const icu::Locale& locale,
|
||||
UErrorCode& success);
|
||||
ICU4XDangiCalendar(const ICU4XDangiCalendar& other);
|
||||
|
||||
virtual ~ICU4XDangiCalendar();
|
||||
|
||||
ICU4XDangiCalendar* clone() const override;
|
||||
|
||||
const char* getType() const override;
|
||||
|
||||
protected:
|
||||
std::string_view eraName(int32_t extendedYear) const override;
|
||||
|
||||
static constexpr int32_t dangiRelatedYearDiff = -2333;
|
||||
|
||||
int32_t relatedYearDifference() const override {
|
||||
return dangiRelatedYearDiff;
|
||||
}
|
||||
|
||||
public:
|
||||
UClassID getDynamicClassID() const override;
|
||||
static UClassID U_EXPORT2 getStaticClassID();
|
||||
|
||||
protected:
|
||||
DECLARE_OVERRIDE_SYSTEM_DEFAULT_CENTURY
|
||||
|
||||
struct SystemDefaultCenturyLocale {
|
||||
static inline const char* identifier = "@calendar=dangi";
|
||||
};
|
||||
static inline SystemDefaultCentury<ICU4XDangiCalendar,
|
||||
SystemDefaultCenturyLocale>
|
||||
defaultCentury_{};
|
||||
};
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
|
||||
#endif
|
||||
42
intl/components/src/calendar/ICU4XUniquePtr.h
Normal file
42
intl/components/src/calendar/ICU4XUniquePtr.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef intl_components_calendar_ICU4XUniquePtr_h_
|
||||
#define intl_components_calendar_ICU4XUniquePtr_h_
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "ICU4XCalendar.h"
|
||||
#include "ICU4XDate.h"
|
||||
#include "ICU4XIsoDate.h"
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
class ICU4XCalendarDeleter {
|
||||
public:
|
||||
void operator()(capi::ICU4XCalendar* ptr) {
|
||||
capi::ICU4XCalendar_destroy(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
using UniqueICU4XCalendar =
|
||||
mozilla::UniquePtr<capi::ICU4XCalendar, ICU4XCalendarDeleter>;
|
||||
|
||||
class ICU4XDateDeleter {
|
||||
public:
|
||||
void operator()(capi::ICU4XDate* ptr) { capi::ICU4XDate_destroy(ptr); }
|
||||
};
|
||||
|
||||
using UniqueICU4XDate = mozilla::UniquePtr<capi::ICU4XDate, ICU4XDateDeleter>;
|
||||
|
||||
class ICU4XIsoDateDeleter {
|
||||
public:
|
||||
void operator()(capi::ICU4XIsoDate* ptr) { capi::ICU4XIsoDate_destroy(ptr); }
|
||||
};
|
||||
|
||||
using UniqueICU4XIsoDate =
|
||||
mozilla::UniquePtr<capi::ICU4XIsoDate, ICU4XIsoDateDeleter>;
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
|
||||
#endif
|
||||
59
intl/components/src/calendar/ISODate.cpp
Normal file
59
intl/components/src/calendar/ISODate.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/intl/calendar/ISODate.h"
|
||||
|
||||
#include <array>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
// Copied from js/src/builtin/temporal/Calendar.cpp
|
||||
|
||||
static int32_t DayFromYear(int32_t year) {
|
||||
return 365 * (year - 1970) + FloorDiv(year - 1969, 4) -
|
||||
FloorDiv(year - 1901, 100) + FloorDiv(year - 1601, 400);
|
||||
}
|
||||
|
||||
static constexpr bool IsISOLeapYear(int32_t year) {
|
||||
return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
|
||||
}
|
||||
|
||||
static constexpr int32_t ISODaysInMonth(int32_t year, int32_t month) {
|
||||
MOZ_ASSERT(1 <= month && month <= 12);
|
||||
|
||||
constexpr uint8_t daysInMonth[2][13] = {
|
||||
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
|
||||
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
|
||||
|
||||
return daysInMonth[IsISOLeapYear(year)][month];
|
||||
}
|
||||
|
||||
static constexpr auto FirstDayOfMonth(int32_t year) {
|
||||
// The following array contains the day of year for the first day of each
|
||||
// month, where index 0 is January, and day 0 is January 1.
|
||||
std::array<int32_t, 13> days = {};
|
||||
for (int32_t month = 1; month <= 12; ++month) {
|
||||
days[month] = days[month - 1] + ISODaysInMonth(year, month);
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
static int32_t ISODayOfYear(const ISODate& isoDate) {
|
||||
const auto& [year, month, day] = isoDate;
|
||||
|
||||
// First day of month arrays for non-leap and leap years.
|
||||
constexpr decltype(FirstDayOfMonth(0)) firstDayOfMonth[2] = {
|
||||
FirstDayOfMonth(1), FirstDayOfMonth(0)};
|
||||
|
||||
return firstDayOfMonth[IsISOLeapYear(year)][month - 1] + day;
|
||||
}
|
||||
|
||||
int32_t MakeDay(const ISODate& date) {
|
||||
return DayFromYear(date.year) + ISODayOfYear(date) - 1;
|
||||
}
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
41
intl/components/src/calendar/ISODate.h
Normal file
41
intl/components/src/calendar/ISODate.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef intl_components_calendar_ISODate_h_
|
||||
#define intl_components_calendar_ISODate_h_
|
||||
|
||||
#include "mozilla/intl/calendar/MonthCode.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
struct ISODate final {
|
||||
int32_t year = 0;
|
||||
int32_t month = 0;
|
||||
int32_t day = 0;
|
||||
};
|
||||
|
||||
struct CalendarDate final {
|
||||
int32_t year = 0;
|
||||
MonthCode monthCode = {};
|
||||
int32_t day = 0;
|
||||
};
|
||||
|
||||
inline int32_t FloorDiv(int32_t dividend, int32_t divisor) {
|
||||
int32_t quotient = dividend / divisor;
|
||||
int32_t remainder = dividend % divisor;
|
||||
if (remainder < 0) {
|
||||
quotient -= 1;
|
||||
}
|
||||
return quotient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the day relative to the Unix epoch, January 1 1970.
|
||||
*/
|
||||
int32_t MakeDay(const ISODate& date);
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
|
||||
#endif
|
||||
102
intl/components/src/calendar/MonthCode.h
Normal file
102
intl/components/src/calendar/MonthCode.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef intl_components_calendar_MonthCode_h_
|
||||
#define intl_components_calendar_MonthCode_h_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string_view>
|
||||
|
||||
namespace mozilla::intl::calendar {
|
||||
|
||||
// Copied from js/src/builtin/temporal/MonthCode.h
|
||||
|
||||
class MonthCode final {
|
||||
public:
|
||||
enum class Code {
|
||||
Invalid = 0,
|
||||
|
||||
// Months 01 - M12.
|
||||
M01 = 1,
|
||||
M02,
|
||||
M03,
|
||||
M04,
|
||||
M05,
|
||||
M06,
|
||||
M07,
|
||||
M08,
|
||||
M09,
|
||||
M10,
|
||||
M11,
|
||||
M12,
|
||||
|
||||
// Epagomenal month M13.
|
||||
M13,
|
||||
|
||||
// Leap months M01 - M12.
|
||||
M01L,
|
||||
M02L,
|
||||
M03L,
|
||||
M04L,
|
||||
M05L,
|
||||
M06L,
|
||||
M07L,
|
||||
M08L,
|
||||
M09L,
|
||||
M10L,
|
||||
M11L,
|
||||
M12L,
|
||||
};
|
||||
|
||||
private:
|
||||
static constexpr int32_t toLeapMonth =
|
||||
static_cast<int32_t>(Code::M01L) - static_cast<int32_t>(Code::M01);
|
||||
|
||||
Code code_ = Code::Invalid;
|
||||
|
||||
public:
|
||||
constexpr MonthCode() = default;
|
||||
|
||||
constexpr explicit MonthCode(Code code) : code_(code) {}
|
||||
|
||||
constexpr explicit MonthCode(int32_t month, bool isLeapMonth = false) {
|
||||
code_ = static_cast<Code>(month + (isLeapMonth ? toLeapMonth : 0));
|
||||
}
|
||||
|
||||
constexpr auto code() const { return code_; }
|
||||
|
||||
constexpr int32_t ordinal() const {
|
||||
int32_t ordinal = static_cast<int32_t>(code_);
|
||||
if (isLeapMonth()) {
|
||||
ordinal -= toLeapMonth;
|
||||
}
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
constexpr bool isLeapMonth() const { return code_ >= Code::M01L; }
|
||||
|
||||
constexpr explicit operator std::string_view() const {
|
||||
constexpr const char* name =
|
||||
"M01L"
|
||||
"M02L"
|
||||
"M03L"
|
||||
"M04L"
|
||||
"M05L"
|
||||
"M06L"
|
||||
"M07L"
|
||||
"M08L"
|
||||
"M09L"
|
||||
"M10L"
|
||||
"M11L"
|
||||
"M12L"
|
||||
"M13";
|
||||
size_t index = (ordinal() - 1) * 4;
|
||||
size_t length = 3 + isLeapMonth();
|
||||
return {name + index, length};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla::intl::calendar
|
||||
|
||||
#endif
|
||||
35
intl/components/src/calendar/moz.build
Normal file
35
intl/components/src/calendar/moz.build
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
FINAL_LIBRARY = "intlcomponents"
|
||||
|
||||
EXPORTS.mozilla.intl.calendar = [
|
||||
"ICU4XCalendar.h",
|
||||
"ICU4XChineseBasedCalendar.h",
|
||||
"ICU4XChineseCalendar.h",
|
||||
"ICU4XDangiCalendar.h",
|
||||
"ICU4XUniquePtr.h",
|
||||
"ISODate.h",
|
||||
"MonthCode.h",
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
"/intl/icu_capi/bindings/c",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"ICU4XCalendar.cpp",
|
||||
"ICU4XChineseBasedCalendar.cpp",
|
||||
"ICU4XChineseCalendar.cpp",
|
||||
"ICU4XDangiCalendar.cpp",
|
||||
"ISODate.cpp",
|
||||
]
|
||||
|
||||
# ICU requires RTTI
|
||||
if CONFIG["CC_TYPE"] in ("clang", "gcc"):
|
||||
CXXFLAGS += ["-frtti"]
|
||||
elif CONFIG["OS_TARGET"] == "WINNT":
|
||||
CXXFLAGS += ["-GR"]
|
||||
@@ -5314,6 +5314,10 @@ product.
|
||||
<li><code>intl/tzdata</code></li>
|
||||
<li><code>js/src/util</code></li>
|
||||
</ul>
|
||||
<p>and to parts of the code in:</p>
|
||||
<ul>
|
||||
<li><code>intl/components/src/calendar/ICU4XChineseBasedCalendar.cpp</code></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<pre>
|
||||
|
||||
Reference in New Issue
Block a user