Skip to content

Commit

Permalink
ICU-22991 Reduce unnecessary Grego calculation
Browse files Browse the repository at this point in the history
See #3329
  • Loading branch information
FrankYFTang authored and Squash Bot committed Jan 9, 2025
1 parent a8d9f47 commit 9b4aad0
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 48 deletions.
70 changes: 39 additions & 31 deletions icu4j/main/core/src/main/java/com/ibm/icu/impl/Grego.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,48 +107,55 @@ public static long fieldsToDay(int year, int month, int dom) {
* @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 int[] dayToFields(long day, int[] fields) {
if (fields == null || fields.length < 5) {
fields = new int[5];
}
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;

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) {
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;


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;
Expand All @@ -173,13 +180,16 @@ public static int[] timeToFields(long time, int[] fields) {
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
Expand All @@ -188,14 +198,12 @@ public static long floorDivide(long numerator, long denominator) {
((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)));
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,8 @@ public void setRawOffset(int offsetMillis) {
}
}

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
Expand Down Expand Up @@ -314,11 +312,11 @@ public boolean useDaylightTime() {
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.
Expand Down Expand Up @@ -1296,4 +1294,4 @@ public TimeZone cloneAsThawed() {
tz.isFrozen = false;
return tz;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,7 @@ public Date getFinalStart(int prevRawOffset, int prevDSTSavings) {
*/
@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);
}
Expand All @@ -209,8 +208,7 @@ public Date getNextStart(long base, int prevRawOffset, int prevDSTSavings, boole
*/
@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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,12 +342,10 @@ public TimeZoneRule[] getTimeZoneRules(long start) {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -823,14 +823,13 @@ private boolean parse() {
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);
}
Expand Down

0 comments on commit 9b4aad0

Please sign in to comment.