Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICU-22027 Add Temporal related Calendar API #2274

Merged
merged 1 commit into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
FrankYFTang marked this conversation as resolved.
Show resolved Hide resolved
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