ICU-22027 Add C++ Calendar API for Temporal

API proposal
https://docs.google.com/document/d/1UYriEzzExiLhi2RD3zjTsI5UQHv1dXaFqrct7yXNdCA/edit#heading=h.x9obor85vpx9

Design Doc
https://docs.google.com/document/d/15ViyC9s0k3VEDwBmAkKxxz4IadZ6QrAIoETkdkF0cVA/

ICU-22027 Adjust API to remove the mention of M00L for now.
This commit is contained in:
Frank Tang 2022-08-23 16:47:06 -07:00 committed by Frank Yung-Fong Tang
parent a7f4531bfa
commit cd1b772cbf
21 changed files with 2212 additions and 338 deletions

View file

@ -660,6 +660,7 @@ static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = {
{ -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY
{ 0, 0, 24*kOneHour-1, 24*kOneHour-1 }, // MILLISECONDS_IN_DAY
{ 0, 0, 1, 1 }, // IS_LEAP_MONTH
{ 0, 0, 11, 11 } // ORDINAL_MONTH
};
// Resource bundle tags read by this class
@ -1421,7 +1422,8 @@ void Calendar::computeFields(UErrorCode &ec)
(1 << UCAL_MONTH) |
(1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE
(1 << UCAL_DAY_OF_YEAR) |
(1 << UCAL_EXTENDED_YEAR);
(1 << UCAL_EXTENDED_YEAR) |
(1 << UCAL_ORDINAL_MONTH);
for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
if ((mask & 1) == 0) {
@ -1690,7 +1692,9 @@ void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode& status)
if (U_FAILURE(status)) {
return;
}
internalSet(UCAL_MONTH, getGregorianMonth());
int32_t month = getGregorianMonth();
internalSet(UCAL_MONTH, month);
internalSet(UCAL_ORDINAL_MONTH, month);
internalSet(UCAL_DAY_OF_MONTH, getGregorianDayOfMonth());
internalSet(UCAL_DAY_OF_YEAR, getGregorianDayOfYear());
int32_t eyear = getGregorianYear();
@ -1772,6 +1776,7 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
}
case UCAL_MONTH:
case UCAL_ORDINAL_MONTH:
// Rolling the month involves both pinning the final value
// and adjusting the DAY_OF_MONTH if necessary. We only adjust the
// DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
@ -1829,6 +1834,7 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
}
set(field, newYear);
pinField(UCAL_MONTH,status);
pinField(UCAL_ORDINAL_MONTH,status);
pinField(UCAL_DAY_OF_MONTH,status);
return;
}
@ -1837,6 +1843,7 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
// Rolling the year can involve pinning the DAY_OF_MONTH.
set(field, internalGet(field) + amount);
pinField(UCAL_MONTH,status);
pinField(UCAL_ORDINAL_MONTH,status);
pinField(UCAL_DAY_OF_MONTH,status);
return;
@ -1976,6 +1983,7 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
// have to be updated as well.
set(UCAL_DAY_OF_YEAR, day_of_year);
clear(UCAL_MONTH);
clear(UCAL_ORDINAL_MONTH);
return;
}
case UCAL_DAY_OF_YEAR:
@ -2118,6 +2126,7 @@ void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status
U_FALLTHROUGH;
case UCAL_EXTENDED_YEAR:
case UCAL_MONTH:
case UCAL_ORDINAL_MONTH:
{
UBool oldLenient = isLenient();
setLenient(true);
@ -2700,7 +2709,6 @@ int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) cons
}
}
int32_t
Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const
{
@ -2764,6 +2772,47 @@ Calendar::inDaylightTime(UErrorCode& status) const
return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : false);
}
bool
Calendar::inTemporalLeapYear(UErrorCode& status) const
{
// Default to Gregorian based leap year rule.
return getActualMaximum(UCAL_DAY_OF_YEAR, status) == 366;
}
// -------------------------------------
static const char * const gTemporalMonthCodes[] = {
"M01", "M02", "M03", "M04", "M05", "M06",
"M07", "M08", "M09", "M10", "M11", "M12", nullptr
};
const char*
Calendar::getTemporalMonthCode(UErrorCode& status) const
{
int32_t month = get(UCAL_MONTH, status);
if (U_FAILURE(status)) return nullptr;
U_ASSERT(month < 12);
U_ASSERT(internalGet(UCAL_IS_LEAP_MONTH) == 0);
return gTemporalMonthCodes[month];
}
void
Calendar::setTemporalMonthCode(const char* code, UErrorCode& status )
{
if (U_FAILURE(status)) return;
int32_t len = static_cast<int32_t>(uprv_strlen(code));
if (len == 3 && code[0] == 'M') {
for (int m = 0; gTemporalMonthCodes[m] != nullptr; m++) {
if (uprv_strcmp(code, gTemporalMonthCodes[m]) == 0) {
set(UCAL_MONTH, m);
set(UCAL_IS_LEAP_MONTH, 0);
return;
}
}
}
status = U_ILLEGAL_ARGUMENT_ERROR;
}
// -------------------------------------
/**
@ -2799,7 +2848,7 @@ void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) {
switch (field) {
case UCAL_DAY_OF_MONTH:
y = handleGetExtendedYear();
validateField(field, 1, handleGetMonthLength(y, internalGet(UCAL_MONTH)), status);
validateField(field, 1, handleGetMonthLength(y, internalGetMonth()), status);
break;
case UCAL_DAY_OF_YEAR:
y = handleGetExtendedYear();
@ -2860,7 +2909,7 @@ UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCale
return defaultField;
}
UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) {
UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) const {
int32_t bestField = UCAL_FIELD_COUNT;
int32_t tempBestField;
for (int32_t g=0; precedenceTable[g][0][0] != -1 && (bestField == UCAL_FIELD_COUNT); ++g) {
@ -2929,6 +2978,16 @@ const UFieldResolutionTable Calendar::kDatePrecedence[] =
};
const UFieldResolutionTable Calendar::kMonthPrecedence[] =
{
{
{ UCAL_MONTH,kResolveSTOP, kResolveSTOP },
{ UCAL_ORDINAL_MONTH,kResolveSTOP, kResolveSTOP },
{kResolveSTOP}
},
{{kResolveSTOP}}
};
const UFieldResolutionTable Calendar::kDOWPrecedence[] =
{
{
@ -3211,6 +3270,7 @@ int32_t Calendar::computeJulianDay()
if (fStamp[UCAL_JULIAN_DAY] >= (int32_t)kMinimumUserStamp) {
int32_t bestStamp = newestStamp(UCAL_ERA, UCAL_DAY_OF_WEEK_IN_MONTH, kUnset);
bestStamp = newestStamp(UCAL_YEAR_WOY, UCAL_EXTENDED_YEAR, bestStamp);
bestStamp = newestStamp(UCAL_ORDINAL_MONTH, UCAL_ORDINAL_MONTH, bestStamp);
if (bestStamp <= fStamp[UCAL_JULIAN_DAY]) {
return internalGet(UCAL_JULIAN_DAY);
}
@ -3250,8 +3310,8 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) {
// give calendar subclass a chance to have a default 'first' month
int32_t month;
if(isSet(UCAL_MONTH)) {
month = internalGet(UCAL_MONTH);
if(isSet(UCAL_MONTH) || isSet(UCAL_ORDINAL_MONTH)) {
month = internalGetMonth();
} else {
month = getDefaultMonthInYear(year);
}
@ -3319,7 +3379,7 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) {
// past the first of the given day-of-week in this month.
// Note that we handle -2, -3, etc. correctly, even though
// values < -1 are technically disallowed.
int32_t m = internalGet(UCAL_MONTH, UCAL_JANUARY);
int32_t m = internalGetMonth(UCAL_JANUARY);
int32_t monthLength = handleGetMonthLength(year, m);
date += ((monthLength - date) / 7 + dim + 1) * 7;
}
@ -3544,23 +3604,25 @@ int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t w
}
case UCAL_DATE:
if((internalGet(UCAL_MONTH)==0) &&
{
int32_t m = internalGetMonth();
if((m == 0) &&
(woy >= getLeastMaximum(UCAL_WEEK_OF_YEAR))) {
return yearWoy+1; // month 0, late woy = in the next year
} else if(woy==1) {
//if(nextJan1InPrevYear) {
if(internalGet(UCAL_MONTH)==0) {
if(m == 0) {
return yearWoy;
} else {
return yearWoy-1;
}
//}
}
//(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) {
//within 1st week and in this month..
//return yearWoy+1;
return yearWoy;
}
//(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) {
//within 1st week and in this month..
//return yearWoy+1;
return yearWoy;
default: // assume the year is appropriate
return yearWoy;
@ -3626,6 +3688,10 @@ Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
result = getMaximum(field);
break;
case UCAL_ORDINAL_MONTH:
result = inTemporalLeapYear(status) ? getMaximum(UCAL_ORDINAL_MONTH) : getLeastMaximum(UCAL_ORDINAL_MONTH);
break;
default:
// For all other fields, do it the hard way....
result = getActualHelper(field, getLeastMaximum(field), getMaximum(field),status);
@ -3955,6 +4021,20 @@ Calendar::internalSet(EDateFields field, int32_t value)
internalSet((UCalendarDateFields) field, value);
}
int32_t Calendar::internalGetMonth() const {
if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
return internalGet(UCAL_MONTH);
}
return internalGet(UCAL_ORDINAL_MONTH);
}
int32_t Calendar::internalGetMonth(int32_t defaultValue) const {
if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
return internalGet(UCAL_MONTH, defaultValue);
}
return internalGet(UCAL_ORDINAL_MONTH);
}
BasicTimeZone*
Calendar::getBasicTimeZone(void) const {
if (dynamic_cast<const OlsonTimeZone *>(fZone) != NULL

View file

@ -13,6 +13,7 @@
#include "cecal.h"
#include "gregoimp.h" //Math
#include "cstring.h"
U_NAMESPACE_BEGIN
@ -42,6 +43,7 @@ static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
{/*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
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
{ 0, 0, 12, 12}, // ORDINAL_MONTH
};
//-------------------------------------------------------------------------
@ -132,6 +134,24 @@ CECalendar::jdToCE(int32_t julianDay, int32_t jdEpochOffset, int32_t& year, int3
day = (doy % 30) + 1; // 1-based days in a month
}
static const char* kMonthCode13 = "M13";
const char* CECalendar::getTemporalMonthCode(UErrorCode& status) const {
if (get(UCAL_MONTH, status) == 12) return kMonthCode13;
return Calendar::getTemporalMonthCode(status);
}
void
CECalendar::setTemporalMonthCode(const char* code, UErrorCode& status) {
if (U_FAILURE(status)) return;
if (uprv_strcmp(code, kMonthCode13) == 0) {
set(UCAL_MONTH, 12);
set(UCAL_IS_LEAP_MONTH, 0);
return;
}
Calendar::setTemporalMonthCode(code, status);
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -24,6 +24,36 @@ U_NAMESPACE_BEGIN
*/
class U_I18N_API CECalendar : public Calendar {
public:
/**
* Gets The Temporal monthCode value corresponding to the month for the date.
* The value is a string identifier that starts with the literal grapheme
* "M" followed by two graphemes representing the zero-padded month number
* of the current month in a normal (non-leap) year. For the short thirteen
* month in each year in the CECalendar, the value is "M13".
*
* @param status ICU Error Code
* @return One of 13 possible strings in {"M01".. "M12", "M13"}.
* @draft ICU 73
*/
virtual const char* getTemporalMonthCode(UErrorCode& status) const override;
/**
* Sets The Temporal monthCode which is a string identifier that starts
* with the literal grapheme "M" followed by two graphemes representing
* the zero-padded month number of the current month in a normal
* (non-leap) year. For CECalendar calendar, the values
* are "M01" .. "M13" while the "M13" is represent the short thirteen month
* in each year.
*
* @param temporalMonth The value to be set for temporal monthCode.
* @param status ICU Error Code
*
* @draft ICU 73
*/
virtual void setTemporalMonthCode(const char* code, UErrorCode& status) override;
protected:
//-------------------------------------------------------------------------
// Constructors...

View file

@ -26,6 +26,7 @@
#include "unicode/simpletz.h"
#include "uhash.h"
#include "ucln_in.h"
#include "cstring.h"
// Debugging
#ifdef U_DEBUG_CHNSECAL
@ -124,7 +125,7 @@ ChineseCalendar* ChineseCalendar::clone() const {
ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success)
: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success),
isLeapYear(false),
hasLeapMonthBetweenWinterSolstices(false),
fEpochYear(CHINESE_EPOCH_YEAR),
fZoneAstroCalc(getChineseCalZoneAstroCalc())
{
@ -134,7 +135,7 @@ ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success)
ChineseCalendar::ChineseCalendar(const Locale& aLocale, int32_t epochYear,
const TimeZone* zoneAstroCalc, UErrorCode &success)
: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success),
isLeapYear(false),
hasLeapMonthBetweenWinterSolstices(false),
fEpochYear(epochYear),
fZoneAstroCalc(zoneAstroCalc)
{
@ -142,7 +143,7 @@ ChineseCalendar::ChineseCalendar(const Locale& aLocale, int32_t epochYear,
}
ChineseCalendar::ChineseCalendar(const ChineseCalendar& other) : Calendar(other) {
isLeapYear = other.isLeapYear;
hasLeapMonthBetweenWinterSolstices = other.hasLeapMonthBetweenWinterSolstices;
fEpochYear = other.fEpochYear;
fZoneAstroCalc = other.fZoneAstroCalc;
}
@ -196,6 +197,7 @@ static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
{/*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
};
@ -321,7 +323,6 @@ const UFieldResolutionTable* ChineseCalendar::getFieldResolutionTable() const {
* @stable ICU 2.8
*/
int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const {
ChineseCalendar *nonConstThis = (ChineseCalendar*)this; // cast away const
// If the month is out of range, adjust it into range, and
@ -340,6 +341,7 @@ int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
// Save fields for later restoration
int32_t saveMonth = internalGet(UCAL_MONTH);
int32_t saveOrdinalMonth = internalGet(UCAL_ORDINAL_MONTH);
int32_t saveIsLeapMonth = internalGet(UCAL_IS_LEAP_MONTH);
// Ignore IS_LEAP_MONTH field if useMonth is false
@ -361,8 +363,8 @@ int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
}
nonConstThis->internalSet(UCAL_MONTH, saveMonth);
nonConstThis->internalSet(UCAL_ORDINAL_MONTH, saveOrdinalMonth);
nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, saveIsLeapMonth);
return julianDay - 1;
}
@ -374,6 +376,7 @@ int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
void ChineseCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) {
switch (field) {
case UCAL_MONTH:
case UCAL_ORDINAL_MONTH:
if (amount != 0) {
int32_t dom = get(UCAL_DAY_OF_MONTH, status);
if (U_FAILURE(status)) break;
@ -404,6 +407,7 @@ void ChineseCalendar::add(EDateFields field, int32_t amount, UErrorCode& status)
void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) {
switch (field) {
case UCAL_MONTH:
case UCAL_ORDINAL_MONTH:
if (amount != 0) {
int32_t dom = get(UCAL_DAY_OF_MONTH, status);
if (U_FAILURE(status)) break;
@ -419,7 +423,7 @@ void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode
// leap year.
int32_t m = get(UCAL_MONTH, status); // 0-based month
if (U_FAILURE(status)) break;
if (isLeapYear) { // (member variable)
if (hasLeapMonthBetweenWinterSolstices) { // (member variable)
if (get(UCAL_IS_LEAP_MONTH, status) == 1) {
++m;
} else {
@ -442,7 +446,7 @@ void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode
// Now do the standard roll computation on m, with the
// allowed range of 0..n-1, where n is 12 or 13.
int32_t n = isLeapYear ? 13 : 12; // Months in this year
int32_t n = hasLeapMonthBetweenWinterSolstices ? 13 : 12; // Months in this year
int32_t newM = (m + amount) % n;
if (newM < 0) {
newM += n;
@ -661,7 +665,7 @@ UBool ChineseCalendar::isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) co
* IS_LEAP_MONTH fields, as required by
* <code>handleComputeMonthStart()</code>.
*
* <p>As a side effect, this method sets {@link #isLeapYear}.
* <p>As a side effect, this method sets {@link #hasLeapMonthBetweenWinterSolstices}.
* @param days days after January 1, 1970 0:00 astronomical base zone
* of the date to compute fields for
* @param gyear the Gregorian year of the given date
@ -672,7 +676,6 @@ UBool ChineseCalendar::isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) co
*/
void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t gmonth,
UBool setAllFields) {
// Find the winter solstices before and after the target date.
// These define the boundaries of this Chinese year, specifically,
// the position of month 11, which always contains the solstice.
@ -692,24 +695,33 @@ void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t
int32_t firstMoon = newMoonNear(solsticeBefore + 1, true);
int32_t lastMoon = newMoonNear(solsticeAfter + 1, false);
int32_t thisMoon = newMoonNear(days + 1, false); // Start of this month
// Note: isLeapYear is a member variable
isLeapYear = synodicMonthsBetween(firstMoon, lastMoon) == 12;
// Note: hasLeapMonthBetweenWinterSolstices is a member variable
hasLeapMonthBetweenWinterSolstices = synodicMonthsBetween(firstMoon, lastMoon) == 12;
int32_t month = synodicMonthsBetween(firstMoon, thisMoon);
if (isLeapYear && isLeapMonthBetween(firstMoon, thisMoon)) {
int32_t theNewYear = newYear(gyear);
if (days < theNewYear) {
theNewYear = newYear(gyear-1);
}
if (hasLeapMonthBetweenWinterSolstices && isLeapMonthBetween(firstMoon, thisMoon)) {
month--;
}
if (month < 1) {
month += 12;
}
UBool isLeapMonth = isLeapYear &&
int32_t ordinalMonth = synodicMonthsBetween(theNewYear, thisMoon);
if (ordinalMonth < 0) {
ordinalMonth += 12;
}
UBool isLeapMonth = hasLeapMonthBetweenWinterSolstices &&
hasNoMajorSolarTerm(thisMoon) &&
!isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, false));
internalSet(UCAL_MONTH, month-1); // Convert from 1-based to 0-based
internalSet(UCAL_ORDINAL_MONTH, ordinalMonth); // Convert from 1-based to 0-based
internalSet(UCAL_IS_LEAP_MONTH, isLeapMonth?1:0);
if (setAllFields) {
// Extended year and cycle year is based on the epoch year
@ -897,8 +909,83 @@ ChineseCalendar::internalGetDefaultCenturyStartYear() const
return gSystemDefaultCenturyStartYear;
}
bool
ChineseCalendar::inTemporalLeapYear(UErrorCode &status) const
{
int32_t days = getActualMaximum(UCAL_DAY_OF_YEAR, status);
if (U_FAILURE(status)) return false;
return days > 360;
}
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar)
static const char * const gTemporalLeapMonthCodes[] = {
"M01L", "M02L", "M03L", "M04L", "M05L", "M06L",
"M07L", "M08L", "M09L", "M10L", "M11L", "M12L", nullptr
};
const char* ChineseCalendar::getTemporalMonthCode(UErrorCode &status) const {
// We need to call get, not internalGet, to force the calculation
// from UCAL_ORDINAL_MONTH.
int32_t is_leap = get(UCAL_IS_LEAP_MONTH, status);
if (U_FAILURE(status)) return nullptr;
if (is_leap != 0) {
int32_t month = get(UCAL_MONTH, status);
if (U_FAILURE(status)) return nullptr;
return gTemporalLeapMonthCodes[month];
}
return Calendar::getTemporalMonthCode(status);
}
void
ChineseCalendar::setTemporalMonthCode(const char* code, UErrorCode& status )
{
if (U_FAILURE(status)) return;
int32_t len = static_cast<int32_t>(uprv_strlen(code));
if (len != 4 || code[0] != 'M' || code[3] != 'L') {
set(UCAL_IS_LEAP_MONTH, 0);
return Calendar::setTemporalMonthCode(code, status);
}
for (int m = 0; gTemporalLeapMonthCodes[m] != nullptr; m++) {
if (uprv_strcmp(code, gTemporalLeapMonthCodes[m]) == 0) {
set(UCAL_MONTH, m);
set(UCAL_IS_LEAP_MONTH, 1);
return;
}
}
status = U_ILLEGAL_ARGUMENT_ERROR;
}
int32_t ChineseCalendar::internalGetMonth() const {
if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
return internalGet(UCAL_MONTH);
}
LocalPointer<Calendar> temp(this->clone());
temp->set(UCAL_MONTH, 0);
temp->set(UCAL_IS_LEAP_MONTH, 0);
temp->set(UCAL_DATE, 1);
// Calculate the UCAL_MONTH and UCAL_IS_LEAP_MONTH by adding number of
// months.
UErrorCode status = U_ZERO_ERROR;
temp->roll(UCAL_MONTH, internalGet(UCAL_ORDINAL_MONTH), status);
ChineseCalendar *nonConstThis = (ChineseCalendar*)this; // cast away const
nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, temp->get(UCAL_IS_LEAP_MONTH, status));
int32_t month = temp->get(UCAL_MONTH, status);
U_ASSERT(U_SUCCESS(status));
nonConstThis->internalSet(UCAL_MONTH, month);
return month;
}
int32_t ChineseCalendar::internalGetMonth(int32_t defaultValue) const {
if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
return internalGet(UCAL_MONTH, defaultValue);
}
return internalGetMonth();
}
U_NAMESPACE_END
#endif

View file

@ -113,6 +113,49 @@ class U_I18N_API ChineseCalendar : public Calendar {
*/
ChineseCalendar(const Locale& aLocale, UErrorCode &success);
/**
* Returns true if the date is in a leap year.
*
* @param status ICU Error Code
* @return True if the date in the fields is in a Temporal proposal
* defined leap year. False otherwise.
*/
virtual bool inTemporalLeapYear(UErrorCode &status) const override;
/**
* Gets The Temporal monthCode value corresponding to the month for the date.
* The value is a string identifier that starts with the literal grapheme
* "M" followed by two graphemes representing the zero-padded month number
* of the current month in a normal (non-leap) year and suffixed by an
* optional literal grapheme "L" if this is a leap month in a lunisolar
* calendar. For Chinese calendars (including Dangi), the values are
* "M01" .. "M12" for non-leap year, and "M01" .. "M12" with one of
* "M01L" .. "M12L" for leap year.
*
* @param status ICU Error Code
* @return One of 24 possible strings in
* {"M01" .. "M12", "M01L" .. "M12L"}.
* @draft ICU 73
*/
virtual const char* getTemporalMonthCode(UErrorCode &status) const override;
/**
* Sets The Temporal monthCode which is a string identifier that starts
* with the literal grapheme "M" followed by two graphemes representing
* the zero-padded month number of the current month in a normal
* (non-leap) year and suffixed by an optional literal grapheme "L" if this
* is a leap month in a lunisolar calendar. For Chinese calendars, the values
* are "M01" .. "M12" for non-leap years, and "M01" .. "M12" plus one in
* "M01L" .. "M12L" for leap year.
*
* @param temporalMonth The value to be set for temporal monthCode. One of
* 24 possible strings in {"M01" .. "M12", "M01L" .. "M12L"}.
* @param status ICU Error Code
*
* @draft ICU 73
*/
virtual void setTemporalMonthCode(const char* code, UErrorCode& status) override;
protected:
/**
@ -152,7 +195,12 @@ class U_I18N_API ChineseCalendar : public Calendar {
// Internal data....
//-------------------------------------------------------------------------
UBool isLeapYear;
// There is a leap month between the Winter Solstice before and after the
// current date.This is different from leap year because in some year, such as
// 1813 and 2033, the leap month is after the Winter Solstice of that year. So
// this value could be false for a date prior to the Winter Solstice of that
// year but that year still has a leap month and therefor is a leap year.
UBool hasLeapMonthBetweenWinterSolstices;
int32_t fEpochYear; // Start year of this Chinese calendar instance.
const TimeZone* fZoneAstroCalc; // Zone used for the astronomical calculation
// of this Chinese calendar instance.
@ -241,6 +289,10 @@ class U_I18N_API ChineseCalendar : public Calendar {
*/
virtual const char * getType() const override;
protected:
virtual int32_t internalGetMonth(int32_t defaultValue) const override;
virtual int32_t internalGetMonth() const override;
protected:
/**

View file

@ -92,6 +92,7 @@ CopticCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/)
internalSet(UCAL_ERA, era);
internalSet(UCAL_YEAR, year);
internalSet(UCAL_MONTH, month);
internalSet(UCAL_ORDINAL_MONTH, month);
internalSet(UCAL_DATE, day);
internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day);
}

View file

@ -80,6 +80,7 @@ EthiopicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/)
internalSet(UCAL_ERA, (eyear > 0) ? AMETE_MIHRET : AMETE_ALEM);
internalSet(UCAL_YEAR, (eyear > 0) ? eyear : (eyear + AMETE_MIHRET_DELTA));
internalSet(UCAL_MONTH, month);
internalSet(UCAL_ORDINAL_MONTH, month);
internalSet(UCAL_DATE, day);
internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day);
}
@ -216,6 +217,7 @@ EthiopicAmeteAlemCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*
internalSet(UCAL_ERA, AMETE_ALEM);
internalSet(UCAL_YEAR, eyear + AMETE_MIHRET_DELTA);
internalSet(UCAL_MONTH, month);
internalSet(UCAL_ORDINAL_MONTH, month);
internalSet(UCAL_DATE, day);
internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day);
}

View file

@ -101,6 +101,7 @@ static const int32_t kGregorianCalendarLimits[UCAL_FIELD_COUNT][4] = {
{/*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
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
{ 0, 0, 11, 11}, // ORDINAL_MONTH
};
/*
@ -435,6 +436,7 @@ void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& statu
}
internalSet(UCAL_MONTH, month);
internalSet(UCAL_ORDINAL_MONTH, month);
internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
internalSet(UCAL_EXTENDED_YEAR, eyear);
@ -633,7 +635,7 @@ GregorianCalendar::yearLength() const
void
GregorianCalendar::pinDayOfMonth()
{
int32_t monthLen = monthLength(internalGet(UCAL_MONTH));
int32_t monthLen = monthLength(internalGetMonth());
int32_t dom = internalGet(UCAL_DATE);
if(dom > monthLen)
set(UCAL_DATE, monthLen);
@ -659,7 +661,7 @@ GregorianCalendar::validateFields() const
if (isSet(UCAL_DATE)) {
int32_t date = internalGet(UCAL_DATE);
if (date < getMinimum(UCAL_DATE) ||
date > monthLength(internalGet(UCAL_MONTH))) {
date > monthLength(internalGetMonth())) {
return false;
}
}
@ -839,7 +841,7 @@ GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& s
case UCAL_DAY_OF_MONTH:
case UCAL_WEEK_OF_MONTH:
{
int32_t max = monthLength(internalGet(UCAL_MONTH));
int32_t max = monthLength(internalGetMonth());
UDate t = internalGetTime();
// We subtract 1 from the DAY_OF_MONTH to make it zero-based, and an
// additional 10 if we are after the cutover. Thus the monthStart
@ -872,7 +874,7 @@ GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& s
// may be one year before or after the calendar year.
int32_t isoYear = get(UCAL_YEAR_WOY, status);
int32_t isoDoy = internalGet(UCAL_DAY_OF_YEAR);
if (internalGet(UCAL_MONTH) == UCAL_JANUARY) {
if (internalGetMonth() == UCAL_JANUARY) {
if (woy >= 52) {
isoDoy += handleGetYearLength(isoYear);
}

View file

@ -20,6 +20,7 @@
#if !UCONFIG_NO_FORMATTING
#include "cmemory.h"
#include "cstring.h"
#include "umutex.h"
#include <float.h>
#include "gregoimp.h" // ClockMath
@ -63,6 +64,7 @@ static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
{/*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
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
{ 0, 0, 11, 12}, // ORDINAL_MONTH
};
/**
@ -217,7 +219,8 @@ void HebrewCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode&
return;
}
switch (field) {
case UCAL_MONTH:
case UCAL_MONTH:
case UCAL_ORDINAL_MONTH:
{
// We can't just do a set(MONTH, get(MONTH) + amount). The
// reason is ADAR_1. Suppose amount is +2 and we land in
@ -315,6 +318,7 @@ void HebrewCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode&
}
switch (field) {
case UCAL_MONTH:
case UCAL_ORDINAL_MONTH:
{
int32_t month = get(UCAL_MONTH, status);
int32_t year = get(UCAL_YEAR, status);
@ -534,7 +538,8 @@ int32_t HebrewCalendar::handleGetYearLength(int32_t eyear) const {
}
void HebrewCalendar::validateField(UCalendarDateFields field, UErrorCode &status) {
if (field == UCAL_MONTH && !isLeapYear(handleGetExtendedYear()) && internalGet(UCAL_MONTH) == ADAR_1) {
if ((field == UCAL_MONTH || field == UCAL_ORDINAL_MONTH)
&& !isLeapYear(handleGetExtendedYear()) && internalGetMonth() == ADAR_1) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
@ -607,6 +612,11 @@ void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status)
internalSet(UCAL_ERA, 0);
internalSet(UCAL_YEAR, year);
internalSet(UCAL_EXTENDED_YEAR, year);
int32_t ordinal_month = month;
if (!isLeap && ordinal_month > ADAR_1) {
ordinal_month--;
}
internalSet(UCAL_ORDINAL_MONTH, ordinal_month);
internalSet(UCAL_MONTH, month);
internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
@ -728,6 +738,49 @@ int32_t HebrewCalendar::defaultCenturyStartYear() const {
return gSystemDefaultCenturyStartYear;
}
bool HebrewCalendar::inTemporalLeapYear(UErrorCode& status) const {
if (U_FAILURE(status)) return false;
int32_t eyear = get(UCAL_EXTENDED_YEAR, status);
if (U_FAILURE(status)) return false;
return isLeapYear(eyear);
}
static const char * const gTemporalMonthCodesForHebrew[] = {
"M01", "M02", "M03", "M04", "M05", "M05L", "M06",
"M07", "M08", "M09", "M10", "M11", "M12", nullptr
};
const char* HebrewCalendar::getTemporalMonthCode(UErrorCode& status) const {
int32_t month = get(UCAL_MONTH, status);
if (U_FAILURE(status)) return nullptr;
return gTemporalMonthCodesForHebrew[month];
}
void HebrewCalendar::setTemporalMonthCode(const char* code, UErrorCode& status )
{
if (U_FAILURE(status)) return;
int32_t len = static_cast<int32_t>(uprv_strlen(code));
if (len == 3 || len == 4) {
for (int m = 0; gTemporalMonthCodesForHebrew[m] != nullptr; m++) {
if (uprv_strcmp(code, gTemporalMonthCodesForHebrew[m]) == 0) {
set(UCAL_MONTH, m);
return;
}
}
}
status = U_ILLEGAL_ARGUMENT_ERROR;
}
int32_t HebrewCalendar::internalGetMonth() const {
if (resolveFields(kMonthPrecedence) == UCAL_ORDINAL_MONTH) {
int32_t ordinalMonth = internalGet(UCAL_ORDINAL_MONTH);
HebrewCalendar *nonConstThis = (HebrewCalendar*)this; // cast away const
int32_t year = nonConstThis->handleGetExtendedYear();
return ordinalMonth + ((isLeapYear(year) && (ordinalMonth > ADAR_1)) ? 1: 0);
}
return Calendar::internalGetMonth();
}
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(HebrewCalendar)

View file

@ -399,6 +399,51 @@ public:
*/
virtual int32_t defaultCenturyStartYear() const override;
public:
/**
* Returns true if the date is in a leap year.
*
* @param status ICU Error Code
* @return True if the date in the fields is in a Temporal proposal
* defined leap year. False otherwise.
*/
virtual bool inTemporalLeapYear(UErrorCode& status) const override;
/**
* Gets The Temporal monthCode value corresponding to the month for the date.
* The value is a string identifier that starts with the literal grapheme
* "M" followed by two graphemes representing the zero-padded month number
* of the current month in a normal (non-leap) year and suffixed by an
* optional literal grapheme "L" if this is a leap month in a lunisolar
* calendar. For the Hebrew calendar, the values are "M01" .. "M12" for
* non-leap year, and "M01" .. "M05", "M05L", "M06" .. "M12" for leap year.
*
* @param status ICU Error Code
* @return One of 13 possible strings in {"M01".. "M05", "M05L",
* "M06" .. "M12"}.
* @draft ICU 73
*/
virtual const char* getTemporalMonthCode(UErrorCode& status) const override;
/**
* Sets The Temporal monthCode which is a string identifier that starts
* with the literal grapheme "M" followed by two graphemes representing
* the zero-padded month number of the current month in a normal
* (non-leap) year and suffixed by an optional literal grapheme "L" if this
* is a leap month in a lunisolar calendar. For Hebrew calendar, the values
* are "M01" .. "M12" for non-leap years, and "M01" .. "M05", "M05L", "M06"
* .. "M12" for leap year.
*
* @param temporalMonth The value to be set for temporal monthCode.
* @param status ICU Error Code
*
* @draft ICU 73
*/
virtual void setTemporalMonthCode(const char* code, UErrorCode& status ) override;
protected:
virtual int32_t internalGetMonth() const override;
private: // Calendar-specific implementation
/**
* Finds the day # of the first day in the given Hebrew year.

View file

@ -80,6 +80,7 @@ static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
{/*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
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
{ 0, 0, 11, 11}, // ORDINAL_MONTH
};
static const int32_t INDIAN_ERA_START = 78;
@ -293,6 +294,7 @@ void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& /* stat
internalSet(UCAL_EXTENDED_YEAR, IndianYear);
internalSet(UCAL_YEAR, IndianYear);
internalSet(UCAL_MONTH, IndianMonth);
internalSet(UCAL_ORDINAL_MONTH, IndianMonth);
internalSet(UCAL_DAY_OF_MONTH, IndianDayOfMonth);
internalSet(UCAL_DAY_OF_YEAR, yday + 1); // yday is 0-based
}

View file

@ -259,6 +259,7 @@ static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
{/*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
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
{ 0, 0, 11, 11}, // ORDINAL_MONTH
};
/**
@ -546,6 +547,7 @@ void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status)
internalSet(UCAL_YEAR, year);
internalSet(UCAL_EXTENDED_YEAR, year);
internalSet(UCAL_MONTH, month);
internalSet(UCAL_ORDINAL_MONTH, month);
internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
}
@ -631,6 +633,14 @@ int32_t IslamicCalendar::defaultCenturyStartYear() const
return gSystemDefaultCenturyStartYear;
}
bool
IslamicCalendar::inTemporalLeapYear(UErrorCode &status) const
{
int32_t days = getActualMaximum(UCAL_DAY_OF_YEAR, status);
if (U_FAILURE(status)) return false;
return days == 355;
}
U_CFUNC void U_CALLCONV
IslamicCalendar::initializeSystemDefaultCentury()
@ -757,6 +767,7 @@ void IslamicCivilCalendar::handleComputeFields(int32_t julianDay, UErrorCode &st
internalSet(UCAL_YEAR, year);
internalSet(UCAL_EXTENDED_YEAR, year);
internalSet(UCAL_MONTH, month);
internalSet(UCAL_ORDINAL_MONTH, month);
internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
}
@ -932,6 +943,7 @@ void IslamicUmalquraCalendar::handleComputeFields(int32_t julianDay, UErrorCode
internalSet(UCAL_YEAR, year);
internalSet(UCAL_EXTENDED_YEAR, year);
internalSet(UCAL_MONTH, month);
internalSet(UCAL_ORDINAL_MONTH, month);
internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
}

View file

@ -350,6 +350,15 @@ class U_I18N_API IslamicCalendar : public Calendar {
*/
virtual void setRelatedYear(int32_t year) override;
/**
* Returns true if the date is in a leap year.
*
* @param status ICU Error Code
* @return True if the date in the fields is in a Temporal proposal
* defined leap year. False otherwise.
*/
virtual bool inTemporalLeapYear(UErrorCode &status) const override;
private:
IslamicCalendar() = delete; // default constructor not implemented

View file

@ -219,7 +219,7 @@ void JapaneseCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status
//Calendar::timeToFields(theTime, quick, status);
GregorianCalendar::handleComputeFields(julianDay, status);
int32_t year = internalGet(UCAL_EXTENDED_YEAR); // Gregorian year
int32_t eraIdx = gJapaneseEraRules->getEraIndex(year, internalGet(UCAL_MONTH) + 1, internalGet(UCAL_DAY_OF_MONTH), status);
int32_t eraIdx = gJapaneseEraRules->getEraIndex(year, internalGetMonth() + 1, internalGet(UCAL_DAY_OF_MONTH), status);
internalSet(UCAL_ERA, eraIdx);
internalSet(UCAL_YEAR, year - gJapaneseEraRules->getStartYear(eraIdx, status) + 1);

View file

@ -58,6 +58,7 @@ static const int32_t kPersianCalendarLimits[UCAL_FIELD_COUNT][4] = {
{/*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
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
{ 0, 0, 11, 11}, // ORDINAL_MONTH
};
U_NAMESPACE_BEGIN
@ -229,6 +230,7 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*statu
internalSet(UCAL_YEAR, year);
internalSet(UCAL_EXTENDED_YEAR, year);
internalSet(UCAL_MONTH, month);
internalSet(UCAL_ORDINAL_MONTH, month);
internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
}

View file

@ -1349,6 +1349,66 @@ public:
*/
virtual UBool isWeekend(void) const;
#ifndef U_HIDE_DRAFT_API
/**
* Returns true if the date is in a leap year. Recalculate the current time
* field values if the time value has been changed by a call to * setTime().
* This method is semantically const, but may alter the object in memory.
* A "leap year" is a year that contains more days than other years (for
* solar or lunar calendars) or more months than other years (for lunisolar
* calendars like Hebrew or Chinese), as defined in the ECMAScript Temporal
* proposal.
*
* @param status ICU Error Code
* @return True if the date in the fields is in a Temporal proposal
* defined leap year. False otherwise.
* @draft ICU 73
*/
virtual bool inTemporalLeapYear(UErrorCode& status) const;
/**
* Gets The Temporal monthCode value corresponding to the month for the date.
* The value is a string identifier that starts with the literal grapheme
* "M" followed by two graphemes representing the zero-padded month number
* of the current month in a normal (non-leap) year and suffixed by an
* optional literal grapheme "L" if this is a leap month in a lunisolar
* calendar. The 25 possible values are "M01" .. "M13" and "M01L" .. "M12L".
* For the Hebrew calendar, the values are "M01" .. "M12" for non-leap year, and
* "M01" .. "M05", "M05L", "M06" .. "M12" for leap year.
* For the Chinese calendar, the values are "M01" .. "M12" for non-leap year and
* in leap year with another monthCode in "M01L" .. "M12L".
* For Coptic and Ethiopian calendar, the Temporal monthCode values for any
* years are "M01" to "M13".
*
* @param status ICU Error Code
* @return One of 25 possible strings in {"M01".."M13", "M01L".."M12L"}.
* @draft ICU 73
*/
virtual const char* getTemporalMonthCode(UErrorCode& status) const;
/**
* Sets The Temporal monthCode which is a string identifier that starts
* with the literal grapheme "M" followed by two graphemes representing
* the zero-padded month number of the current month in a normal
* (non-leap) year and suffixed by an optional literal grapheme "L" if this
* is a leap month in a lunisolar calendar. The 25 possible values are
* "M01" .. "M13" and "M01L" .. "M12L". For Hebrew calendar, the values are
* "M01" .. "M12" for non-leap years, and "M01" .. "M05", "M05L", "M06"
* .. "M12" for leap year.
* For the Chinese calendar, the values are "M01" .. "M12" for non-leap year and
* in leap year with another monthCode in "M01L" .. "M12L".
* For Coptic and Ethiopian calendar, the Temporal monthCode values for any
* years are "M01" to "M13".
*
* @param temporalMonth The value to be set for temporal monthCode.
* @param status ICU Error Code
*
* @draft ICU 73
*/
virtual void setTemporalMonthCode(const char* temporalMonth, UErrorCode& status);
#endif /* U_HIDE_DRAFT_API */
protected:
/**
@ -1489,6 +1549,31 @@ protected:
* @internal
*/
inline int32_t internalGet(UCalendarDateFields field) const {return fFields[field];}
/**
* Use this function instead of internalGet(UCAL_MONTH). The implementation
* check the timestamp of UCAL_MONTH and UCAL_ORDINAL_MONTH and use the
* one set later. The subclass should override it to conver the value of UCAL_ORDINAL_MONTH
* to UCAL_MONTH correctly if UCAL_ORDINAL_MONTH has higher priority.
*
* @return The value for the UCAL_MONTH.
* @internal
*/
virtual int32_t internalGetMonth() const;
/**
* Use this function instead of internalGet(UCAL_MONTH, defaultValue). The implementation
* check the timestamp of UCAL_MONTH and UCAL_ORDINAL_MONTH and use the
* one set later. The subclass should override it to conver the value of UCAL_ORDINAL_MONTH
* to UCAL_MONTH correctly if UCAL_ORDINAL_MONTH has higher priority.
*
* @param defaultValue a default value used if the UCAL_MONTH and
* UCAL_ORDINAL are both unset.
* @return The value for the UCAL_MONTH.
* @internal
*/
virtual int32_t internalGetMonth(int32_t defaultValue) const;
#endif /* U_HIDE_INTERNAL_API */
#ifndef U_HIDE_DEPRECATED_API
@ -1569,7 +1654,6 @@ protected:
*/
virtual int32_t getLimit(UCalendarDateFields field, ELimitType limitType) const;
/**
* Return the Julian day number of day before the first day of the
* given month in the given extended year. Subclasses should override
@ -1722,6 +1806,13 @@ protected:
*/
static const UFieldResolutionTable kDOWPrecedence[];
/**
* Precedence table for Months
* @see #resolveFields
* @internal
*/
static const UFieldResolutionTable kMonthPrecedence[];
/**
* Given a precedence table, return the newest field combination in
* the table, or UCAL_FIELD_COUNT if none is found.
@ -1749,7 +1840,7 @@ protected:
* match, then UCAL_FIELD_COUNT is returned.
* @internal
*/
UCalendarDateFields resolveFields(const UFieldResolutionTable *precedenceTable);
UCalendarDateFields resolveFields(const UFieldResolutionTable *precedenceTable) const;
#endif /* U_HIDE_INTERNAL_API */

View file

@ -442,6 +442,33 @@ enum UCalendarDateFields {
*/
UCAL_IS_LEAP_MONTH,
#ifndef U_HIDE_DRAFT_API
/**
* Field number indicating the month. This is a calendar-specific value.
* Differ from UCAL_MONTH, this value is continuous and unique within a
* year and range from 0 to 11 or 0 to 12 depending on how many months in a
* year, the calendar system has leap month or not, and in leap year or not.
* It is the ordinal position of that month in the corresponding year of
* the calendar. For Chinese, Dangi, and Hebrew calendar, the range is
* 0 to 11 in non-leap years and 0 to 12 in leap years. For Coptic and Ethiopian
* calendar, the range is always 0 to 12. For other calendars supported by
* ICU now, the range is 0 to 11. When the number of months in a year of the
* identified calendar is variable, a different UCAL_ORDINAL_MONTH value can
* be used for dates that are part of the same named month in different years.
* For example, in the Hebrew calendar, "1 Nisan 5781" is associated with
* UCAL_ORDINAL_MONTH value 6 while "1 Nisan 5782" is associated with
* UCAL_ORDINAL_MONTH value 7 because 5782 is a leap year and Nisan follows
* the insertion of Adar I. In Chinese calendar, "Year 4664 Month 6 Day 2"
* is associated with UCAL_ORDINAL_MONTH value 5 while "Year 4665 Month 6 Day 2"
* is associated with UCAL_ORDINAL_MONTH value 6 because 4665 is a leap year
* and there is an extra "Leap Month 5" which associated with UCAL_ORDINAL_MONTH
* value 5 before "Month 6" of year 4664.
*
* @draft ICU 73
*/
UCAL_ORDINAL_MONTH,
#endif // U_HIDE_DRAFT_API
/* Do not conditionalize the following with #ifndef U_HIDE_DEPRECATED_API,
* it is needed for layout of Calendar, DateFormat, and other objects */
#ifndef U_FORCE_HIDE_DEPRECATED_API
@ -449,7 +476,12 @@ enum UCalendarDateFields {
* One more than the highest normal UCalendarDateFields value.
* @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
*/
UCAL_FIELD_COUNT,
#ifdef U_HIDE_DRAFT_API
UCAL_FIELD_COUNT = UCAL_IS_LEAP_MONTH + 1,
#else // U_HIDE_DRAFT_API (for UCAL_ORDINAL_MONTH)
UCAL_FIELD_COUNT = UCAL_ORDINAL_MONTH + 1,
#endif // U_HIDE_DRAFT_API (for UCAL_ORDINAL_MONTH)
#endif // U_FORCE_HIDE_DEPRECATED_API
/**

File diff suppressed because it is too large Load diff

View file

@ -285,6 +285,53 @@ public: // package
int32_t minute, int32_t second, int32_t millisecond, int32_t zone_offset,
int32_t dst_offset, int32_t year_woy, int32_t dow_local, int32_t extended_year,
int32_t julian_day, int32_t milliseconds_in_day, int32_t is_leap_month);
void TestGregorianCalendarInTemporalLeapYear(void);
void TestChineseCalendarInTemporalLeapYear(void);
void TestDangiCalendarInTemporalLeapYear(void);
void TestHebrewCalendarInTemporalLeapYear(void);
void TestIslamicCalendarInTemporalLeapYear(void);
void TestIslamicCivilCalendarInTemporalLeapYear(void);
void TestIslamicUmalquraCalendarInTemporalLeapYear(void);
void TestIslamicRGSACalendarInTemporalLeapYear(void);
void TestIslamicTBLACalendarInTemporalLeapYear(void);
void TestPersianCalendarInTemporalLeapYear(void);
void TestIndianCalendarInTemporalLeapYear(void);
void TestTaiwanCalendarInTemporalLeapYear(void);
void TestJapaneseCalendarInTemporalLeapYear(void);
void TestBuddhistCalendarInTemporalLeapYear(void);
void TestCopticCalendarInTemporalLeapYear(void);
void TestEthiopicCalendarInTemporalLeapYear(void);
void TestEthiopicAmeteAlemCalendarInTemporalLeapYear(void);
void TestChineseCalendarGetTemporalMonthCode(void);
void TestDangiCalendarGetTemporalMonthCode(void);
void TestHebrewCalendarGetTemporalMonthCode(void);
void TestCopticCalendarGetTemporalMonthCode(void);
void TestEthiopicCalendarGetTemporalMonthCode(void);
void TestEthiopicAmeteAlemCalendarGetTemporalMonthCode(void);
void TestGregorianCalendarSetTemporalMonthCode(void);
void TestChineseCalendarSetTemporalMonthCode(void);
void TestHebrewCalendarSetTemporalMonthCode(void);
void TestCopticCalendarSetTemporalMonthCode(void);
void TestEthiopicCalendarSetTemporalMonthCode(void);
void TestMostCalendarsOrdinalMonthSet(void);
void TestChineseCalendarOrdinalMonthSet(void);
void TestDangiCalendarOrdinalMonthSet(void);
void TestHebrewCalendarOrdinalMonthSet(void);
void TestCalendarAddOrdinalMonth(void);
void TestCalendarRollOrdinalMonth(void);
void TestLimitsOrdinalMonth(void);
void TestActualLimitsOrdinalMonth(void);
void RunChineseCalendarInTemporalLeapYearTest(Calendar* cal);
void RunIslamicCalendarInTemporalLeapYearTest(Calendar* cal);
void Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(Calendar* cal);
void RunChineseCalendarGetTemporalMonthCode(Calendar* cal);
void RunCECalendarGetTemporalMonthCode(Calendar* cal);
};
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -1091,7 +1091,14 @@ void IntlCalendarTest::checkConsistency(const char* locale) {
" Gregorian(e=" + g->get(UCAL_ERA, status) + " " +
g->get(UCAL_YEAR, status) + "/" +
(g->get(UCAL_MONTH, status) + 1) + "/" +
g->get(UCAL_DATE, status) + ") ");
g->get(UCAL_DATE, status) + ") \n" +
" Calendar[" + base->getType() +
"](e=" + base->get(UCAL_ERA, status) + " " +
base->get(UCAL_YEAR, status) + "/" +
(base->get(UCAL_MONTH, status) + 1) + "/" +
base->get(UCAL_DATE, status) + ") ordinalMonth=" +
base->get(UCAL_ORDINAL_MONTH, status));
status.errIfFailureAndReset();
return;
}

View file

@ -107,6 +107,9 @@ static const Field names_UCalendarDateFields[] =
FIELD_NAME_STR( LEN_UCAL, UCAL_JULIAN_DAY ),
FIELD_NAME_STR( LEN_UCAL, UCAL_MILLISECONDS_IN_DAY ),
FIELD_NAME_STR( LEN_UCAL, UCAL_IS_LEAP_MONTH ),
#ifndef U_HIDE_DRAFT_API
FIELD_NAME_STR( LEN_UCAL, UCAL_ORDINAL_MONTH ),
#endif // U_HIDE_DRAFT_API
};