ICU-22639 Clone the calendar so we don't mess with the real one.

This commit is contained in:
Fredrik Roubert 2024-01-17 10:42:47 +09:00 committed by Fredrik Roubert
parent ca53838b83
commit c5160765d4
4 changed files with 50 additions and 18 deletions

View file

@ -325,8 +325,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
// modify the extended year value accordingly.
if (month < 0 || month > 11) {
@ -341,32 +339,29 @@ int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
int32_t julianDay = newMoon + kEpochStartAsJulianDay;
// 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
int32_t isLeapMonth = useMonth ? saveIsLeapMonth : 0;
int32_t isLeapMonth = useMonth ? internalGet(UCAL_IS_LEAP_MONTH) : 0;
// Clone the calendar so we don't mess with the real one.
LocalPointer<ChineseCalendar> work(clone());
if (work.isNull())
return 0;
UErrorCode status = U_ZERO_ERROR;
nonConstThis->computeGregorianFields(julianDay, status);
work->computeGregorianFields(julianDay, status);
if (U_FAILURE(status))
return 0;
// This will modify the MONTH and IS_LEAP_MONTH fields (only)
nonConstThis->computeChineseFields(newMoon, getGregorianYear(),
getGregorianMonth(), false);
if (month != internalGet(UCAL_MONTH) ||
isLeapMonth != internalGet(UCAL_IS_LEAP_MONTH)) {
// This will modify the MONTH and IS_LEAP_MONTH fields (only)
work->computeChineseFields(newMoon, work->getGregorianYear(),
work->getGregorianMonth(), false);
if (month != work->internalGet(UCAL_MONTH) ||
isLeapMonth != work->internalGet(UCAL_IS_LEAP_MONTH)) {
newMoon = newMoonNear(newMoon + SYNODIC_GAP, true);
julianDay = newMoon + kEpochStartAsJulianDay;
}
nonConstThis->internalSet(UCAL_MONTH, saveMonth);
nonConstThis->internalSet(UCAL_ORDINAL_MONTH, saveOrdinalMonth);
nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, saveIsLeapMonth);
return julianDay - 1;
}

View file

@ -325,6 +325,10 @@ class U_I18N_API ChineseCalendar : public Calendar {
int32_t internalGetDefaultCenturyStartYear() const;
ChineseCalendar() = delete; // default constructor not implemented
#ifdef __CalendarTest__
friend void CalendarTest::TestChineseCalendarComputeMonthStart();
#endif
};
U_NAMESPACE_END

View file

@ -191,6 +191,8 @@ void CalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
TESTCASE_AUTO(TestRollWeekOfYear);
TESTCASE_AUTO(TestFirstDayOfWeek);
TESTCASE_AUTO(TestChineseCalendarComputeMonthStart);
TESTCASE_AUTO_END;
}
@ -5628,6 +5630,35 @@ void CalendarTest::TestFirstDayOfWeek() {
verifyFirstDayOfWeek("zxx", UCAL_MONDAY);
}
void CalendarTest::TestChineseCalendarComputeMonthStart() { // ICU-22639
UErrorCode status = U_ZERO_ERROR;
// An extended year for which hasLeapMonthBetweenWinterSolstices is true.
constexpr int32_t eyear = 4643;
constexpr int32_t monthStart = 2453764;
LocalPointer<Calendar> calendar(
Calendar::createInstance(Locale("en_US@calendar=chinese"), status),
status);
if (failure(status, "Calendar::createInstance")) return;
// This test case is a friend of ChineseCalendar and may access internals.
const ChineseCalendar& chinese =
*dynamic_cast<ChineseCalendar*>(calendar.getAlias());
// The initial value of hasLeapMonthBetweenWinterSolstices should be false.
assertFalse("hasLeapMonthBetweenWinterSolstices [#1]",
chinese.hasLeapMonthBetweenWinterSolstices);
assertEquals("monthStart", monthStart,
chinese.handleComputeMonthStart(eyear, 0, false));
// Calling a const method must not haved changed the state of the object.
assertFalse("hasLeapMonthBetweenWinterSolstices [#2]",
chinese.hasLeapMonthBetweenWinterSolstices);
}
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof

View file

@ -337,6 +337,8 @@ public: // package
void verifyFirstDayOfWeek(const char* locale, UCalendarDaysOfWeek expected);
void TestFirstDayOfWeek();
void TestChineseCalendarComputeMonthStart();
void RunChineseCalendarInTemporalLeapYearTest(Calendar* cal);
void RunIslamicCalendarInTemporalLeapYearTest(Calendar* cal);
void Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(Calendar* cal);