ICU-22991 Reduce unnecessary Grego calculation

See #3329
This commit is contained in:
Frank Tang 2025-01-09 00:32:11 +00:00 committed by Frank Yung-Fong Tang
parent ba5cf31f77
commit bd50f8be32
5 changed files with 49 additions and 48 deletions

View file

@ -107,48 +107,55 @@ public class Grego {
* @return the day of week
*/
public static int dayOfWeek(long day) {
long[] remainder = new long[1];
floorDivide(day + 5 /* Calendar.THURSDAY */, 7, remainder);
int dayOfWeek = (int)remainder[0];
Pair<Long, Integer> result = floorDivideAndRemainer(day + 5 /* Calendar.THURSDAY */, 7);
int dayOfWeek = result.second;
dayOfWeek = (dayOfWeek == 0) ? 7 : dayOfWeek;
return dayOfWeek;
}
public static Pair<Integer, Integer> dayToYear(long day) {
// Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
day += JULIAN_1970_CE - JULIAN_1_CE;
Pair<Long, Integer> n400 = floorDivideAndRemainer(day, 146097);
Pair<Long, Integer> n100 = floorDivideAndRemainer(n400.second, 36524);
Pair<Long, Integer> n4 = floorDivideAndRemainer(n100.second, 1461);
Pair<Long, Integer> n1 = floorDivideAndRemainer(n4.second, 365);
int year = (int)(400 * n400.first + 100 * n100.first + 4 * n4.first + n1.first);
int dayOfYear = n1.second;
if (n100.first == 4 || n1.first == 4) {
dayOfYear = 365; // Dec 31 at end of 4- or 400-yr cycle
}
else {
++year;
}
dayOfYear++; // 1-based day of year
return new Pair<Integer, Integer>(year, dayOfYear);
}
public static int[] dayToFields(long day, int[] fields) {
if (fields == null || fields.length < 5) {
fields = new int[5];
}
Pair<Integer, Integer> result = dayToYear(day);
int year = result.first;
int dayOfYear = result.second;
// Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
day += JULIAN_1970_CE - JULIAN_1_CE;
long[] rem = new long[1];
long n400 = floorDivide(day, 146097, rem);
long n100 = floorDivide(rem[0], 36524, rem);
long n4 = floorDivide(rem[0], 1461, rem);
long n1 = floorDivide(rem[0], 365, rem);
int year = (int)(400 * n400 + 100 * n100 + 4 * n4 + n1);
int dayOfYear = (int)rem[0];
if (n100 == 4 || n1 == 4) {
dayOfYear = 365; // Dec 31 at end of 4- or 400-yr cycle
}
else {
++year;
}
boolean isLeap = isLeapYear(year);
int correction = 0;
int march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
if (dayOfYear >= march1) {
if (dayOfYear > march1) {
correction = isLeap ? 1 : 2;
}
int month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month
int dayOfMonth = dayOfYear - DAYS_BEFORE[isLeap ? month + 12 : month] + 1; // one-based DOM
int month = (12 * (dayOfYear - 1 + correction) + 6) / 367; // zero-based month
int dayOfMonth = dayOfYear - DAYS_BEFORE[isLeap ? month + 12 : month]; // one-based DOM
int dayOfWeek = (int)((day + 2) % 7); // day 0 is Monday(2)
if (dayOfWeek < 1 /* Sunday */) {
dayOfWeek += 7;
}
dayOfYear++; // 1-based day of year
fields[0] = year;
fields[1] = month;
@ -173,13 +180,16 @@ public class Grego {
if (fields == null || fields.length < 6) {
fields = new int[6];
}
long[] remainder = new long[1];
long day = floorDivide(time, 24*60*60*1000 /* milliseconds per day */, remainder);
dayToFields(day, fields);
fields[5] = (int)remainder[0];
Pair<Long, Integer> result = floorDivideAndRemainer(time, 24*60*60*1000 /* milliseconds per day */);
dayToFields(result.first, fields);
fields[5] = result.second;
return fields;
}
public static int timeToYear(long time) {
return dayToYear(floorDivideAndRemainer(time, 24*60*60*1000 /* milliseconds per day */).first).first;
}
public static long floorDivide(long numerator, long denominator) {
// We do this computation in order to handle
// a numerator of Long.MIN_VALUE correctly
@ -188,14 +198,12 @@ public class Grego {
((numerator + 1) / denominator) - 1;
}
private static long floorDivide(long numerator, long denominator, long[] remainder) {
private static Pair<Long, Integer> floorDivideAndRemainer(long numerator, int denominator) {
if (numerator >= 0) {
remainder[0] = numerator % denominator;
return numerator / denominator;
return new Pair<Long, Integer>(floorDivide(numerator, denominator), (int)(numerator % denominator));
}
long quotient = ((numerator + 1) / denominator) - 1;
remainder[0] = numerator - (quotient * denominator);
return quotient;
long quotient = floorDivide(numerator, denominator);
return new Pair<Long, Integer>(quotient, (int)(numerator - (quotient * denominator)));
}
/*

View file

@ -234,10 +234,8 @@ public class OlsonTimeZone extends BasicTimeZone {
}
}
int[] fields = Grego.timeToFields(current, null);
finalStartYear = fields[0];
finalStartMillis = Grego.fieldsToDay(fields[0], 0, 1);
finalStartYear = Grego.timeToYear(current);
finalStartMillis = Grego.fieldsToDay(finalStartYear, 0, 1);
if (bDst) {
// we probably do not need to set start year of final rule
@ -314,11 +312,11 @@ public class OlsonTimeZone extends BasicTimeZone {
return (finalZone != null && finalZone.useDaylightTime());
}
int[] fields = Grego.timeToFields(current, null);
int year = Grego.timeToYear(current);
// Find start of this year, and start of next year
long start = Grego.fieldsToDay(fields[0], 0, 1) * SECONDS_PER_DAY;
long limit = Grego.fieldsToDay(fields[0] + 1, 0, 1) * SECONDS_PER_DAY;
long start = Grego.fieldsToDay(year, 0, 1) * SECONDS_PER_DAY;
long limit = Grego.fieldsToDay(year + 1, 0, 1) * SECONDS_PER_DAY;
// Return true if DST is observed at any time during the current
// year.
@ -1296,4 +1294,4 @@ public class OlsonTimeZone extends BasicTimeZone {
tz.isFrozen = false;
return tz;
}
}
}

View file

@ -191,8 +191,7 @@ public class AnnualTimeZoneRule extends TimeZoneRule {
*/
@Override
public Date getNextStart(long base, int prevRawOffset, int prevDSTSavings, boolean inclusive) {
int[] fields = Grego.timeToFields(base, null);
int year = fields[0];
int year = Grego.timeToYear(base);
if (year < startYear) {
return getFirstStart(prevRawOffset, prevDSTSavings);
}
@ -209,8 +208,7 @@ public class AnnualTimeZoneRule extends TimeZoneRule {
*/
@Override
public Date getPreviousStart(long base, int prevRawOffset, int prevDSTSavings, boolean inclusive) {
int[] fields = Grego.timeToFields(base, null);
int year = fields[0];
int year = Grego.timeToYear(base);
if (year > endYear) {
return getFinalStart(prevRawOffset, prevDSTSavings);
}

View file

@ -342,12 +342,10 @@ public abstract class BasicTimeZone extends TimeZone {
filteredRules.add(ar);
} else {
// Calculate the transition year
int[] dfields = new int[6];
Grego.timeToFields(tzt.getTime(), dfields);
// Recreate the rule
AnnualTimeZoneRule newar = new AnnualTimeZoneRule(ar.getName(),
ar.getRawOffset(), ar.getDSTSavings(),
ar.getRule(), dfields[0], ar.getEndYear());
ar.getRule(), Grego.timeToYear(tzt.getTime()), ar.getEndYear());
filteredRules.add(newar);
}
// Check if this is a final rule

View file

@ -823,14 +823,13 @@ public class VTimeZone extends BasicTimeZone {
DateTimeRule.UTC_TIME);
} else {
// Update the end year
int fields[] = Grego.timeToFields(start.getTime(), null);
newRule = new AnnualTimeZoneRule(
finalRule.getName(),
finalRule.getRawOffset(),
finalRule.getDSTSavings(),
finalRule.getRule(),
finalRule.getStartYear(),
fields[0]);
Grego.timeToYear(start.getTime()));
}
rules.set(finalRuleIdx, newRule);
}