Skip to content

Commit 97c2c33

Browse files
committed
ICU-23102 Refactor Chinese Calendar to avoid save/restore
1 parent 1c1533b commit 97c2c33

File tree

1 file changed

+59
-73
lines changed

1 file changed

+59
-73
lines changed

icu4j/main/core/src/main/java/com/ibm/icu/util/ChineseCalendar.java

Lines changed: 59 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public class ChineseCalendar extends Calendar {
128128
* 1813 and 2033, the leap month is after the Winter Solstice of that year. So
129129
* this value could be false for a date prior to the Winter Solstice of that
130130
* year but that year still has a leap month and therefor is a leap year.
131-
* @see #computeChineseFields
131+
* @see #computeMonthInfo
132132
*/
133133
private transient boolean hasLeapMonthBetweenWinterSolstices;
134134

@@ -823,31 +823,62 @@ private boolean isLeapMonthBetween(int newMoon1, int newMoon2) {
823823
* @stable ICU 2.8
824824
*/
825825
protected void handleComputeFields(int julianDay) {
826+
int days = julianDay - EPOCH_JULIAN_DAY; // local days
827+
int gyear = getGregorianYear();
828+
int gmonth = getGregorianMonth();
829+
MonthInfo info = computeMonthInfo(days, gyear);
830+
831+
// Extended year and cycle year is based on the epoch year
832+
int extended_year = gyear - epochYear;
833+
int cycle_year = gyear - CHINESE_EPOCH_YEAR;
834+
if (info.month < 10 ||
835+
gmonth >= JULY) {
836+
extended_year++;
837+
cycle_year++;
838+
}
839+
int dayOfMonth = days - info.thisMoon + 1;
840+
841+
// 0->0,60 1->1,1 60->1,60 61->2,1 etc.
842+
int[] yearOfCycle = new int[1];
843+
int cycle = floorDivide(cycle_year-1, 60, yearOfCycle);
844+
845+
// Days will be before the first new year we compute if this
846+
// date is in month 11, leap 11, 12. There is never a leap 12.
847+
// New year computations are cached so this should be cheap in
848+
// the long run.
849+
int newYear = newYear(gyear);
850+
if (days < newYear) {
851+
newYear = newYear(gyear-1);
852+
}
826853

827-
computeChineseFields(julianDay - EPOCH_JULIAN_DAY, // local days
828-
getGregorianYear(), getGregorianMonth(),
829-
true); // set all fields
854+
hasLeapMonthBetweenWinterSolstices = info.hasLeapMonthBetweenWinterSolstices;
855+
internalSet(EXTENDED_YEAR, extended_year);
856+
internalSet(ERA, cycle+1);
857+
internalSet(YEAR, yearOfCycle[0]+1);
858+
internalSet(MONTH, info.month); // Convert from 1-based to 0-based
859+
internalSet(ORDINAL_MONTH, info.ordinalMonth);
860+
internalSet(DAY_OF_MONTH, dayOfMonth);
861+
internalSet(IS_LEAP_MONTH, info.isLeapMonth?1:0);
862+
internalSet(DAY_OF_YEAR, days - newYear + 1);
830863
}
831864

832-
/**
833-
* Compute fields for the Chinese calendar system. This method can
834-
* either set all relevant fields, as required by
835-
* <code>handleComputeFields()</code>, or it can just set the MONTH and
836-
* IS_LEAP_MONTH fields, as required by
837-
* <code>handleComputeMonthStart()</code>.
838-
*
839-
* <p>As a side effect, this method sets {@link #hasLeapMonthBetweenWinterSolstices}.
840-
* @param days days after January 1, 1970 0:00 astronomical base zone of the
841-
* date to compute fields for
842-
* @param gyear the Gregorian year of the given date
843-
* @param gmonth the Gregorian month of the given date
844-
* @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR,
845-
* DAY_OF_MONTH, and DAY_OF_YEAR fields. In either case set the MONTH
846-
* and IS_LEAP_MONTH fields.
847-
*/
848-
private void computeChineseFields(int days, int gyear, int gmonth,
849-
boolean setAllFields) {
865+
class MonthInfo {
866+
int month;
867+
int ordinalMonth;
868+
int thisMoon;
869+
boolean isLeapMonth;
870+
boolean hasLeapMonthBetweenWinterSolstices;
871+
MonthInfo(int month, int ordinalMonth, int thisMoon, boolean isLeapMonth,
872+
boolean hasLeapMonthBetweenWinterSolstices) {
873+
this.month = month;
874+
this.ordinalMonth = ordinalMonth;
875+
this.thisMoon = thisMoon;
876+
this.isLeapMonth = isLeapMonth;
877+
this.hasLeapMonthBetweenWinterSolstices = hasLeapMonthBetweenWinterSolstices;
878+
}
879+
};
850880

881+
private MonthInfo computeMonthInfo(int days, int gyear) {
851882
// Find the winter solstices before and after the target date.
852883
// These define the boundaries of this Chinese year, specifically,
853884
// the position of month 11, which always contains the solstice.
@@ -867,8 +898,8 @@ private void computeChineseFields(int days, int gyear, int gmonth,
867898
int firstMoon = newMoonNear(solsticeBefore + 1, true);
868899
int lastMoon = newMoonNear(solsticeAfter + 1, false);
869900
int thisMoon = newMoonNear(days + 1, false); // Start of this month
870-
// Note: hasLeapMonthBetweenWinterSolstices is a member variable
871-
hasLeapMonthBetweenWinterSolstices = synodicMonthsBetween(firstMoon, lastMoon) == 12;
901+
902+
boolean hasLeapMonthBetweenWinterSolstices = synodicMonthsBetween(firstMoon, lastMoon) == 12;
872903

873904
int month = synodicMonthsBetween(firstMoon, thisMoon);
874905
int theNewYear = newYear(gyear);
@@ -890,42 +921,7 @@ private void computeChineseFields(int days, int gyear, int gmonth,
890921
hasNoMajorSolarTerm(thisMoon) &&
891922
!isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, false));
892923

893-
internalSet(MONTH, month-1); // Convert from 1-based to 0-based
894-
internalSet(ORDINAL_MONTH, ordinalMonth);
895-
internalSet(IS_LEAP_MONTH, isLeapMonth?1:0);
896-
897-
if (setAllFields) {
898-
899-
// Extended year and cycle year is based on the epoch year
900-
int extended_year = gyear - epochYear;
901-
int cycle_year = gyear - CHINESE_EPOCH_YEAR;
902-
if (month < 11 ||
903-
gmonth >= JULY) {
904-
extended_year++;
905-
cycle_year++;
906-
}
907-
int dayOfMonth = days - thisMoon + 1;
908-
909-
internalSet(EXTENDED_YEAR, extended_year);
910-
911-
// 0->0,60 1->1,1 60->1,60 61->2,1 etc.
912-
int[] yearOfCycle = new int[1];
913-
int cycle = floorDivide(cycle_year-1, 60, yearOfCycle);
914-
internalSet(ERA, cycle+1);
915-
internalSet(YEAR, yearOfCycle[0]+1);
916-
917-
internalSet(DAY_OF_MONTH, dayOfMonth);
918-
919-
// Days will be before the first new year we compute if this
920-
// date is in month 11, leap 11, 12. There is never a leap 12.
921-
// New year computations are cached so this should be cheap in
922-
// the long run.
923-
int newYear = newYear(gyear);
924-
if (days < newYear) {
925-
newYear = newYear(gyear-1);
926-
}
927-
internalSet(DAY_OF_YEAR, days - newYear + 1);
928-
}
924+
return new MonthInfo(month-1, ordinalMonth, thisMoon, isLeapMonth, hasLeapMonthBetweenWinterSolstices);
929925
}
930926

931927
//------------------------------------------------------------------
@@ -999,27 +995,17 @@ private int handleComputeMonthStartWithLeap(int eyear, int month, int isLeapMont
999995

1000996
int julianDay = newMoon + EPOCH_JULIAN_DAY;
1001997

1002-
// Save fields for later restoration
1003-
int saveMonth = internalGet(MONTH);
1004-
int saveOrdinalMonth = internalGet(ORDINAL_MONTH);
1005-
int saveIsLeapMonth = internalGet(IS_LEAP_MONTH);
1006-
1007998
computeGregorianFields(julianDay);
1008999

10091000
// This will modify the MONTH and IS_LEAP_MONTH fields (only)
1010-
computeChineseFields(newMoon, getGregorianYear(),
1011-
getGregorianMonth(), false);
1001+
MonthInfo info = computeMonthInfo(newMoon, getGregorianYear());
10121002

1013-
if (month != internalGet(MONTH) ||
1014-
isLeapMonth != internalGet(IS_LEAP_MONTH)) {
1003+
if (month != info.month ||
1004+
info.isLeapMonth != (isLeapMonth != 0) ) {
10151005
newMoon = newMoonNear(newMoon + SYNODIC_GAP, true);
10161006
julianDay = newMoon + EPOCH_JULIAN_DAY;
10171007
}
10181008

1019-
internalSet(MONTH, saveMonth);
1020-
internalSet(ORDINAL_MONTH, saveOrdinalMonth);
1021-
internalSet(IS_LEAP_MONTH, saveIsLeapMonth);
1022-
10231009
return julianDay - 1;
10241010
}
10251011

0 commit comments

Comments
 (0)