ICU-22633 Test and fix int32_t overflow for Calendar set

Add test to set with INT32_MAX and INT32_MIN then call getTime()
and fix all the undefined errors.
This commit is contained in:
Frank Tang 2024-03-07 16:14:46 -08:00 committed by Frank Yung-Fong Tang
parent 18c7d48b3e
commit d43d216feb
26 changed files with 474 additions and 165 deletions

View file

@ -74,9 +74,12 @@ int32_t BuddhistCalendar::handleGetExtendedYear(UErrorCode& status)
if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
year = internalGet(UCAL_EXTENDED_YEAR, kGregorianEpoch);
} else {
// extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc
year = internalGet(UCAL_YEAR, kGregorianEpoch - kBuddhistEraStart)
+ kBuddhistEraStart;
// extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc
year = internalGet(UCAL_YEAR, kGregorianEpoch - kBuddhistEraStart);
if (uprv_add32_overflow(year, kBuddhistEraStart, &year)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
}
return year;
}

View file

@ -2929,7 +2929,7 @@ void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) {
if (U_FAILURE(status)) {
return;
}
validateField(field, 1, handleGetMonthLength(y, internalGetMonth()), status);
validateField(field, 1, handleGetMonthLength(y, internalGetMonth(status), status), status);
break;
case UCAL_DAY_OF_YEAR:
y = handleGetExtendedYear(status);
@ -3146,7 +3146,7 @@ void Calendar::computeTime(UErrorCode& status) {
UDate t = 0;
if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) || fStamp[UCAL_DST_OFFSET] >= ((int32_t)kMinimumUserStamp)) {
t = millis + millisInDay - (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET));
t = millis + millisInDay - internalGet(UCAL_ZONE_OFFSET) - internalGet(UCAL_DST_OFFSET);
} else {
// Compute the time zone offset and DST offset. There are two potential
// ambiguities here. We'll assume a 2:00 am (wall time) switchover time
@ -3416,12 +3416,21 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCo
int32_t month;
if(isSet(UCAL_MONTH) || isSet(UCAL_ORDINAL_MONTH)) {
month = internalGetMonth();
month = internalGetMonth(status);
if (U_FAILURE(status)) {
return 0;
}
} else {
month = getDefaultMonthInYear(year);
month = getDefaultMonthInYear(year, status);
if (U_FAILURE(status)) {
return 0;
}
}
int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth);
int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth, status);
if (U_FAILURE(status)) {
return 0;
}
if (bestField == UCAL_DAY_OF_MONTH) {
@ -3467,7 +3476,10 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCo
first += 7;
}
int32_t dowLocal = getLocalDOW();
int32_t dowLocal = getLocalDOW(status);
if (U_FAILURE(status)) {
return 0;
}
// Find the first target DOW (dowLocal) in the month or year.
// Actually, it may be just before the first of the month or year.
@ -3484,7 +3496,12 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCo
// negative.
int32_t dim = internalGet(UCAL_DAY_OF_WEEK_IN_MONTH, 1);
if (dim >= 0) {
date += 7*(dim - 1);
int32_t temp;
if (uprv_mul32_overflow(7, dim - 1, &temp) ||
uprv_add32_overflow(date, temp, &date)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
} else {
// Move date to the last of this day-of-week in this month,
@ -3493,9 +3510,18 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCo
// 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 = internalGetMonth(UCAL_JANUARY);
int32_t monthLength = handleGetMonthLength(year, m);
date += ((monthLength - date) / 7 + dim + 1) * 7;
int32_t m = internalGetMonth(UCAL_JANUARY, status);
int32_t monthLength = handleGetMonthLength(year, m, status);
if (U_FAILURE(status)) {
return 0;
}
int32_t temp;
if (uprv_add32_overflow((monthLength - date) / 7, dim+1, &temp) ||
uprv_mul32_overflow(temp, 7, &temp) ||
uprv_add32_overflow(date, temp, &date)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
}
} else {
#if defined (U_DEBUG_CAL)
@ -3510,7 +3536,10 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCo
// need to be sure to stay in 'real' year.
int32_t woy = internalGet(bestField);
int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, false); // jd of day before jan 1
int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, false, status); // jd of day before jan 1
if (U_FAILURE(status)) {
return 0;
}
int32_t nextFirst = julianDayToDayOfWeek(nextJulianDay + 1) - firstDayOfWeek;
if (nextFirst < 0) { // 0..6 ldow of Jan 1
@ -3553,7 +3582,12 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCo
}
// Now adjust for the week number.
testDate += 7 * (woy - 1);
int32_t weeks;
if (uprv_mul32_overflow(woy-1, 7, &weeks) ||
uprv_add32_overflow(weeks, testDate, &testDate)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
#if defined (U_DEBUG_CAL)
fprintf(stderr, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n",
@ -3561,7 +3595,10 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCo
#endif
if(julianDay+testDate > nextJulianDay) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1)
// Fire up the calculating engines.. retry YWOY = (year-1)
julianDay = handleComputeMonthStart(year-1, 0, false); // jd before Jan 1 of previous year
julianDay = handleComputeMonthStart(year-1, 0, false, status); // jd before Jan 1 of previous year
if (U_FAILURE(status)) {
return 0;
}
first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; // 0 based local dow of first week
if(first < 0) { // 0..6
@ -3587,14 +3624,24 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCo
}
// Now adjust for the week number.
date += 7 * (internalGet(bestField) - 1);
int32_t weeks = internalGet(bestField);
if (uprv_add32_overflow(weeks, -1, &weeks) ||
uprv_mul32_overflow(7, weeks, &weeks) ||
uprv_add32_overflow(date, weeks, &date)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
}
return julianDay + date;
if (uprv_add32_overflow(julianDay, date, &julianDay)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
return julianDay;
}
int32_t
Calendar::getDefaultMonthInYear(int32_t /*eyear*/)
Calendar::getDefaultMonthInYear(int32_t /*eyear*/, UErrorCode& /* status */)
{
return 0;
}
@ -3606,17 +3653,28 @@ Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/)
}
int32_t Calendar::getLocalDOW()
int32_t Calendar::getLocalDOW(UErrorCode& status)
{
// Get zero-based localized DOW, valid range 0..6. This is the DOW
if (U_FAILURE(status)) {
return 0;
}
// Get zero-based localized DOW, valid range 0..6. This is the DOW
// we are looking for.
int32_t dowLocal = 0;
switch (resolveFields(kDOWPrecedence)) {
case UCAL_DAY_OF_WEEK:
dowLocal = internalGet(UCAL_DAY_OF_WEEK) - fFirstDayOfWeek;
dowLocal = internalGet(UCAL_DAY_OF_WEEK);
if (uprv_add32_overflow(dowLocal, -fFirstDayOfWeek, &dowLocal)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
break;
case UCAL_DOW_LOCAL:
dowLocal = internalGet(UCAL_DOW_LOCAL) - 1;
dowLocal = internalGet(UCAL_DOW_LOCAL);
if (uprv_add32_overflow(dowLocal, -1, &dowLocal)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
break;
default:
break;
@ -3628,8 +3686,11 @@ int32_t Calendar::getLocalDOW()
return dowLocal;
}
int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy)
int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy, UErrorCode& status)
{
if (U_FAILURE(status)) {
return 0;
}
// We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine
// what year we fall in, so that other code can set it properly.
// (code borrowed from computeWeekFields and handleComputeJulianDay)
@ -3639,10 +3700,21 @@ int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t w
UCalendarDateFields bestField = resolveFields(kDatePrecedence); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields
// Now, a local DOW
int32_t dowLocal = getLocalDOW(); // 0..6
int32_t dowLocal = getLocalDOW(status); // 0..6
if (U_FAILURE(status)) {
return 0;
}
int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw
int32_t jan1Start = handleComputeMonthStart(yearWoy, 0, false);
int32_t nextJan1Start = handleComputeMonthStart(yearWoy+1, 0, false); // next year's Jan1 start
int32_t jan1Start = handleComputeMonthStart(yearWoy, 0, false, status);
int32_t yearWoyPlus1;
if (uprv_add32_overflow(yearWoy, 1, &yearWoyPlus1)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
int32_t nextJan1Start = handleComputeMonthStart(yearWoyPlus1, 0, false, status); // next year's Jan1 start
if (U_FAILURE(status)) {
return 0;
}
// At this point julianDay is the 0-based day BEFORE the first day of
// January 1, year 1 of the given calendar. If julianDay == 0, it
@ -3719,7 +3791,10 @@ int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t w
case UCAL_DATE:
{
int32_t m = internalGetMonth();
int32_t m = internalGetMonth(status);
if (U_FAILURE(status)) {
return 0;
}
if((m == 0) &&
(woy >= getLeastMaximum(UCAL_WEEK_OF_YEAR))) {
return yearWoy+1; // month 0, late woy = in the next year
@ -3743,16 +3818,19 @@ int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t w
}
}
int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const
int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const
{
return handleComputeMonthStart(extendedYear, month+1, true) -
handleComputeMonthStart(extendedYear, month, true);
return handleComputeMonthStart(extendedYear, month+1, true, status) -
handleComputeMonthStart(extendedYear, month, true, status);
}
int32_t Calendar::handleGetYearLength(int32_t eyear) const
{
return handleComputeMonthStart(eyear+1, 0, false) -
handleComputeMonthStart(eyear, 0, false);
UErrorCode status = U_ZERO_ERROR;
int32_t result = handleComputeMonthStart(eyear+1, 0, false, status) -
handleComputeMonthStart(eyear, 0, false, status);
U_ASSERT(U_SUCCESS(status));
return result;
}
int32_t
@ -3776,7 +3854,7 @@ Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
}
cal->setLenient(true);
cal->prepareGetActual(field,false,status);
result = handleGetMonthLength(cal->get(UCAL_EXTENDED_YEAR, status), cal->get(UCAL_MONTH, status));
result = handleGetMonthLength(cal->get(UCAL_EXTENDED_YEAR, status), cal->get(UCAL_MONTH, status), status);
delete cal;
}
break;
@ -4181,14 +4259,17 @@ 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);
int32_t Calendar::internalGetMonth(UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
return internalGet(UCAL_ORDINAL_MONTH);
if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
return internalGet(UCAL_MONTH, status);
}
return internalGet(UCAL_ORDINAL_MONTH, status);
}
int32_t Calendar::internalGetMonth(int32_t defaultValue) const {
int32_t Calendar::internalGetMonth(int32_t defaultValue, UErrorCode& /* status */) const {
if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
return internalGet(UCAL_MONTH, defaultValue);
}

View file

@ -77,7 +77,7 @@ CECalendar::operator=(const CECalendar& right)
//-------------------------------------------------------------------------
int64_t
CECalendar::handleComputeMonthStart(int32_t eyear,int32_t emonth, UBool /*useMonth*/) const
CECalendar::handleComputeMonthStart(int32_t eyear,int32_t emonth, UBool /*useMonth*/, UErrorCode& /*status*/) const
{
int64_t year64 = eyear;
// handle month > 12, < 0 (e.g. from add/set)

View file

@ -98,7 +98,7 @@ protected:
* Return JD of start of given month/extended year
* @internal
*/
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override;
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth, UErrorCode& status) const override;
/**
* Calculate the limit for a specified type of limit and field

View file

@ -256,8 +256,12 @@ int32_t ChineseCalendar::handleGetExtendedYear(UErrorCode& status) {
* whether or not the given month is a leap month.
* @stable ICU 2.8
*/
int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
int32_t thisStart = handleComputeMonthStart(extendedYear, month, true) -
int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const {
int32_t thisStart = handleComputeMonthStart(extendedYear, month, true, status);
if (U_FAILURE(status)) {
return 0;
}
thisStart = thisStart -
kEpochStartAsJulianDay + 1; // Julian day -> local days
int32_t nextStart = newMoonNear(thisStart + SYNODIC_GAP, true);
return nextStart - thisStart;
@ -338,16 +342,26 @@ const UFieldResolutionTable* ChineseCalendar::getFieldResolutionTable() const {
* day of the given month and year
* @stable ICU 2.8
*/
int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const {
int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth, UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
// If the month is out of range, adjust it into range, and
// modify the extended year value accordingly.
if (month < 0 || month > 11) {
double m = month;
eyear += (int32_t)ClockMath::floorDivide(m, 12.0, &m);
if (uprv_add32_overflow(eyear, ClockMath::floorDivide(m, 12.0, &m), &eyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
month = (int32_t)m;
}
int32_t gyear = eyear + fEpochYear - 1; // Gregorian year
int32_t gyear;
if (uprv_add32_overflow(eyear, fEpochYear - 1, &gyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
int32_t theNewYear = newYear(gyear);
int32_t newMoon = newMoonNear(theNewYear + month * 29, true);
@ -358,13 +372,14 @@ int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
// Clone the calendar so we don't mess with the real one.
LocalPointer<ChineseCalendar> work(clone());
if (work.isNull())
if (work.isNull()) {
return 0;
}
UErrorCode status = U_ZERO_ERROR;
work->computeGregorianFields(julianDay, status);
if (U_FAILURE(status))
if (U_FAILURE(status)) {
return 0;
}
// This will modify the MONTH and IS_LEAP_MONTH fields (only)
work->computeChineseFields(newMoon, work->getGregorianYear(),
@ -458,7 +473,11 @@ 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 = hasLeapMonthBetweenWinterSolstices ? 13 : 12; // Months in this year
int32_t newM = (m + amount) % n;
if (uprv_add32_overflow(amount, m, &amount)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
int32_t newM = amount % n;
if (newM < 0) {
newM += n;
}
@ -986,7 +1005,10 @@ ChineseCalendar::setTemporalMonthCode(const char* code, UErrorCode& status )
status = U_ILLEGAL_ARGUMENT_ERROR;
}
int32_t ChineseCalendar::internalGetMonth() const {
int32_t ChineseCalendar::internalGetMonth(UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
return internalGet(UCAL_MONTH);
}
@ -996,24 +1018,29 @@ int32_t ChineseCalendar::internalGetMonth() const {
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);
U_ASSERT(U_SUCCESS(status));
if (U_FAILURE(status)) {
return 0;
}
ChineseCalendar *nonConstThis = (ChineseCalendar*)this; // cast away const
nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, temp->get(UCAL_IS_LEAP_MONTH, status));
U_ASSERT(U_SUCCESS(status));
int32_t month = temp->get(UCAL_MONTH, status);
U_ASSERT(U_SUCCESS(status));
if (U_FAILURE(status)) {
return 0;
}
nonConstThis->internalSet(UCAL_MONTH, month);
return month;
}
int32_t ChineseCalendar::internalGetMonth(int32_t defaultValue) const {
int32_t ChineseCalendar::internalGetMonth(int32_t defaultValue, UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
if (resolveFields(kMonthPrecedence) == UCAL_MONTH) {
return internalGet(UCAL_MONTH, defaultValue);
}
return internalGetMonth();
return internalGetMonth(status);
}
U_NAMESPACE_END

View file

@ -207,8 +207,8 @@ class U_I18N_API ChineseCalendar : public Calendar {
protected:
virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override;
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override;
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override;
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const override;
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth, UErrorCode& status) const override;
virtual int32_t handleGetExtendedYear(UErrorCode& status) override;
virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override;
virtual const UFieldResolutionTable* getFieldResolutionTable() const override;
@ -286,9 +286,9 @@ 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(int32_t defaultValue, UErrorCode& status) const override;
virtual int32_t internalGetMonth() const override;
virtual int32_t internalGetMonth(UErrorCode& status) const override;
protected:
/**

View file

@ -51,6 +51,8 @@ U_NAMESPACE_BEGIN
// Constructors...
//-------------------------------------------------------------------------
const TimeZone* getDangiCalZoneAstroCalc(UErrorCode &status);
DangiCalendar::DangiCalendar(const Locale& aLocale, UErrorCode& success)
: ChineseCalendar(aLocale, DANGI_EPOCH_YEAR, getDangiCalZoneAstroCalc(success), success)
{
@ -136,7 +138,7 @@ static void U_CALLCONV initDangiCalZoneAstroCalc(UErrorCode &status) {
ucln_i18n_registerCleanup(UCLN_I18N_DANGI_CALENDAR, calendar_dangi_cleanup);
}
const TimeZone* DangiCalendar::getDangiCalZoneAstroCalc(UErrorCode &status) const {
const TimeZone* getDangiCalZoneAstroCalc(UErrorCode &status) {
umtx_initOnce(gDangiCalendarInitOnce, &initDangiCalZoneAstroCalc, status);
return gDangiCalendarZoneAstroCalc;
}

View file

@ -88,8 +88,6 @@ class DangiCalendar : public ChineseCalendar {
private:
const TimeZone* getDangiCalZoneAstroCalc(UErrorCode &status) const;
// UObject stuff
public:
/**

View file

@ -70,7 +70,12 @@ EthiopicCalendar::handleGetExtendedYear(UErrorCode& status)
if (internalGet(UCAL_ERA, AMETE_MIHRET) == AMETE_MIHRET) {
return internalGet(UCAL_YEAR, 1); // Default to year 1
}
return internalGet(UCAL_YEAR, 1) - AMETE_MIHRET_DELTA;
int32_t year = internalGet(UCAL_YEAR, 1);
if (uprv_add32_overflow(year, -AMETE_MIHRET_DELTA, &year)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
return year;
}
void
@ -194,8 +199,13 @@ EthiopicAmeteAlemCalendar::handleGetExtendedYear(UErrorCode& status)
if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
}
return internalGet(UCAL_YEAR, 1 + AMETE_MIHRET_DELTA)
- AMETE_MIHRET_DELTA; // Default to year 1 of Amelete Mihret
// Default to year 1 of Amelete Mihret
int32_t year = internalGet(UCAL_YEAR, 1 + AMETE_MIHRET_DELTA);
if (uprv_add32_overflow(year, -AMETE_MIHRET_DELTA, &year)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
return year;
}
void

View file

@ -538,14 +538,21 @@ int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField,
int64_t GregorianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month,
UBool /* useMonth */) const
UBool /* useMonth */, UErrorCode& status) const
{
if (U_FAILURE(status)) {
return 0;
}
GregorianCalendar *nonConstThis = (GregorianCalendar*)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) {
eyear += ClockMath::floorDivide(month, 12, &month);
if (uprv_add32_overflow(ClockMath::floorDivide(month, 12, &month),
eyear, &eyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
}
UBool isLeap = eyear%4 == 0;
@ -584,7 +591,7 @@ int64_t GregorianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month,
return julianDay;
}
int32_t GregorianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const
int32_t GregorianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& /* status */) const
{
// If the month is out of range, adjust it into range, and
// modify the extended year value accordingly.
@ -601,10 +608,10 @@ int32_t GregorianCalendar::handleGetYearLength(int32_t eyear) const {
int32_t
GregorianCalendar::monthLength(int32_t month) const
GregorianCalendar::monthLength(int32_t month, UErrorCode& status) const
{
int32_t year = internalGet(UCAL_EXTENDED_YEAR);
return handleGetMonthLength(year, month);
return handleGetMonthLength(year, month, status);
}
// -------------------------------------
@ -641,10 +648,12 @@ GregorianCalendar::validateFields() const
// specially.
if (isSet(UCAL_DATE)) {
int32_t date = internalGet(UCAL_DATE);
UErrorCode internalStatus = U_ZERO_ERROR;
if (date < getMinimum(UCAL_DATE) ||
date > monthLength(internalGetMonth())) {
date > monthLength(internalGetMonth(internalStatus), internalStatus) ||
U_FAILURE(internalStatus)) {
return false;
}
}
}
if (isSet(UCAL_DAY_OF_YEAR)) {
@ -821,7 +830,10 @@ GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& s
case UCAL_DAY_OF_MONTH:
case UCAL_WEEK_OF_MONTH:
{
int32_t max = monthLength(internalGetMonth());
int32_t max = monthLength(internalGetMonth(status), status);
if (U_FAILURE(status)) {
return;
}
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
@ -854,7 +866,11 @@ 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 (internalGetMonth() == UCAL_JANUARY) {
int32_t month = internalGetMonth(status);
if (U_FAILURE(status)) {
return;
}
if (month == UCAL_JANUARY) {
if (woy >= 52) {
isoDoy += handleGetYearLength(isoYear);
}
@ -1197,7 +1213,11 @@ int32_t GregorianCalendar::handleGetExtendedYear(UErrorCode& status) {
break;
case UCAL_YEAR_WOY:
year = handleGetExtendedYearFromWeekFields(internalGet(UCAL_YEAR_WOY), internalGet(UCAL_WEEK_OF_YEAR));
year = handleGetExtendedYearFromWeekFields(
internalGet(UCAL_YEAR_WOY), internalGet(UCAL_WEEK_OF_YEAR), status);
if (U_FAILURE(status)) {
return 0;
}
#if defined (U_DEBUG_CAL)
// if(internalGet(UCAL_YEAR_WOY) != year) {
fprintf(stderr, "%s:%d: hGEYFWF[%d,%d] -> %d\n",
@ -1212,14 +1232,17 @@ int32_t GregorianCalendar::handleGetExtendedYear(UErrorCode& status) {
return year;
}
int32_t GregorianCalendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy)
int32_t GregorianCalendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy, UErrorCode& status)
{
if (U_FAILURE(status)) {
return 0;
}
// convert year to extended form
int32_t era = internalGet(UCAL_ERA, AD);
if(era == BC) {
yearWoy = 1 - yearWoy;
}
return Calendar::handleGetExtendedYearFromWeekFields(yearWoy, woy);
return Calendar::handleGetExtendedYearFromWeekFields(yearWoy, woy, status);
}

View file

@ -508,7 +508,7 @@ int32_t HebrewCalendar::handleGetLimit(UCalendarDateFields field, ELimitType lim
* Returns the length of the given month in the given year
* @internal
*/
int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& /* status */) const {
// Resolve out-of-range months. This is necessary in order to
// obtain the correct year. We correct to
// a 12- or 13-month year (add/subtract 12 or 13, depending
@ -546,7 +546,7 @@ int32_t HebrewCalendar::handleGetYearLength(int32_t eyear) const {
void HebrewCalendar::validateField(UCalendarDateFields field, UErrorCode &status) {
if ((field == UCAL_MONTH || field == UCAL_ORDINAL_MONTH)
&& !isLeapYear(handleGetExtendedYear(status)) && internalGetMonth() == ADAR_1) {
&& !isLeapYear(handleGetExtendedYear(status)) && internalGetMonth(status) == ADAR_1) {
if (U_FAILURE(status)) {
return;
}
@ -656,8 +656,11 @@ int32_t HebrewCalendar::handleGetExtendedYear(UErrorCode& status ) {
* Return JD of start of given month/year.
* @internal
*/
int64_t HebrewCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const {
UErrorCode status = U_ZERO_ERROR;
int64_t HebrewCalendar::handleComputeMonthStart(
int32_t eyear, int32_t month, UBool /*useMonth*/, UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
// Resolve out-of-range months. This is necessary in order to
// obtain the correct year. We correct to
// a 12- or 13-month year (add/subtract 12 or 13, depending
@ -665,11 +668,19 @@ int64_t HebrewCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UB
// the leap year determines whether or not month 5 (Adar 1)
// is present, we allow 0..12 in any given year.
while (month < 0) {
month += monthsInYear(--eyear);
if (uprv_add32_overflow(eyear, -1, &eyear) ||
uprv_add32_overflow(month, monthsInYear(eyear), &month)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
}
// Careful: allow 0..12 in all years
while (month > 12) {
month -= monthsInYear(eyear++);
if (uprv_add32_overflow(month, -monthsInYear(eyear), &month) ||
uprv_add32_overflow(eyear, 1, &eyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
}
int64_t day = startOfYear(eyear, status);
@ -784,17 +795,26 @@ void HebrewCalendar::setTemporalMonthCode(const char* code, UErrorCode& status )
status = U_ILLEGAL_ARGUMENT_ERROR;
}
int32_t HebrewCalendar::internalGetMonth() const {
int32_t HebrewCalendar::internalGetMonth(UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
if (resolveFields(kMonthPrecedence) == UCAL_ORDINAL_MONTH) {
int32_t ordinalMonth = internalGet(UCAL_ORDINAL_MONTH);
HebrewCalendar *nonConstThis = (HebrewCalendar*)this; // cast away const
UErrorCode status = U_ZERO_ERROR;
int32_t year = nonConstThis->handleGetExtendedYear(status);
U_ASSERT(U_SUCCESS(status));
return ordinalMonth + (((!isLeapYear(year)) && (ordinalMonth > ADAR_1)) ? 1: 0);
if (U_FAILURE(status)) {
return 0;
}
if (isLeapYear(year) || ordinalMonth <= ADAR_1) {
return ordinalMonth;
}
if (!uprv_add32_overflow(ordinalMonth, 1, &ordinalMonth)) {
return ordinalMonth;
}
}
return Calendar::internalGetMonth();
return Calendar::internalGetMonth(status);
}
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(HebrewCalendar)

View file

@ -319,7 +319,7 @@ public:
* implementation than the default implementation in Calendar.
* @internal
*/
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override;
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const override;
/**
* Return the number of days in the given extended year of this
@ -369,7 +369,7 @@ public:
* @internal
*/
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month,
UBool useMonth) const override;
UBool useMonth, UErrorCode& status) const override;
/**
@ -443,7 +443,7 @@ public:
virtual void setTemporalMonthCode(const char* code, UErrorCode& status ) override;
protected:
virtual int32_t internalGetMonth() const override;
virtual int32_t internalGetMonth(UErrorCode& status) const override;
private: // Calendar-specific implementation
/**

View file

@ -108,7 +108,7 @@ static UBool isGregorianLeap(int32_t year)
* @param eyear The year in Saka Era
* @param month The month(0-based) in Indian calendar
*/
int32_t IndianCalendar::handleGetMonthLength(int32_t eyear, int32_t month) const {
int32_t IndianCalendar::handleGetMonthLength(int32_t eyear, int32_t month, UErrorCode& /* status */) const {
if (month < 0 || month > 11) {
eyear += ClockMath::floorDivide(month, 12, &month);
}
@ -203,14 +203,20 @@ static double IndianToJD(int32_t year, int32_t month, int32_t date) {
* @param eyear The year in Indian Calendar measured from Saka Era (78 AD).
* @param month The month in Indian calendar
*/
int64_t IndianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */ ) const {
int64_t IndianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */, UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
//month is 0 based; converting it to 1-based
int32_t imonth;
// If the month is out of range, adjust it into range, and adjust the extended year accordingly
if (month < 0 || month > 11) {
eyear += (int32_t)ClockMath::floorDivide(month, 12, &month);
if (uprv_add32_overflow(eyear, ClockMath::floorDivide(month, 12, &month), &eyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
}
if(month == 12){

View file

@ -209,7 +209,7 @@ public:
* @param year The month(0-based) in Indian year
* @internal
*/
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override;
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const override;
/**
* Return the number of days in the given Indian year
@ -225,7 +225,7 @@ public:
/**
* @internal
*/
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override;
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth, UErrorCode& status) const override;
//-------------------------------------------------------------------------
// Functions for converting from milliseconds to field values

View file

@ -321,8 +321,19 @@ int64_t IslamicCalendar::yearStart(int32_t year) const{
* @param year The hijri year
* @param month The hijri month, 0-based (assumed to be in range 0..11)
*/
int64_t IslamicCalendar::monthStart(int32_t year, int32_t month) const {
return trueMonthStart(12*(year-1) + month);
int64_t IslamicCalendar::monthStart(int32_t year, int32_t month, UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
int32_t temp;
if (uprv_add32_overflow(year, -1, &temp) ||
uprv_mul32_overflow(temp, 12, &temp) ||
uprv_add32_overflow(temp, month, &month)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
return trueMonthStart(month);
}
/**
@ -427,7 +438,8 @@ double IslamicCalendar::moonAge(UDate time, UErrorCode &status)
* @param year The hijri month, 0-based
* @draft ICU 2.4
*/
int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
UErrorCode& /* status */) const {
month = 12*(extendedYear-1) + month;
return trueMonthStart(month+1) - trueMonthStart(month) ;
}
@ -453,18 +465,30 @@ int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear) const {
/**
* @draft ICU 2.4
*/
int64_t IslamicCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */) const {
int64_t IslamicCalendar::handleComputeMonthStart(int32_t eyear, int32_t month,
UBool /* useMonth */,
UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
// This may be called by Calendar::handleComputeJulianDay with months out of the range
// 0..11. Need to handle that here since monthStart requires months in the range 0.11.
if (month > 11) {
eyear += (month / 12);
if (uprv_add32_overflow(eyear, (month / 12), &eyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
month %= 12;
} else if (month < 0) {
month++;
eyear += (month / 12) - 1;
if (uprv_add32_overflow(eyear, (month / 12) - 1, &eyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
month = (month % 12) + 11;
}
return monthStart(eyear, month) + getEpoc() - 1;
return monthStart(eyear, month, status) + getEpoc() - 1;
}
//-------------------------------------------------------------------------
@ -531,14 +555,20 @@ void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status)
int32_t year = month >= 0 ? ((month / 12) + 1) : ((month + 1 ) / 12);
month = ((month % 12) + 12 ) % 12;
int64_t dayOfMonth = (days - monthStart(year, month)) + 1;
int64_t dayOfMonth = (days - monthStart(year, month, status)) + 1;
if (U_FAILURE(status)) {
return;
}
if (dayOfMonth > INT32_MAX || dayOfMonth < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
// Now figure out the day of the year.
int64_t dayOfYear = (days - monthStart(year, 0)) + 1;
int64_t dayOfYear = (days - monthStart(year, 0, status)) + 1;
if (U_FAILURE(status)) {
return;
}
if (dayOfYear > INT32_MAX || dayOfYear < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
@ -697,10 +727,10 @@ int64_t IslamicCivilCalendar::yearStart(int32_t year) const{
* @param year The hijri year
* @param month The hijri month, 0-based (assumed to be in range 0..11)
*/
int64_t IslamicCivilCalendar::monthStart(int32_t year, int32_t month) const {
int64_t IslamicCivilCalendar::monthStart(int32_t year, int32_t month, UErrorCode& /*status*/) const {
// This does not handle months out of the range 0..11
return static_cast<int64_t>(
uprv_ceil(29.5*month) + 354LL*(year-1) +
uprv_ceil(29.5*month) + 354LL*(year-1LL) +
ClockMath::floorDivideInt64(
11LL*static_cast<int64_t>(year) + 3LL, 30LL));
}
@ -712,7 +742,8 @@ int64_t IslamicCivilCalendar::monthStart(int32_t year, int32_t month) const {
* @param year The hijri month, 0-based
* @draft ICU 2.4
*/
int32_t IslamicCivilCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
int32_t IslamicCivilCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
UErrorCode& /* status */) const {
int32_t length = 29 + (month+1) % 2;
if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) {
length++;
@ -755,14 +786,20 @@ void IslamicCivilCalendar::handleComputeFields(int32_t julianDay, UErrorCode &st
uprv_ceil((days - 29 - yearStart(year)) / 29.5 ));
month = month<11?month:11;
int64_t dayOfMonth = (days - monthStart(year, month)) + 1;
int64_t dayOfMonth = (days - monthStart(year, month, status)) + 1;
if (U_FAILURE(status)) {
return;
}
if (dayOfMonth > INT32_MAX || dayOfMonth < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
// Now figure out the day of the year.
int64_t dayOfYear = (days - monthStart(year, 0)) + 1;
int64_t dayOfYear = (days - monthStart(year, 0, status)) + 1;
if (U_FAILURE(status)) {
return;
}
if (dayOfYear > INT32_MAX || dayOfYear < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
@ -826,7 +863,7 @@ IslamicUmalquraCalendar* IslamicUmalquraCalendar::clone() const {
*/
int64_t IslamicUmalquraCalendar::yearStart(int32_t year) const {
if (year < UMALQURA_YEAR_START || year > UMALQURA_YEAR_END) {
return 354LL * (year-1) +
return 354LL * (year-1LL) +
ClockMath::floorDivideInt64((11LL*year+3LL), 30LL);
}
year -= UMALQURA_YEAR_START;
@ -844,10 +881,16 @@ int64_t IslamicUmalquraCalendar::yearStart(int32_t year) const {
* @param year The hijri year
* @param month The hijri month, 0-based (assumed to be in range 0..11)
*/
int64_t IslamicUmalquraCalendar::monthStart(int32_t year, int32_t month) const {
int64_t IslamicUmalquraCalendar::monthStart(int32_t year, int32_t month, UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
int64_t ms = yearStart(year);
for(int i=0; i< month; i++){
ms+= handleGetMonthLength(year, i);
ms+= handleGetMonthLength(year, i, status);
if (U_FAILURE(status)) {
return 0;
}
}
return ms;
}
@ -858,7 +901,8 @@ int64_t IslamicUmalquraCalendar::monthStart(int32_t year, int32_t month) const {
* @param year The hijri year
* @param year The hijri month, 0-based
*/
int32_t IslamicUmalquraCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
int32_t IslamicUmalquraCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
UErrorCode& /* status */) const {
int32_t length = 0;
if (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END) {
length = 29 + (month+1) % 2;
@ -880,9 +924,11 @@ int32_t IslamicUmalquraCalendar::handleGetYearLength(int32_t extendedYear) const
return 354 + (civilLeapYear(extendedYear) ? 1 : 0);
}
int len = 0;
UErrorCode internalStatus = U_ZERO_ERROR;
for(int i=0; i<12; i++) {
len += handleGetMonthLength(extendedYear, i);
len += handleGetMonthLength(extendedYear, i, internalStatus);
}
U_ASSERT(U_SUCCESS(internalStatus));
return len;
}
@ -933,10 +979,13 @@ void IslamicUmalquraCalendar::handleComputeFields(int32_t julianDay, UErrorCode
break;
}
if (d < yearLength){
int32_t monthLen = handleGetMonthLength(year, month);
int32_t monthLen = handleGetMonthLength(year, month, status);
for (month = 0;
d > monthLen;
monthLen = handleGetMonthLength(year, ++month)) {
monthLen = handleGetMonthLength(year, ++month, status)) {
if (U_FAILURE(status)) {
return;
}
d -= monthLen;
}
break;
@ -944,14 +993,20 @@ void IslamicUmalquraCalendar::handleComputeFields(int32_t julianDay, UErrorCode
}
}
dayOfMonth = (days - monthStart(year, month)) + 1;
dayOfMonth = (days - monthStart(year, month, status)) + 1;
if (U_FAILURE(status)) {
return;
}
if (dayOfMonth > INT32_MAX || dayOfMonth < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
// Now figure out the day of the year.
dayOfYear = (days - monthStart(year, 0)) + 1;
dayOfYear = (days - monthStart(year, 0, status)) + 1;
if (U_FAILURE(status)) {
return;
}
if (dayOfYear > INT32_MAX || dayOfYear < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;

View file

@ -210,7 +210,7 @@ class U_I18N_API IslamicCalendar : public Calendar {
* @param year The hijri year
* @param year The hijri month, 0-based
*/
virtual int64_t monthStart(int32_t year, int32_t month) const;
virtual int64_t monthStart(int32_t year, int32_t month, UErrorCode& status) const;
/**
* Find the day number on which a particular month of the true/lunar
@ -250,7 +250,7 @@ class U_I18N_API IslamicCalendar : public Calendar {
* @param year The hijri month, 0-based
* @internal
*/
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override;
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const override;
/**
* Return the number of days in the given Islamic year
@ -266,7 +266,7 @@ class U_I18N_API IslamicCalendar : public Calendar {
/**
* @internal
*/
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override;
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth, UErrorCode& status) const override;
//-------------------------------------------------------------------------
// Functions for converting from milliseconds to field values
@ -468,7 +468,7 @@ class U_I18N_API IslamicCivilCalendar : public IslamicCalendar {
* @param year The hijri month, 0-based
* @internal
*/
virtual int64_t monthStart(int32_t year, int32_t month) const override;
virtual int64_t monthStart(int32_t year, int32_t month, UErrorCode& status) const override;
/**
* Return the length (in days) of the given month.
@ -477,7 +477,7 @@ class U_I18N_API IslamicCivilCalendar : public IslamicCalendar {
* @param year The hijri month, 0-based
* @internal
*/
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override;
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const override;
/**
* Return the number of days in the given Islamic year
@ -651,7 +651,7 @@ class U_I18N_API IslamicUmalquraCalendar : public IslamicCalendar {
* @param year The hijri month, 0-based
* @internal
*/
virtual int64_t monthStart(int32_t year, int32_t month) const override;
virtual int64_t monthStart(int32_t year, int32_t month, UErrorCode& status) const override;
/**
* Return the length (in days) of the given month.
@ -660,7 +660,7 @@ class U_I18N_API IslamicUmalquraCalendar : public IslamicCalendar {
* @param year The hijri month, 0-based
* @internal
*/
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override;
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const override;
/**
* Return the number of days in the given Islamic year

View file

@ -146,8 +146,11 @@ const char *JapaneseCalendar::getType() const
return "japanese";
}
int32_t JapaneseCalendar::getDefaultMonthInYear(int32_t eyear)
int32_t JapaneseCalendar::getDefaultMonthInYear(int32_t eyear, UErrorCode& status)
{
if (U_FAILURE(status)) {
return 0;
}
int32_t era = internalGetEra();
// TODO do we assume we can trust 'era'? What if it is denormalized?
@ -155,9 +158,10 @@ int32_t JapaneseCalendar::getDefaultMonthInYear(int32_t eyear)
// Find out if we are at the edge of an era
int32_t eraStart[3] = { 0,0,0 };
UErrorCode status = U_ZERO_ERROR;
gJapaneseEraRules->getStartDate(era, eraStart, status);
U_ASSERT(U_SUCCESS(status));
if (U_FAILURE(status)) {
return 0;
}
if(eyear == eraStart[0]) {
// Yes, we're in the first year of this era.
return eraStart[1] // month
@ -210,9 +214,12 @@ int32_t JapaneseCalendar::handleGetExtendedYear(UErrorCode& status)
}
// extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc
year = internalGet(UCAL_YEAR, 1) // pin to minimum of year 1 (first year)
+ eraStartYear // add gregorian starting year
- 1; // Subtract one because year starts at 1
year = internalGet(UCAL_YEAR, 1); // pin to minimum of year 1 (first year)
// add gregorian starting year, subtract one because year starts at 1
if (uprv_add32_overflow(year, eraStartYear - 1, &year)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
}
return year;
}
@ -223,7 +230,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, internalGetMonth() + 1, internalGet(UCAL_DAY_OF_MONTH), status);
int32_t eraIdx = gJapaneseEraRules->getEraIndex(year, internalGetMonth(status) + 1, internalGet(UCAL_DAY_OF_MONTH), status);
internalSet(UCAL_ERA, eraIdx);
internalSet(UCAL_YEAR, year - gJapaneseEraRules->getStartYear(eraIdx, status) + 1);

View file

@ -212,7 +212,7 @@ protected:
* @param eyear the extended year
* @internal
*/
virtual int32_t getDefaultMonthInYear(int32_t eyear) override;
virtual int32_t getDefaultMonthInYear(int32_t eyear, UErrorCode& status) override;
/***
* Called by computeJulianDay. Returns the default day (1-based) for the month,

View file

@ -111,17 +111,16 @@ int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType li
*/
UBool PersianCalendar::isLeapYear(int32_t year)
{
int32_t remainder;
ClockMath::floorDivide(25 * year + 11, 33, &remainder);
return (remainder < 8);
int64_t y = static_cast<int64_t>(year) * 25LL + 11LL;
return (y % 33L < 8);
}
/**
* Return the day # on which the given year starts. Days are counted
* from the Persian epoch, origin 0.
*/
int32_t PersianCalendar::yearStart(int32_t year) {
return handleComputeMonthStart(year,0,false);
int32_t PersianCalendar::yearStart(int32_t year, UErrorCode& status) {
return handleComputeMonthStart(year,0,false, status);
}
/**
@ -131,8 +130,8 @@ int32_t PersianCalendar::yearStart(int32_t year) {
* @param year The Persian year
* @param year The Persian month, 0-based
*/
int32_t PersianCalendar::monthStart(int32_t year, int32_t month) const {
return handleComputeMonthStart(year,month,true);
int32_t PersianCalendar::monthStart(int32_t year, int32_t month, UErrorCode& status) const {
return handleComputeMonthStart(year,month,true, status);
}
//----------------------------------------------------------------------
@ -145,7 +144,7 @@ int32_t PersianCalendar::monthStart(int32_t year, int32_t month) const {
* @param year The Persian year
* @param year The Persian month, 0-based
*/
int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& /*status*/) const {
// If the month is out of range, adjust it into range, and
// modify the extended year value accordingly.
if (month < 0 || month > 11) {
@ -167,14 +166,20 @@ int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const {
//-------------------------------------------------------------------------
// Return JD of start of given month/year
int64_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const {
int64_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/, UErrorCode& status) const {
if (U_FAILURE(status)) {
return 0;
}
// If the month is out of range, adjust it into range, and
// modify the extended year value accordingly.
if (month < 0 || month > 11) {
eyear += ClockMath::floorDivide(month, 12, &month);
if (uprv_add32_overflow(eyear, ClockMath::floorDivide(month, 12, &month), &eyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
}
int64_t julianDay = PERSIAN_EPOCH - 1 + 365LL * (eyear - 1) + ClockMath::floorDivide(8LL * eyear + 21, 33);
int64_t julianDay = PERSIAN_EPOCH - 1LL + 365LL * (eyear - 1LL) + ClockMath::floorDivide(8LL * eyear + 21, 33);
if (month != 0) {
julianDay += kPersianNumDays[month];

View file

@ -176,7 +176,7 @@ class PersianCalendar : public Calendar {
* Return the day # on which the given year starts. Days are counted
* from the Hijri epoch, origin 0.
*/
int32_t yearStart(int32_t year);
int32_t yearStart(int32_t year, UErrorCode& status);
/**
* Return the day # on which the given month starts. Days are counted
@ -185,7 +185,7 @@ class PersianCalendar : public Calendar {
* @param year The hijri shamsi year
* @param year The hijri shamsi month, 0-based
*/
int32_t monthStart(int32_t year, int32_t month) const;
int32_t monthStart(int32_t year, int32_t month, UErrorCode& status) const;
//----------------------------------------------------------------------
// Calendar framework
@ -203,7 +203,7 @@ class PersianCalendar : public Calendar {
* @param year The hijri shamsi month, 0-based
* @internal
*/
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override;
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const override;
/**
* Return the number of days in the given Persian year
@ -219,7 +219,7 @@ class PersianCalendar : public Calendar {
/**
* @internal
*/
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override;
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth, UErrorCode& status) const override;
//-------------------------------------------------------------------------
// Functions for converting from milliseconds to field values

View file

@ -519,7 +519,12 @@ SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingT
rawOffsetGMT = getRawOffset();
int32_t year, month, dom, dow, millis;
int32_t day = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis);
double dday = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis);
if (dday > INT32_MAX || dday < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
int32_t day = dday;
Grego::dayToFields(day, year, month, dom, dow);

View file

@ -78,10 +78,17 @@ int32_t TaiwanCalendar::handleGetExtendedYear(UErrorCode& status)
year = internalGet(UCAL_EXTENDED_YEAR, kGregorianEpoch);
} else {
int32_t era = internalGet(UCAL_ERA, MINGUO);
year = internalGet(UCAL_YEAR, 1);
if(era == MINGUO) {
year = internalGet(UCAL_YEAR, 1) + kTaiwanEraStart;
if (uprv_add32_overflow(year, kTaiwanEraStart, &year)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
} else if(era == BEFORE_MINGUO) {
year = 1 - internalGet(UCAL_YEAR, 1) + kTaiwanEraStart;
if (uprv_add32_overflow(1 + kTaiwanEraStart, -year, &year)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
} else {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;

View file

@ -1558,7 +1558,7 @@ protected:
* @return The value for the UCAL_MONTH.
* @internal
*/
virtual int32_t internalGetMonth() const;
virtual int32_t internalGetMonth(UErrorCode& status) const;
/**
* Use this function instead of internalGet(UCAL_MONTH, defaultValue). The implementation
@ -1568,10 +1568,12 @@ protected:
*
* @param defaultValue a default value used if the UCAL_MONTH and
* UCAL_ORDINAL are both unset.
* @param status Output param set to failure code on function return
* when this function fails.
* @return The value for the UCAL_MONTH.
* @internal
*/
virtual int32_t internalGetMonth(int32_t defaultValue) const;
virtual int32_t internalGetMonth(int32_t defaultValue, UErrorCode& status) const;
#ifndef U_HIDE_DEPRECATED_API
/**
@ -1660,12 +1662,14 @@ protected:
* @param useMonth if false, compute the day before the first day of
* the given year, otherwise, compute the day before the first day of
* the given month
* @param status Output param set to failure code on function return
* when this function fails.
* @return the Julian day number of the day before the first
* day of the given month and year
* @internal
*/
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month,
UBool useMonth) const = 0;
UBool useMonth, UErrorCode& status) const = 0;
/**
* Return the number of days in the given month of the given extended
@ -1674,7 +1678,7 @@ protected:
* implementation than the default implementation in Calendar.
* @internal
*/
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const ;
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const ;
/**
* Return the number of days in the given extended year of this
@ -1716,7 +1720,7 @@ protected:
* @return the extended year, UCAL_EXTENDED_YEAR
* @internal
*/
virtual int32_t handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy);
virtual int32_t handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy, UErrorCode& status);
/**
* Validate a single field of this calendar. Subclasses should
@ -2022,9 +2026,11 @@ protected:
* Called by computeJulianDay. Returns the default month (0-based) for the year,
* taking year and era into account. Defaults to 0 for Gregorian, which doesn't care.
* @param eyear The extended year
* @param status Output param set to failure code on function return
* when this function fails.
* @internal
*/
virtual int32_t getDefaultMonthInYear(int32_t eyear) ;
virtual int32_t getDefaultMonthInYear(int32_t eyear, UErrorCode& status);
/**
@ -2155,7 +2161,7 @@ protected:
* returns the local DOW, valid range 0..6
* @internal
*/
int32_t getLocalDOW();
int32_t getLocalDOW(UErrorCode& status);
#endif /* U_HIDE_INTERNAL_API */
private:

View file

@ -483,12 +483,13 @@ public:
* @param useMonth if false, compute the day before the first day of
* the given year, otherwise, compute the day before the first day of
* the given month
* @param status Fill-in parameter which receives the status of this operation.
* @return the Julian day number of the day before the first
* day of the given month and year
* @internal
*/
virtual int64_t handleComputeMonthStart(int32_t eyear, int32_t month,
UBool useMonth) const override;
UBool useMonth, UErrorCode& status) const override;
/**
* Subclasses may override this. This method calls
@ -508,7 +509,7 @@ public:
* implementation than the default implementation in Calendar.
* @internal
*/
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override;
virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const override;
/**
* Return the number of days in the given extended year of this
@ -522,10 +523,11 @@ public:
/**
* return the length of the given month.
* @param month the given month.
* @param status Fill-in parameter which receives the status of this operation.
* @return the length of the given month.
* @internal
*/
virtual int32_t monthLength(int32_t month) const;
virtual int32_t monthLength(int32_t month, UErrorCode& status) const;
/**
* return the length of the month according to the given year.
@ -597,7 +599,7 @@ public:
* @return the extended year, UCAL_EXTENDED_YEAR
* @internal
*/
virtual int32_t handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy) override;
virtual int32_t handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy, UErrorCode& status) override;
/**

View file

@ -197,6 +197,7 @@ void CalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
TESTCASE_AUTO(Test22633PersianOverflow);
TESTCASE_AUTO(Test22633HebrewOverflow);
TESTCASE_AUTO(Test22633AMPMOverflow);
TESTCASE_AUTO(Test22633SetGetTimeOverflow);
TESTCASE_AUTO(TestAddOverflow);
@ -5719,6 +5720,56 @@ void CalendarTest::Test22633AMPMOverflow() {
assertTrue("Should return success", U_SUCCESS(status));
}
void CalendarTest::Test22633SetGetTimeOverflow() {
UErrorCode status = U_ZERO_ERROR;
Locale locale = Locale::getEnglish();
LocalPointer<StringEnumeration> values(
Calendar::getKeywordValuesForLocale("calendar", locale, false, status),
status);
assertTrue("Should return success", U_SUCCESS(status));
if (U_SUCCESS(status)) {
const char* value = nullptr;
while ((value = values->next(nullptr, status)) != nullptr && U_SUCCESS(status)) {
bool isHebrew = strcmp("hebrew", value) == 0;
locale.setKeywordValue("calendar", value, status);
assertTrue("Should return success", U_SUCCESS(status));
LocalPointer<Calendar> cal(Calendar::createInstance(*TimeZone::getGMT(), locale, status), status);
assertTrue("Should return success", U_SUCCESS(status));
assertTrue("Should return success", U_SUCCESS(status));
for (int32_t i = 0; i < UCAL_FIELD_COUNT; i++) {
status = U_ZERO_ERROR;
cal->clear();
cal->set(static_cast<UCalendarDateFields>(i), INT32_MAX);
cal->getTime(status);
status = U_ZERO_ERROR;
cal->clear();
cal->set(static_cast<UCalendarDateFields>(i), INT32_MIN);
cal->getTime(status);
if (!isHebrew) {
for (int32_t j = 0; j < UCAL_FIELD_COUNT; j++) {
status = U_ZERO_ERROR;
cal->clear();
cal->set(static_cast<UCalendarDateFields>(i), INT32_MAX);
cal->set(static_cast<UCalendarDateFields>(j), INT32_MAX);
cal->getTime(status);
status = U_ZERO_ERROR;
cal->clear();
cal->set(static_cast<UCalendarDateFields>(i), INT32_MIN);
cal->set(static_cast<UCalendarDateFields>(j), INT32_MIN);
cal->getTime(status);
}
}
}
status = U_ZERO_ERROR;
}
}
}
void CalendarTest::TestChineseCalendarComputeMonthStart() { // ICU-22639
UErrorCode status = U_ZERO_ERROR;
@ -5740,7 +5791,7 @@ void CalendarTest::TestChineseCalendarComputeMonthStart() { // ICU-22639
chinese.hasLeapMonthBetweenWinterSolstices);
assertEquals("monthStart", monthStart,
chinese.handleComputeMonthStart(eyear, 0, false));
chinese.handleComputeMonthStart(eyear, 0, false, status));
// Calling a const method must not haved changed the state of the object.
assertFalse("hasLeapMonthBetweenWinterSolstices [#2]",

View file

@ -340,6 +340,7 @@ public: // package
void Test22633PersianOverflow();
void Test22633HebrewOverflow();
void Test22633AMPMOverflow();
void Test22633SetGetTimeOverflow();
void verifyFirstDayOfWeek(const char* locale, UCalendarDaysOfWeek expected);
void TestFirstDayOfWeek();