Skip to content

Commit

Permalink
ICU-22991 Reduce Calendar object size
Browse files Browse the repository at this point in the history
See #3327
  • Loading branch information
FrankYFTang committed Jan 8, 2025
1 parent 6f93c07 commit 9eafd8c
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 93 deletions.
12 changes: 6 additions & 6 deletions icu4c/source/i18n/basictz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,13 @@ BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
|| (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
&& (date + MILLIS_PER_YEAR > nextTransitionTime)) {

int32_t year, month, dom, dow, doy, mid;
int32_t year, mid;
int8_t month, dom, dow;
UDate d;

// Get local wall time for the next transition time
Grego::timeToFields(nextTransitionTime + initialRaw + initialDst,
year, month, dom, dow, doy, mid, status);
year, month, dom, dow, mid, status);
if (U_FAILURE(status)) return;
int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
// Create DOW rule
Expand Down Expand Up @@ -193,7 +194,7 @@ BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,

// Get local wall time for the next transition time
Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
year, month, dom, dow, doy, mid, status);
year, month, dom, dow, mid, status);
if (U_FAILURE(status)) return;
weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
// Generate another DOW rule
Expand Down Expand Up @@ -225,7 +226,7 @@ BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,

// Generate another DOW rule
Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
year, month, dom, dow, doy, mid, status);
year, month, dom, dow, mid, status);
if (U_FAILURE(status)) return;
weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
Expand Down Expand Up @@ -486,8 +487,7 @@ BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
}
} else {
// Calculate the transition year
int32_t year, month, dom, dow, doy, mid;
Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid, status);
int32_t year = Grego::timeToYear(tzt.getTime(), status);
if (U_FAILURE(status)) {
return;
}
Expand Down
3 changes: 1 addition & 2 deletions icu4c/source/i18n/calendar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1524,14 +1524,13 @@ void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode& ec) {
if (U_FAILURE(ec)) {
return;
}
int32_t gregorianDayOfWeekUnused;
if (uprv_add32_overflow(
julianDay, -kEpochStartAsJulianDay, &julianDay)) {
ec = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
Grego::dayToFields(julianDay, fGregorianYear, fGregorianMonth,
fGregorianDayOfMonth, gregorianDayOfWeekUnused,
fGregorianDayOfMonth,
fGregorianDayOfYear, ec);
}

Expand Down
8 changes: 2 additions & 6 deletions icu4c/source/i18n/chnsecal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,13 +369,9 @@ int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) != 0;
}

int32_t unusedMonth;
int32_t unusedDayOfWeek;
int32_t unusedDayOfMonth;
int32_t unusedDayOfYear;
Grego::dayToFields(newMoon, gyear, unusedMonth, unusedDayOfWeek, unusedDayOfMonth, unusedDayOfYear, status);
int32_t newMonthYear = Grego::dayToYear(newMoon, status);

struct MonthInfo monthInfo = computeMonthInfo(setting, gyear, newMoon, status);
struct MonthInfo monthInfo = computeMonthInfo(setting, newMonthYear, newMoon, status);
if (U_FAILURE(status)) {
return 0;
}
Expand Down
5 changes: 3 additions & 2 deletions icu4c/source/i18n/erarules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,9 @@ void EraRules::initCurrentEra() {
localMillis += (rawOffset + dstOffset);
}

int year, month0, dom, dow, doy, mid;
Grego::timeToFields(localMillis, year, month0, dom, dow, doy, mid, ec);
int32_t year, mid;
int8_t month0, dom;
Grego::timeToFields(localMillis, year, month0, dom, mid, ec);
if (U_FAILURE(ec)) return;
int currentEncodedDate = encodeDate(year, month0 + 1 /* changes to 1-base */, dom);
int eraIdx = numEras - 1;
Expand Down
98 changes: 73 additions & 25 deletions icu4c/source/i18n/gregoimp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,57 +117,105 @@ int64_t Grego::fieldsToDay(int32_t year, int32_t month, int32_t dom) {
return julian - JULIAN_1970_CE; // JD => epoch day
}

void Grego::dayToFields(int32_t day, int32_t& year, int32_t& month,
int32_t& dom, int32_t& dow, int32_t& doy, UErrorCode& status) {

void Grego::dayToFields(int32_t day, int32_t& year, int8_t& month,
int8_t& dom, int8_t& dow, int16_t& doy, UErrorCode& status) {
year = dayToYear(day, doy, status); // one-based doy
if (U_FAILURE(status)) return;

// Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
if (uprv_add32_overflow(day, JULIAN_1970_CE - JULIAN_1_CE, &day)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}

// Convert from the day number to the multiple radix
// representation. We use 400-year, 100-year, and 4-year cycles.
// For example, the 4-year cycle has 4 years + 1 leap day; giving
// 1461 == 365*4 + 1 days.
int32_t n400 = ClockMath::floorDivide(day, 146097, &doy); // 400-year cycle length
int32_t n100 = ClockMath::floorDivide(doy, 36524, &doy); // 100-year cycle length
int32_t n4 = ClockMath::floorDivide(doy, 1461, &doy); // 4-year cycle length
int32_t n1 = ClockMath::floorDivide(doy, 365, &doy);
year = 400*n400 + 100*n100 + 4*n4 + n1;
if (n100 == 4 || n1 == 4) {
doy = 365; // Dec 31 at end of 4- or 400-year cycle
} else {
++year;
}

UBool isLeap = isLeapYear(year);

// Gregorian day zero is a Monday.
dow = (day + 1) % 7;
dow += (dow < 0) ? (UCAL_SUNDAY + 7) : UCAL_SUNDAY;

// Common Julian/Gregorian calculation
int32_t correction = 0;
bool isLeap = isLeapYear(year);
int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
if (doy >= march1) {
if (doy > march1) {
correction = isLeap ? 1 : 2;
}
month = (12 * (doy + correction) + 6) / 367; // zero-based month
dom = doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)] + 1; // one-based DOM
month = (12 * (doy - 1 + correction) + 6) / 367; // zero-based month
dom = doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)]; // one-based DOM
}

int32_t Grego::dayToYear(int32_t day, UErrorCode& status) {
int16_t unusedDOY;
return dayToYear(day, unusedDOY, status);
}

int32_t Grego::dayToYear(int32_t day, int16_t& doy, UErrorCode& status) {
if (U_FAILURE(status)) return 0;
// Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
if (uprv_add32_overflow(day, JULIAN_1970_CE - JULIAN_1_CE, &day)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}

// Convert from the day number to the multiple radix
// representation. We use 400-year, 100-year, and 4-year cycles.
// For example, the 4-year cycle has 4 years + 1 leap day; giving
// 1461 == 365*4 + 1 days.
int32_t doy32;
int32_t n400 = ClockMath::floorDivide(day, 146097, &doy32); // 400-year cycle length
int32_t n100 = ClockMath::floorDivide(doy32, 36524, &doy32); // 100-year cycle length
int32_t n4 = ClockMath::floorDivide(doy32, 1461, &doy32); // 4-year cycle length
int32_t n1 = ClockMath::floorDivide(doy32, 365, &doy32);
int32_t year = 400*n400 + 100*n100 + 4*n4 + n1;
if (n100 == 4 || n1 == 4) {
doy = 365; // Dec 31 at end of 4- or 400-year cycle
} else {
doy = doy32;
++year;
}
doy++; // one-based doy
return year;
}

void Grego::dayToFields(int32_t day, int32_t& year, int8_t& month,
int8_t& dom, int8_t& dow, UErrorCode& status) {
int16_t unusedDOY;
dayToFields(day, year, month, dom, dow, unusedDOY, status);
}

void Grego::dayToFields(int32_t day, int32_t& year, int8_t& month,
int8_t& dom, int16_t& doy, UErrorCode& status) {
int8_t unusedDOW;
dayToFields(day, year, month, dom, unusedDOW, doy, status);
}

void Grego::timeToFields(UDate time, int32_t& year, int8_t& month,
int8_t& dom, int32_t& mid, UErrorCode& status) {
int8_t unusedDOW;
timeToFields(time, year, month, dom, unusedDOW, mid, status);
}

void Grego::timeToFields(UDate time, int32_t& year, int32_t& month,
int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid, UErrorCode& status) {
void Grego::timeToFields(UDate time, int32_t& year, int8_t& month,
int8_t& dom, int8_t& dow, int32_t& mid, UErrorCode& status) {
int16_t unusedDOY;
timeToFields(time, year, month, dom, dow, unusedDOY, mid, status);
}

void Grego::timeToFields(UDate time, int32_t& year, int8_t& month,
int8_t& dom, int8_t& dow, int16_t& doy, int32_t& mid, UErrorCode& status) {
if (U_FAILURE(status)) return;
double millisInDay;
double day = ClockMath::floorDivide(static_cast<double>(time), static_cast<double>(U_MILLIS_PER_DAY), &millisInDay);
mid = static_cast<int32_t>(millisInDay);
dayToFields(day, year, month, dom, dow, doy, status);
}

int32_t Grego::timeToYear(UDate time, UErrorCode& status) {
if (U_FAILURE(status)) return 0;
double millisInDay;
int32_t day = ClockMath::floorDivide(static_cast<double>(time), static_cast<double>(U_MILLIS_PER_DAY), &millisInDay);
return Grego::dayToYear(day, status);
}

int32_t Grego::dayOfWeek(int32_t day) {
int32_t dow;
ClockMath::floorDivide(day + int{UCAL_THURSDAY}, 7, &dow);
Expand Down
82 changes: 70 additions & 12 deletions icu4c/source/i18n/gregoimp.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,21 @@ class Grego {
* @param doy output parameter to receive day-of-year (1-based)
* @param status error code.
*/
static void dayToFields(int32_t day, int32_t& year, int32_t& month,
int32_t& dom, int32_t& dow, int32_t& doy, UErrorCode& status);
static void dayToFields(int32_t day, int32_t& year, int8_t& month,
int8_t& dom, int8_t& dow, int16_t& doy, UErrorCode& status);

/**
* Convert a 1970-epoch day number to proleptic Gregorian year,
* month, day-of-month, and day-of-week.
* @param day 1970-epoch day
* @param year output parameter to receive year
* @param month output parameter to receive month (0-based, 0==Jan)
* @param dom output parameter to receive day-of-month (1-based)
* @param doy output parameter to receive day-of-year (1-based)
* @param status error code.
*/
static void dayToFields(int32_t day, int32_t& year, int8_t& month,
int8_t& dom, int16_t& doy, UErrorCode& status);

/**
* Convert a 1970-epoch day number to proleptic Gregorian year,
Expand All @@ -223,8 +236,24 @@ class Grego {
* @param dow output parameter to receive day-of-week (1-based, 1==Sun)
* @param status error code.
*/
static inline void dayToFields(int32_t day, int32_t& year, int32_t& month,
int32_t& dom, int32_t& dow, UErrorCode& status);
static void dayToFields(int32_t day, int32_t& year, int8_t& month,
int8_t& dom, int8_t& dow, UErrorCode& status);

/**
* Convert a 1970-epoch day number to proleptic Gregorian year.
* @param day 1970-epoch day
* @param status error code.
* @return year.
*/
static int32_t dayToYear(int32_t day, UErrorCode& status);
/**
* Convert a 1970-epoch day number to proleptic Gregorian year.
* @param day 1970-epoch day
* @param doy output parameter to receive day-of-year (1-based)
* @param status error code.
* @return year.
*/
static int32_t dayToYear(int32_t day, int16_t& doy, UErrorCode& status);

/**
* Convert a 1970-epoch milliseconds to proleptic Gregorian year,
Expand All @@ -238,8 +267,43 @@ class Grego {
* @param mid output parameter to receive millis-in-day
* @param status error code.
*/
static void timeToFields(UDate time, int32_t& year, int32_t& month,
int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid, UErrorCode& status);
static void timeToFields(UDate time, int32_t& year, int8_t& month,
int8_t& dom, int8_t& dow, int16_t& doy, int32_t& mid, UErrorCode& status);

/**
* Convert a 1970-epoch milliseconds to proleptic Gregorian year,
* month, day-of-month, and day-of-week, day of year and millis-in-day.
* @param time 1970-epoch milliseconds
* @param year output parameter to receive year
* @param month output parameter to receive month (0-based, 0==Jan)
* @param dom output parameter to receive day-of-month (1-based)
* @param dow output parameter to receive day-of-week (1-based, 1==Sun)
* @param mid output parameter to receive millis-in-day
* @param status error code.
*/
static void timeToFields(UDate time, int32_t& year, int8_t& month,
int8_t& dom, int8_t& dow, int32_t& mid, UErrorCode& status);

/**
* Convert a 1970-epoch milliseconds to proleptic Gregorian year,
* month, day-of-month, and day-of-week, day of year and millis-in-day.
* @param time 1970-epoch milliseconds
* @param year output parameter to receive year
* @param month output parameter to receive month (0-based, 0==Jan)
* @param dom output parameter to receive day-of-month (1-based)
* @param mid output parameter to receive millis-in-day
* @param status error code.
*/
static void timeToFields(UDate time, int32_t& year, int8_t& month,
int8_t& dom, int32_t& mid, UErrorCode& status);

/**
* Convert a 1970-epoch milliseconds to proleptic Gregorian year.
* @param time 1970-epoch milliseconds
* @param status error code.
* @return year.
*/
static int32_t timeToYear(UDate time, UErrorCode& status);

/**
* Return the day of week on the 1970-epoch day
Expand Down Expand Up @@ -305,12 +369,6 @@ Grego::previousMonthLength(int y, int m) {
return (m > 0) ? monthLength(y, m-1) : 31;
}

inline void Grego::dayToFields(int32_t day, int32_t& year, int32_t& month,
int32_t& dom, int32_t& dow, UErrorCode& status) {
int32_t doy_unused;
dayToFields(day,year,month,dom,dow,doy_unused, status);
}

inline double Grego::julianDayToMillis(int32_t julian)
{
return (static_cast<double>(julian) - kEpochStartAsJulianDay) * kOneDay;
Expand Down
17 changes: 2 additions & 15 deletions icu4c/source/i18n/indiancal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,18 +142,6 @@ static double gregorianToJD(int32_t year, int32_t month, int32_t date) {
return Grego::fieldsToDay(year, month, date) + kEpochStartAsJulianDay - 0.5;
}

/*
* Returns the Gregorian Date corresponding to a given Julian Day
* Month is 0 based.
* @param jd The Julian Day
*/
static int32_t* jdToGregorian(double jd, int32_t gregorianDate[3], UErrorCode& status) {
int32_t gdow;
Grego::dayToFields(jd - kEpochStartAsJulianDay,
gregorianDate[0], gregorianDate[1], gregorianDate[2], gdow, status);
return gregorianDate;
}


//-------------------------------------------------------------------------
// Functions for converting from field values to milliseconds....
Expand Down Expand Up @@ -265,10 +253,9 @@ int32_t IndianCalendar::handleGetExtendedYear(UErrorCode& status) {
void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) {
double jdAtStartOfGregYear;
int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday;
int32_t gregorianYear; // Stores gregorian date corresponding to Julian day;
int32_t gd[3];
// Stores gregorian date corresponding to Julian day;
int32_t gregorianYear = Grego::dayToYear(julianDay - kEpochStartAsJulianDay, status);

gregorianYear = jdToGregorian(julianDay, gd, status)[0]; // Gregorian date for Julian day
if (U_FAILURE(status)) return;
IndianYear = gregorianYear - INDIAN_ERA_START; // Year in Saka era
jdAtStartOfGregYear = gregorianToJD(gregorianYear, 0, 1); // JD at start of Gregorian year
Expand Down
3 changes: 1 addition & 2 deletions icu4c/source/i18n/olsontz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -568,9 +568,8 @@ UBool OlsonTimeZone::useDaylightTime() const {
return finalZone->useDaylightTime();
}

int32_t year, month, dom, dow, doy, mid;
UErrorCode status = U_ZERO_ERROR;
Grego::timeToFields(current, year, month, dom, dow, doy, mid, status);
int32_t year = Grego::timeToYear(current, status);
U_ASSERT(U_SUCCESS(status));
if (U_FAILURE(status)) return false; // If error, just return false.

Expand Down
3 changes: 2 additions & 1 deletion icu4c/source/i18n/simpletz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,8 @@ SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingT
}

rawOffsetGMT = getRawOffset();
int32_t year, month, dom, dow, millis;
int32_t year, millis;
int8_t month, dom, dow;
double dday = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis);
if (dday > INT32_MAX || dday < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR;
Expand Down
3 changes: 2 additions & 1 deletion icu4c/source/i18n/timezone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,8 @@ void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
// (with 7 args) twice when local == true and DST is
// detected in the initial call.
for (int32_t pass=0; ; ++pass) {
int32_t year, month, dom, dow, millis;
int32_t year, millis;
int8_t month, dom, dow;
double day = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis);

// out of the range
Expand Down
Loading

0 comments on commit 9eafd8c

Please sign in to comment.