Skip to content

Commit

Permalink
ICU-22027 Add C++ Calendar API for Temporal
Browse files Browse the repository at this point in the history
  • Loading branch information
FrankYFTang committed Jan 17, 2023
1 parent a7f4531 commit cd1b772
Show file tree
Hide file tree
Showing 21 changed files with 2,214 additions and 340 deletions.
110 changes: 95 additions & 15 deletions icu4c/source/i18n/calendar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -2700,7 +2709,6 @@ int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) cons
}
}


int32_t
Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const
{
Expand Down Expand Up @@ -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;
}

// -------------------------------------

/**
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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[] =
{
{
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions icu4c/source/i18n/cecal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "cecal.h"
#include "gregoimp.h" //Math
#include "cstring.h"

U_NAMESPACE_BEGIN

Expand Down Expand Up @@ -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
};

//-------------------------------------------------------------------------
Expand Down Expand Up @@ -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 */
Expand Down
30 changes: 30 additions & 0 deletions icu4c/source/i18n/cecal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down
Loading

0 comments on commit cd1b772

Please sign in to comment.