ICU-22407 Implement Java Temporal Calendar API

See #2526
This commit is contained in:
Frank Tang 2023-07-28 05:38:30 +00:00 committed by Frank Yung-Fong Tang
parent 1f07d2b29f
commit d511cad90d
14 changed files with 1668 additions and 23 deletions

View file

@ -45,6 +45,8 @@ abstract class CECalendar extends Calendar {
{ -5000000, -5000000, 5000000, 5000000 }, // EXTENDED_YEAR
{/* */}, // JULIAN_DAY
{/* */}, // MILLISECONDS_IN_DAY
{/* */}, // IS_LEAP_YEAR
{ 0, 0, 12, 12 }, // ORDINAL_MONTH
};
//-------------------------------------------------------------------------
@ -273,4 +275,45 @@ abstract class CECalendar extends Calendar {
// day
fields[2] = (doy % 30) + 1; // 1-based days in a month
}
//-------------------------------------------------------------------------
// Temporal Calendar API.
//-------------------------------------------------------------------------
/**
* Gets The Temporal monthCode value corresponding to the month for the date.
* The value is a string identifier that starts with the literal grapheme
* "M" followed by two graphemes representing the zero-padded month number
* of the current month in a normal (non-leap) year. For the short thirteen
* month in each year in the CECalendar, the value is "M13".
*
* @return One of 13 possible strings in {"M01".. "M12", "M13"}.
* @draft ICU 74
*/
public String getTemporalMonthCode() {
if (get(MONTH) == 12) return "M13";
return super.getTemporalMonthCode();
}
/**
* Sets The Temporal monthCode which is a string identifier that starts
* with the literal grapheme "M" followed by two graphemes representing
* the zero-padded month number of the current month in a normal
* (non-leap) year. For CECalendar calendar, the values are "M01" .. "M13"
* while the "M13" is represent the short thirteen month in each year.
* @param temporalMonth One of 13 possible strings in {"M01".. "M12", "M13"}.
* @draft ICU 74
*/
public void setTemporalMonthCode( String temporalMonth ) {
if (temporalMonth.equals("M13")) {
set(MONTH, 12);
set(IS_LEAP_MONTH, 0);
return;
}
super.setTemporalMonthCode(temporalMonth);
}
//-------------------------------------------------------------------------
// End of Temporal Calendar API
//-------------------------------------------------------------------------
}

View file

@ -957,13 +957,37 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
*/
public static final int IS_LEAP_MONTH = 22;
/**
* {@icu} Field indicating the month. This is a calendar-specific value.
* Differ from MONTH, this value is continuous and unique within a
* year and range from 0 to 11 or 0 to 12 depending on how many months in a
* year, the calendar system has leap month or not, and in leap year or not.
* It is the ordinal position of that month in the corresponding year of
* the calendar. For Chinese, Dangi, and Hebrew calendar, the range is
* 0 to 11 in non-leap years and 0 to 12 in leap years. For Coptic and Ethiopian
* calendar, the range is always 0 to 12. For other calendars supported by
* ICU now, the range is 0 to 11. When the number of months in a year of the
* identified calendar is variable, a different ORDINAL_MONTH value can
* be used for dates that are part of the same named month in different years.
* For example, in the Hebrew calendar, "1 Nisan 5781" is associated with
* ORDINAL_MONTH value 6 while "1 Nisan 5782" is associated with
* ORDINAL_MONTH value 7 because 5782 is a leap year and Nisan follows
* the insertion of Adar I. In Chinese calendar, "Year 4664 Month 6 Day 2"
* is associated with ORDINAL_MONTH value 5 while "Year 4665 Month 6 Day 2"
* is associated with ORDINAL_MONTH value 6 because 4665 is a leap year
* and there is an extra "Leap Month 5" which associated with ORDINAL_MONTH
* value 5 before "Month 6" of year 4664.
* @draft ICU 74
*/
public static final int ORDINAL_MONTH = 23;
/**
* The number of fields defined by this class. Subclasses may define
* addition fields starting with this number.
* @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
*/
@Deprecated
protected static final int BASE_FIELD_COUNT = 23;
protected static final int BASE_FIELD_COUNT = 24;
/**
* The maximum number of fields possible. Subclasses must not define
@ -1706,7 +1730,8 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
(1 << DAY_OF_MONTH) |
(1 << DAY_OF_YEAR) |
(1 << EXTENDED_YEAR) |
(1 << IS_LEAP_MONTH);
(1 << IS_LEAP_MONTH) |
(1 << ORDINAL_MONTH) ;
for (int i=BASE_FIELD_COUNT; i<fields.length; ++i) {
mask |= (1 << i);
}
@ -2034,6 +2059,89 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
}
//-------------------------------------------------------------------------
// Temporal Calendar API.
//-------------------------------------------------------------------------
/**
* {@icu} Returns true if the date is in a leap year. Recalculate the current time
* field values if the time value has been changed by a call to * setTime().
* This method is semantically const, but may alter the object in memory.
* A "leap year" is a year that contains more days than other years (for
* solar or lunar calendars) or more months than other years (for lunisolar
* calendars like Hebrew or Chinese), as defined in the ECMAScript Temporal
* proposal.
* @return true if the date in the fields is in a Temporal proposal
* defined leap year. False otherwise.
* @draft ICU 74
*/
public boolean inTemporalLeapYear() {
// Default to Gregorian based leap year rule.
return getActualMaximum(DAY_OF_YEAR) == 366;
}
private static String [] gTemporalMonthCodes = {
"M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12"
};
/**
* Gets The Temporal monthCode value corresponding to the month for the date.
* The value is a string identifier that starts with the literal grapheme
* "M" followed by two graphemes representing the zero-padded month number
* of the current month in a normal (non-leap) year and suffixed by an
* optional literal grapheme "L" if this is a leap month in a lunisolar
* calendar. The 25 possible values are "M01" .. "M13" and "M01L" .. "M12L".
* For the Hebrew calendar, the values are "M01" .. "M12" for non-leap year, and
* "M01" .. "M05", "M05L", "M06" .. "M12" for leap year.
* For the Chinese calendar, the values are "M01" .. "M12" for non-leap year and
* in leap year with another monthCode in "M01L" .. "M12L".
* For Coptic and Ethiopian calendar, the Temporal monthCode values for any
* years are "M01" to "M13".
*
* @return One of 25 possible strings in {"M01".."M13", "M01L".."M12L"}.
* @draft ICU 74
*/
public String getTemporalMonthCode() {
int month = get(MONTH);
assert(month < 12);
assert(internalGet(IS_LEAP_MONTH) == 0);
return gTemporalMonthCodes[month];
}
/**
* Sets The Temporal monthCode which is a string identifier that starts
* with the literal grapheme "M" followed by two graphemes representing
* the zero-padded month number of the current month in a normal
* (non-leap) year and suffixed by an optional literal grapheme "L" if this
* is a leap month in a lunisolar calendar. The 25 possible values are
* "M01" .. "M13" and "M01L" .. "M12L". For Hebrew calendar, the values are
* "M01" .. "M12" for non-leap years, and "M01" .. "M05", "M05L", "M06"
* .. "M12" for leap year.
* For the Chinese calendar, the values are "M01" .. "M12" for non-leap year and
* in leap year with another monthCode in "M01L" .. "M12L".
* For Coptic and Ethiopian calendar, the Temporal monthCode values for any
* years are "M01" to "M13".
* @param temporalMonth One of 25 possible strings in {"M01".. "M12", "M13", "M01L",
* "M12L"}.
* @draft ICU 74
*/
public void setTemporalMonthCode( String temporalMonth ) {
if (temporalMonth.length() == 3 && temporalMonth.charAt(0) == 'M') {
for (int m = 0; m < gTemporalMonthCodes.length; m++) {
if (temporalMonth.equals(gTemporalMonthCodes[m])) {
set(MONTH, m);
set(IS_LEAP_MONTH, 0);
return;
}
}
}
throw new IllegalArgumentException("Incorrect temporal Month code: " + temporalMonth);
}
//-------------------------------------------------------------------------
// End of Temporal Calendar API
//-------------------------------------------------------------------------
/**
* Returns the value for a given time field.
* @param field the given time field.
@ -2072,7 +2180,42 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
return (stamp[field] > UNSET) ? fields[field] : defaultValue;
}
/*
* @internal
* @deprecated This API is ICU internal only.
* Use this function instead of internalGet(MONTH). The implementation
* check the timestamp of MONTH and ORDINAL_MONTH and use the
* one set later. The subclass should override it to conver the value of ORDINAL_MONTH
* to MONTH correctly if ORDINAL_MONTH has higher priority.
* @return the value for the given time field.
*/
protected int internalGetMonth()
{
if (resolveFields(MONTH_PRECEDENCE) == MONTH) {
return internalGet(MONTH);
}
return internalGet(ORDINAL_MONTH);
}
/**
* @internal
* @deprecated This API is ICU internal only.
* Use this function instead of internalGet(MONTH, defaultValue). The implementation
* check the timestamp of MONTH and ORDINAL_MONTH and use the
* one set later. The subclass should override it to conver the value of ORDINAL_MONTH
* to MONTH correctly if ORDINAL_MONTH has higher priority.
* @param defaultValue a default value used if the MONTH and
* ORDINAL_MONTH are both unset.
* @return the value for the MONTH.
*/
protected int internalGetMonth(int defaultValue) {
if (resolveFields(MONTH_PRECEDENCE) == MONTH) {
return internalGet(MONTH, defaultValue);
}
return internalGet(ORDINAL_MONTH, defaultValue);
}
/**
* Sets the time field with the given value.
* @param field the given time field.
* @param value the value to be set for the given time field.
@ -2327,6 +2470,14 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
}
fields[field] = 0;
stamp[field] = UNSET;
if (field == MONTH) {
fields[ORDINAL_MONTH] = 0;
stamp[ORDINAL_MONTH] = UNSET;
}
if (field == ORDINAL_MONTH) {
fields[MONTH] = 0;
stamp[MONTH] = UNSET;
}
isTimeSet = areFieldsSet = areAllFieldsSet = areFieldsVirtuallySet = false;
}
@ -2525,6 +2676,10 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
result = getMaximum(field);
break;
case ORDINAL_MONTH:
result = inTemporalLeapYear() ? getMaximum(ORDINAL_MONTH) : getLeastMaximum(ORDINAL_MONTH);
break;
default:
// For all other fields, do it the hard way....
result = getActualHelper(field, getLeastMaximum(field), getMaximum(field));
@ -2887,13 +3042,14 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
}
case MONTH:
case ORDINAL_MONTH:
// Rolling the month involves both pinning the final value
// and adjusting the DAY_OF_MONTH if necessary. We only adjust the
// DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
// E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
{
int max = getActualMaximum(MONTH);
int mon = (internalGet(MONTH) + amount) % (max+1);
int mon = (internalGetMonth() + amount) % (max+1);
if (mon < 0) {
mon += (max + 1);
@ -3090,6 +3246,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
// have to be updated as well.
set(DAY_OF_YEAR, day_of_year);
clear(MONTH);
clear(ORDINAL_MONTH);
return;
}
case DAY_OF_YEAR:
@ -3265,6 +3422,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
// Fall through into standard handling
case EXTENDED_YEAR:
case MONTH:
case ORDINAL_MONTH:
{
boolean oldLenient = isLenient();
setLenient(true);
@ -4416,6 +4574,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
{ -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY
{ 0, 0, 24*ONE_HOUR-1, 24*ONE_HOUR-1 }, // MILLISECONDS_IN_DAY
{ 0, 0, 1, 1 }, // IS_LEAP_MONTH
{ 0, 0, 12, 12 }, // ORDINAL_MONTH
};
/**
@ -5302,6 +5461,13 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
},
};
static final int[][][] MONTH_PRECEDENCE = {
{
{ MONTH },
{ ORDINAL_MONTH },
},
};
/**
* Given a precedence table, return the newest field combination in
* the table, or -1 if none is found.
@ -5434,7 +5600,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
switch (field) {
case DAY_OF_MONTH:
y = handleGetExtendedYear();
validateField(field, 1, handleGetMonthLength(y, internalGet(MONTH)));
validateField(field, 1, handleGetMonthLength(y, internalGetMonth()));
break;
case DAY_OF_YEAR:
y = handleGetExtendedYear();
@ -5905,6 +6071,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
if (stamp[JULIAN_DAY] >= MINIMUM_USER_STAMP) {
int bestStamp = newestStamp(ERA, DAY_OF_WEEK_IN_MONTH, UNSET);
bestStamp = newestStamp(YEAR_WOY, EXTENDED_YEAR, bestStamp);
bestStamp = newestStamp(ORDINAL_MONTH, ORDINAL_MONTH, bestStamp);
if (bestStamp <= stamp[JULIAN_DAY]) {
return internalGet(JULIAN_DAY);
}
@ -6050,7 +6217,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
internalSet(EXTENDED_YEAR, year);
int month = useMonth ? internalGet(MONTH, getDefaultMonthInYear(year)) : 0;
int month = useMonth ? internalGetMonth(getDefaultMonthInYear(year)) : 0;
// Get the Julian day of the day BEFORE the start of this year.
// If useMonth is true, get the day before the start of the month.
@ -6128,7 +6295,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
// 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.
int m = internalGet(MONTH, JANUARY);
int m = internalGetMonth(JANUARY);
int monthLength = handleGetMonthLength(year, m);
date += ((monthLength - date) / 7 + dim + 1) * 7;
}
@ -6219,7 +6386,9 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
* @stable ICU 2.0
*/
protected void handleComputeFields(int julianDay) {
internalSet(MONTH, getGregorianMonth());
int gmonth = getGregorianMonth();
internalSet(MONTH, gmonth);
internalSet(ORDINAL_MONTH, gmonth);
internalSet(DAY_OF_MONTH, getGregorianDayOfMonth());
internalSet(DAY_OF_YEAR, getGregorianDayOfYear());
int eyear = getGregorianYear();
@ -6455,7 +6624,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
"DAY_OF_WEEK_IN_MONTH", "AM_PM", "HOUR", "HOUR_OF_DAY",
"MINUTE", "SECOND", "MILLISECOND", "ZONE_OFFSET",
"DST_OFFSET", "YEAR_WOY", "DOW_LOCAL", "EXTENDED_YEAR",
"JULIAN_DAY", "MILLISECONDS_IN_DAY",
"JULIAN_DAY", "MILLISECONDS_IN_DAY", "IS_LEAP_MONTH", "ORDINAL_MONTH"
};
/**

View file

@ -129,11 +129,14 @@ public class ChineseCalendar extends Calendar {
private transient CalendarCache newYearCache = new CalendarCache();
/**
* True if the current year is a leap year. Updated with each time to
* fields resolution.
* True if there is a leap month between the Winter Solstice before and after the
* current date.This is different from leap year because in some year, such as
* 1813 and 2033, the leap month is after the Winter Solstice of that year. So
* this value could be false for a date prior to the Winter Solstice of that
* year but that year still has a leap month and therefor is a leap year.
* @see #computeChineseFields
*/
private transient boolean isLeapYear;
private transient boolean hasLeapMonthBetweenWinterSolstices;
//------------------------------------------------------------------
// Constructors
@ -420,6 +423,7 @@ public class ChineseCalendar extends Calendar {
{/* */}, // JULIAN_DAY
{/* */}, // MILLISECONDS_IN_DAY
{ 0, 0, 1, 1 }, // IS_LEAP_MONTH
{ 0, 0, 11, 12 }, // ORDINAL_MONTH
};
/**
@ -557,6 +561,7 @@ public class ChineseCalendar extends Calendar {
public void add(int field, int amount) {
switch (field) {
case MONTH:
case ORDINAL_MONTH:
if (amount != 0) {
int dom = get(DAY_OF_MONTH);
int day = get(JULIAN_DAY) - EPOCH_JULIAN_DAY; // Get local day
@ -577,6 +582,7 @@ public class ChineseCalendar extends Calendar {
public void roll(int field, int amount) {
switch (field) {
case MONTH:
case ORDINAL_MONTH:
if (amount != 0) {
int dom = get(DAY_OF_MONTH);
int day = get(JULIAN_DAY) - EPOCH_JULIAN_DAY; // Get local day
@ -589,7 +595,7 @@ public class ChineseCalendar extends Calendar {
// value from 0..11 in a non-leap year, and from 0..12 in a
// leap year.
int m = get(MONTH); // 0-based month
if (isLeapYear) { // (member variable)
if (hasLeapMonthBetweenWinterSolstices) { // (member variable)
if (get(IS_LEAP_MONTH) == 1) {
++m;
} else {
@ -611,7 +617,7 @@ public class ChineseCalendar extends Calendar {
// Now do the standard roll computation on m, with the
// allowed range of 0..n-1, where n is 12 or 13.
int n = isLeapYear ? 13 : 12; // Months in this year
int n = hasLeapMonthBetweenWinterSolstices ? 13 : 12; // Months in this year
int newM = (m + amount) % n;
if (newM < 0) {
newM += n;
@ -837,7 +843,7 @@ public class ChineseCalendar extends Calendar {
* IS_LEAP_MONTH fields, as required by
* <code>handleComputeMonthStart()</code>.
*
* <p>As a side effect, this method sets {@link #isLeapYear}.
* <p>As a side effect, this method sets {@link #hasLeapMonthBetweenWinterSolstices}.
* @param days days after January 1, 1970 0:00 astronomical base zone of the
* date to compute fields for
* @param gyear the Gregorian year of the given date
@ -868,22 +874,31 @@ public class ChineseCalendar extends Calendar {
int firstMoon = newMoonNear(solsticeBefore + 1, true);
int lastMoon = newMoonNear(solsticeAfter + 1, false);
int thisMoon = newMoonNear(days + 1, false); // Start of this month
// Note: isLeapYear is a member variable
isLeapYear = synodicMonthsBetween(firstMoon, lastMoon) == 12;
// Note: hasLeapMonthBetweenWinterSolstices is a member variable
hasLeapMonthBetweenWinterSolstices = synodicMonthsBetween(firstMoon, lastMoon) == 12;
int month = synodicMonthsBetween(firstMoon, thisMoon);
if (isLeapYear && isLeapMonthBetween(firstMoon, thisMoon)) {
int theNewYear = newYear(gyear);
if (days < theNewYear) {
theNewYear = newYear(gyear-1);
}
if (hasLeapMonthBetweenWinterSolstices && isLeapMonthBetween(firstMoon, thisMoon)) {
month--;
}
if (month < 1) {
month += 12;
}
int ordinalMonth = synodicMonthsBetween(theNewYear, thisMoon);
if (ordinalMonth < 0) {
ordinalMonth += 12;
}
boolean isLeapMonth = isLeapYear &&
boolean isLeapMonth = hasLeapMonthBetweenWinterSolstices &&
hasNoMajorSolarTerm(thisMoon) &&
!isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, false));
internalSet(MONTH, month-1); // Convert from 1-based to 0-based
internalSet(ORDINAL_MONTH, ordinalMonth);
internalSet(IS_LEAP_MONTH, isLeapMonth?1:0);
if (setAllFields) {
@ -985,6 +1000,7 @@ public class ChineseCalendar extends Calendar {
// Save fields for later restoration
int saveMonth = internalGet(MONTH);
int saveOrdinalMonth = internalGet(ORDINAL_MONTH);
int saveIsLeapMonth = internalGet(IS_LEAP_MONTH);
// Ignore IS_LEAP_MONTH field if useMonth is false
@ -1003,6 +1019,7 @@ public class ChineseCalendar extends Calendar {
}
internalSet(MONTH, saveMonth);
internalSet(ORDINAL_MONTH, saveOrdinalMonth);
internalSet(IS_LEAP_MONTH, saveIsLeapMonth);
return julianDay - 1;
@ -1042,7 +1059,117 @@ public class ChineseCalendar extends Calendar {
winterSolsticeCache = new CalendarCache();
newYearCache = new CalendarCache();
}
//-------------------------------------------------------------------------
// Temporal Calendar API.
//-------------------------------------------------------------------------
/**
* {@icu} Returns true if the date is in a leap year. Recalculate the current time
* field values if the time value has been changed by a call to setTime().
* This method is semantically const, but may alter the object in memory.
* A "leap year" is a year that contains more days than other years (for
* solar or lunar calendars) or more months than other years (for lunisolar
* calendars like Hebrew or Chinese), as defined in the ECMAScript Temporal
* proposal.
* @return true if the date in the fields is in a Temporal proposal
* defined leap year. False otherwise.
* @draft ICU 74
*/
public boolean inTemporalLeapYear() {
return getActualMaximum(DAY_OF_YEAR) > 360;
}
private static String [] gTemporalLeapMonthCodes = {
"M01L", "M02L", "M03L", "M04L", "M05L", "M06L", "M07L", "M08L", "M09L", "M10L", "M11L", "M12L"
};
/**
* Gets The Temporal monthCode value corresponding to the month for the date.
* The value is a string identifier that starts with the literal grapheme
* "M" followed by two graphemes representing the zero-padded month number
* of the current month in a normal (non-leap) year and suffixed by an
* optional literal grapheme "L" if this is a leap month in a lunisolar
* calendar. For the Chinese calendar, the values are "M01" .. "M12" for
* non-leap year and * in leap year with another monthCode in "M01L" .. "M12L".
*
* @return One of 24 possible strings in {"M01".."M12", "M01L".."M12L"}.
* @draft ICU 74
*/
public String getTemporalMonthCode() {
// We need to call get, not internalGet, to force the calculation
// from ORDINAL_MONTH.
int is_leap = get(IS_LEAP_MONTH);
if (is_leap != 0) {
return gTemporalLeapMonthCodes[get(MONTH)];
}
return super.getTemporalMonthCode();
}
/**
* Sets The Temporal monthCode which is a string identifier that starts
* with the literal grapheme "M" followed by two graphemes representing
* the zero-padded month number of the current month in a normal
* (non-leap) year and suffixed by an optional literal grapheme "L" if this
* is a leap month in a lunisolar calendar.
* For the Chinese calendar, the values are "M01" .. "M12" for non-leap year and
* in leap year with another monthCode in "M01L" .. "M12L".
* @param temporalMonth One of 25 possible strings in {"M01".. "M12", "M13", "M01L",
* "M12L"}.
* @draft ICU 74
*/
public void setTemporalMonthCode( String temporalMonth ) {
if (temporalMonth.length() != 4 || temporalMonth.charAt(0) != 'M' || temporalMonth.charAt(3) != 'L') {
set(IS_LEAP_MONTH, 0);
super.setTemporalMonthCode(temporalMonth);
return;
}
for (int m = 0; m < gTemporalLeapMonthCodes.length; m++) {
if (temporalMonth.equals(gTemporalLeapMonthCodes[m])) {
set(MONTH, m);
set(IS_LEAP_MONTH, 1);
return;
}
}
throw new IllegalArgumentException("Incorrect temporal Month code: " + temporalMonth);
}
//-------------------------------------------------------------------------
// End of Temporal Calendar API
//-------------------------------------------------------------------------
/**
* {@inheritDoc}
* @internal
*/
protected int internalGetMonth()
{
if (resolveFields(MONTH_PRECEDENCE) == MONTH) {
return internalGet(MONTH);
}
Calendar temp = (Calendar) clone();
temp.set(Calendar.MONTH, 0);
temp.set(Calendar.IS_LEAP_MONTH, 0);
temp.set(Calendar.DATE, 1);
// Calculate the MONTH and IS_LEAP_MONTH by adding number of months.
temp.roll(Calendar.MONTH, internalGet(Calendar.ORDINAL_MONTH));
internalSet(Calendar.IS_LEAP_MONTH, temp.get(Calendar.IS_LEAP_MONTH));
int month = temp.get(Calendar.MONTH);
internalSet(Calendar.MONTH, month);
return month;
}
/**
* {@inheritDoc}
* @internal
*/
protected int internalGetMonth(int defaultValue)
{
if (resolveFields(MONTH_PRECEDENCE) == MONTH) {
return internalGet(MONTH, defaultValue);
}
return internalGetMonth();
}
/*
private static CalendarFactory factory;
public static CalendarFactory factory() {

View file

@ -293,6 +293,7 @@ public final class CopticCalendar extends CECalendar
internalSet(ERA, era);
internalSet(YEAR, year);
internalSet(MONTH, fields[1]);
internalSet(ORDINAL_MONTH, fields[1]);
internalSet(DAY_OF_MONTH, fields[2]);
internalSet(DAY_OF_YEAR, (30 * fields[1]) + fields[2]);
}

View file

@ -353,6 +353,7 @@ public final class EthiopicCalendar extends CECalendar
internalSet(ERA, era);
internalSet(YEAR, year);
internalSet(MONTH, fields[1]);
internalSet(ORDINAL_MONTH, fields[1]);
internalSet(DAY_OF_MONTH, fields[2]);
internalSet(DAY_OF_YEAR, (30 * fields[1]) + fields[2]);
}

View file

@ -280,6 +280,7 @@ public class GregorianCalendar extends Calendar {
{/* */}, // JULIAN_DAY
{/* */}, // MILLISECONDS_IN_DAY
{/* */}, // IS_LEAP_MONTH
{ 0, 0, 11, 11 }, // ORDINAL_MONTH
};
/**
@ -572,7 +573,7 @@ public class GregorianCalendar extends Calendar {
// may be one year before or after the calendar year.
int isoYear = get(YEAR_WOY);
int isoDoy = internalGet(DAY_OF_YEAR);
if (internalGet(MONTH) == Calendar.JANUARY) {
if (internalGetMonth() == Calendar.JANUARY) {
if (woy >= 52) {
isoDoy += handleGetYearLength(isoYear);
}
@ -792,6 +793,7 @@ public class GregorianCalendar extends Calendar {
++dayOfYear;
}
internalSet(MONTH, month);
internalSet(ORDINAL_MONTH, month);
internalSet(DAY_OF_MONTH, dayOfMonth);
internalSet(DAY_OF_YEAR, dayOfYear);
internalSet(EXTENDED_YEAR, eyear);

View file

@ -195,6 +195,8 @@ public class HebrewCalendar extends Calendar {
{ -5000000, -5000000, 5000000, 5000000 }, // EXTENDED_YEAR
{/* */}, // JULIAN_DAY
{/* */}, // MILLISECONDS_IN_DAY
{/* */}, // IS_LEAP_MONTH
{ 0, 0, 11, 12 }, // ORDINAL_MONTH
};
/**
@ -450,6 +452,7 @@ public class HebrewCalendar extends Calendar {
{
switch (field) {
case MONTH:
case ORDINAL_MONTH:
{
// We can't just do a set(MONTH, get(MONTH) + amount). The
// reason is ADAR_1. Suppose amount is +2 and we land in
@ -537,6 +540,7 @@ public class HebrewCalendar extends Calendar {
{
switch (field) {
case MONTH:
case ORDINAL_MONTH:
{
int month = get(MONTH);
int year = get(YEAR);
@ -766,7 +770,8 @@ public class HebrewCalendar extends Calendar {
@Override
@Deprecated
protected void validateField(int field) {
if (field == MONTH && !isLeapYear(handleGetExtendedYear()) && internalGet(MONTH) == ADAR_1) {
if ((field == MONTH || field == ORDINAL_MONTH) &&
!isLeapYear(handleGetExtendedYear()) && internalGetMonth() == ADAR_1) {
throw new IllegalArgumentException("MONTH cannot be ADAR_1(5) except leap years");
}
@ -815,7 +820,8 @@ public class HebrewCalendar extends Calendar {
// Now figure out which month we're in, and the date within that month
int yearType = yearType(year);
int monthStart[][] = isLeapYear(year) ? LEAP_MONTH_START : MONTH_START;
boolean isLeap = isLeapYear(year);
int monthStart[][] = isLeap ? LEAP_MONTH_START : MONTH_START;
int month = 0;
while (dayOfYear > monthStart[month][yearType]) {
@ -827,6 +833,11 @@ public class HebrewCalendar extends Calendar {
internalSet(ERA, 0);
internalSet(YEAR, year);
internalSet(EXTENDED_YEAR, year);
int ordinal_month = month;
if (!isLeap && ordinal_month > ADAR_1) {
ordinal_month--;
}
internalSet(ORDINAL_MONTH, ordinal_month);
internalSet(MONTH, month);
internalSet(DAY_OF_MONTH, dayOfMonth);
internalSet(DAY_OF_YEAR, dayOfYear);
@ -893,6 +904,79 @@ public class HebrewCalendar extends Calendar {
return "hebrew";
}
//-------------------------------------------------------------------------
// Temporal Calendar API.
//-------------------------------------------------------------------------
/**
* {@inheritDoc}
* @draft ICU 74
*/
public boolean inTemporalLeapYear() {
return isLeapYear(get(EXTENDED_YEAR));
}
private static String [] gTemporalMonthCodesForHebrew = {
"M01", "M02", "M03", "M04", "M05", "M05L",
"M06", "M07", "M08", "M09", "M10", "M11", "M12"
};
/**
* Gets The Temporal monthCode value corresponding to the month for the date.
* The value is a string identifier that starts with the literal grapheme
* "M" followed by two graphemes representing the zero-padded month number
* of the current month in a normal (non-leap) year and suffixed by an
* optional literal grapheme "L" if this is a leap month in a lunisolar
* calendar. For the Hebrew calendar, the values are "M01" .. "M12" for
* non-leap year, and "M01" .. "M05", "M05L", "M06" .. "M12" for leap year.
*
* @return One of 13 possible strings in {"M01".. "M05", "M05L", "M06" .. "M12"}.
* @draft ICU 74
*/
public String getTemporalMonthCode() {
return gTemporalMonthCodesForHebrew[get(MONTH)];
}
/**
* Sets The Temporal monthCode which is a string identifier that starts
* with the literal grapheme "M" followed by two graphemes representing
* the zero-padded month number of the current month in a normal
* (non-leap) year and suffixed by an optional literal grapheme "L" if this
* is a leap month in a lunisolar calendar. For Hebrew calendar, the values
* are "M01" .. "M12" for non-leap years, and "M01" .. "M05", "M05L", "M06"
* .. "M12" for leap year.
* @param temporalMonth The value to be set for temporal monthCode.
* @draft ICU 74
*/
public void setTemporalMonthCode( String temporalMonth ) {
if (temporalMonth.length() == 3 || temporalMonth.length() == 4) {
for (int m = 0; m < gTemporalMonthCodesForHebrew.length; m++) {
if (temporalMonth.equals(gTemporalMonthCodesForHebrew[m])) {
set(MONTH, m);
return;
}
}
}
throw new IllegalArgumentException("Incorrect temporal Month code: " + temporalMonth);
}
//-------------------------------------------------------------------------
// End of Temporal Calendar API
//-------------------------------------------------------------------------
/**
* {@inheritDoc}
* @internal
*/
protected int internalGetMonth()
{
if (resolveFields(MONTH_PRECEDENCE) == ORDINAL_MONTH) {
int ordinalMonth = internalGet(ORDINAL_MONTH);
int year = handleGetExtendedYear();
return ordinalMonth + (((!isLeapYear(year)) && (ordinalMonth > ADAR_1)) ? 1: 0);
}
return super.internalGetMonth();
}
/*
private static CalendarFactory factory;
public static CalendarFactory factory() {

View file

@ -395,6 +395,7 @@ public class IndianCalendar extends Calendar {
internalSet(EXTENDED_YEAR, IndianYear);
internalSet(YEAR, IndianYear);
internalSet(MONTH, IndianMonth);
internalSet(ORDINAL_MONTH, IndianMonth);
internalSet(DAY_OF_MONTH, IndianDayOfMonth );
internalSet(DAY_OF_YEAR, yday + 1); // yday is 0-based
}
@ -424,6 +425,8 @@ public class IndianCalendar extends Calendar {
{ -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR
{/* */}, // JULIAN_DAY
{/* */}, // MILLISECONDS_IN_DAY
{/* */}, // IS_LEAP_MONTH
{ 0, 0, 11, 11 }, // ORDINAL_MONTH
};

View file

@ -395,6 +395,8 @@ public class IslamicCalendar extends Calendar {
{ 1, 1, 5000000, 5000000}, // EXTENDED_YEAR
{/* */}, // JULIAN_DAY
{/* */}, // MILLISECONDS_IN_DAY
{/* */}, // IS_LEAP_MONTH
{ 0, 0, 11, 11 }, // ORDINAL_MONTH
};
/*
@ -917,6 +919,7 @@ public class IslamicCalendar extends Calendar {
internalSet(YEAR, year);
internalSet(EXTENDED_YEAR, year);
internalSet(MONTH, month);
internalSet(ORDINAL_MONTH, month);
internalSet(DAY_OF_MONTH, dayOfMonth);
internalSet(DAY_OF_YEAR, dayOfYear);
}
@ -1031,6 +1034,29 @@ public class IslamicCalendar extends Calendar {
}
}
//-------------------------------------------------------------------------
// Temporal Calendar API.
//-------------------------------------------------------------------------
/**
* {@icu} Returns true if the date is in a leap year. Recalculate the current time
* field values if the time value has been changed by a call to setTime().
* This method is semantically const, but may alter the object in memory.
* A "leap year" is a year that contains more days than other years (for
* solar or lunar calendars) or more months than other years (for lunisolar
* calendars like Hebrew or Chinese), as defined in the ECMAScript Temporal
* proposal.
* @return true if the date in the fields is in a Temporal proposal
* defined leap year. False otherwise.
* @draft ICU 74
*/
public boolean inTemporalLeapYear() {
return getActualMaximum(DAY_OF_YEAR) == 355;
}
//-------------------------------------------------------------------------
// End of Temporal Calendar API
//-------------------------------------------------------------------------
/*
private static CalendarFactory factory;
public static CalendarFactory factory() {

View file

@ -289,6 +289,9 @@ public class PersianCalendar extends Calendar {
{ -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR
{/* */}, // JULIAN_DAY
{/* */}, // MILLISECONDS_IN_DAY
{/* */}, // IS_LEAP_MONTH
{ 0, 0, 11, 11 }, // ORDINAL_MONTH
};
/**
@ -436,6 +439,7 @@ public class PersianCalendar extends Calendar {
internalSet(YEAR, year);
internalSet(EXTENDED_YEAR, year);
internalSet(MONTH, month);
internalSet(ORDINAL_MONTH, month);
internalSet(DAY_OF_MONTH, dayOfMonth);
internalSet(DAY_OF_YEAR, dayOfYear);
}

View file

@ -53,7 +53,7 @@ public class CalendarRegressionTest extends com.ibm.icu.dev.test.TestFmwk {
"DAY_OF_WEEK_IN_MONTH", "AM_PM", "HOUR", "HOUR_OF_DAY",
"MINUTE", "SECOND", "MILLISECOND", "ZONE_OFFSET",
"DST_OFFSET", "YEAR_WOY", "DOW_LOCAL", "EXTENDED_YEAR",
"JULIAN_DAY", "MILLISECONDS_IN_DAY"
"JULIAN_DAY", "MILLISECONDS_IN_DAY", "IS_LEAP_YEAR", "ORDINAL_MONTH"
};
@ -1427,6 +1427,7 @@ public class CalendarRegressionTest extends com.ibm.icu.dev.test.TestFmwk {
cal.get(Calendar.SECOND) != fields[5] ||
cal.get(Calendar.MILLISECOND) != fields[6]) {
errln("Field " + field +
" " + (op==0 ? "add" : "roll") +
" (" + FIELD_NAME[field] +
") FAIL, expected " +
fields[0] +

View file

@ -0,0 +1,249 @@
// © 2023 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package com.ibm.icu.dev.test.calendar;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.HebrewCalendar;
import com.ibm.icu.util.IslamicCalendar;
import com.ibm.icu.util.ULocale;
@RunWith(JUnit4.class)
public class InTemporalLeapYearTest extends com.ibm.icu.dev.test.TestFmwk {
@Test
public void TestGregorian() {
// test from year 1800 to 2500
GregorianCalendar gc = new GregorianCalendar();
for (int year = 1900; year < 2400; ++year) {
gc.set(year, Calendar.MARCH, 7);
assertEquals("Calendar::inTemporalLeapYear",
gc.isLeapYear(year), gc.inTemporalLeapYear() == true);
}
}
private void RunChinese(Calendar cal) {
GregorianCalendar gc = new GregorianCalendar();
Calendar leapTest = (Calendar)cal.clone();
// Start our test from 1900, Jan 1.
// Check every 29 days in exhausted mode.
int incrementDays = 29;
int startYear = 1900;
int stopYear = 2400;
boolean quick = TestFmwk.getExhaustiveness() <= 5;
if (quick) {
incrementDays = 317;
stopYear = 2100;
}
int yearForHasLeapMonth = -1;
boolean hasLeapMonth = false;
for (gc.set(startYear, Calendar.JANUARY, 1);
gc.get(Calendar.YEAR) <= stopYear;
gc.add(Calendar.DATE, incrementDays)) {
cal.setTime(gc.getTime());
int cal_year = cal.get(Calendar.EXTENDED_YEAR);
if (yearForHasLeapMonth != cal_year) {
leapTest.set(Calendar.EXTENDED_YEAR, cal_year);
leapTest.set(Calendar.MONTH, 0);
leapTest.set(Calendar.DATE, 1);
// seek any leap month
// check any leap month in the next 12 months.
for (hasLeapMonth = false;
(!hasLeapMonth) && cal_year == leapTest.get(Calendar.EXTENDED_YEAR);
leapTest.add(Calendar.MONTH, 1)) {
hasLeapMonth = leapTest.get(Calendar.IS_LEAP_MONTH) != 0;
}
yearForHasLeapMonth = cal_year;
}
boolean actualInLeap = cal.inTemporalLeapYear();
if (hasLeapMonth != actualInLeap) {
logln("Gregorian y=" + gc.get(Calendar.YEAR) +
" m=" + gc.get(Calendar.MONTH) +
" d=" + gc.get(Calendar.DATE) +
" => cal y=" + cal.get(Calendar.EXTENDED_YEAR) +
" m=" + (cal.get(Calendar.IS_LEAP_MONTH) == 1 ? "L" : "") +
cal.get(Calendar.MONTH) +
" d=" + cal.get(Calendar.DAY_OF_MONTH) +
" expected:" + (hasLeapMonth ? "true" : "false") +
" actual:" + (actualInLeap ? "true" : "false"));
}
assertEquals("inTemporalLeapYear", hasLeapMonth, actualInLeap);
}
}
@Test
public void TestChinese() {
RunChinese(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "chinese")));
}
@Test
public void TestDangi() {
RunChinese(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "dangi")));
}
@Test
public void TestHebrew() {
Calendar cal = Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "hebrew"));
GregorianCalendar gc = new GregorianCalendar();
Calendar leapTest = (Calendar)cal.clone();
// Start our test from 1900, Jan 1.
// Check every 29 days in exhausted mode.
int incrementDays = 29;
int startYear = 1900;
int stopYear = 2400;
boolean quick = TestFmwk.getExhaustiveness() <= 5;
if (quick) {
incrementDays = 317;
stopYear = 2100;
}
int yearForHasLeapMonth = -1;
boolean hasLeapMonth = false;
for (gc.set(startYear, Calendar.JANUARY, 1);
gc.get(Calendar.YEAR) <= stopYear;
gc.add(Calendar.DATE, incrementDays)) {
cal.setTime(gc.getTime());
int cal_year = cal.get(Calendar.EXTENDED_YEAR);
if (yearForHasLeapMonth != cal_year) {
leapTest.set(Calendar.EXTENDED_YEAR, cal_year);
leapTest.set(Calendar.MONTH, 0);
leapTest.set(Calendar.DATE, 1);
leapTest.add(Calendar.MONTH, 10);
hasLeapMonth = leapTest.get(Calendar.MONTH) == HebrewCalendar.TAMUZ;
yearForHasLeapMonth = cal_year;
}
boolean actualInLeap = cal.inTemporalLeapYear();
if (hasLeapMonth != actualInLeap) {
logln("Gregorian y=" + gc.get(Calendar.YEAR) +
" m=" + gc.get(Calendar.MONTH) +
" d=" + gc.get(Calendar.DATE) +
" => cal y=" + cal.get(Calendar.EXTENDED_YEAR) +
" m=" + (cal.get(Calendar.IS_LEAP_MONTH) == 1 ? "L" : "") +
cal.get(Calendar.MONTH) +
" d=" + cal.get(Calendar.DAY_OF_MONTH) +
" expected:" + (hasLeapMonth ? "true" : "false") +
" actual:" + (actualInLeap ? "true" : "false"));
}
assertEquals("inTemporalLeapYear", hasLeapMonth, actualInLeap);
}
}
private void RunIslamic(Calendar cal) {
RunXDaysIsLeap(cal, 355);
}
@Test
public void TestIslamic() {
RunIslamic(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "islamic")));
}
@Test
public void TestIslamicCivil() {
RunIslamic(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "islamic-civil")));
}
@Test
public void TestIslamicUmalqura() {
RunIslamic(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "islamic-umalqura")));
}
@Test
public void TestIslamicRGSA() {
RunIslamic(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "islamic-rgsa")));
}
@Test
public void TestIslamicTBLA() {
RunIslamic(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "islamic-tbla")));
}
private void RunXDaysIsLeap(Calendar cal, int x) {
GregorianCalendar gc = new GregorianCalendar();
Calendar leapTest = (Calendar)cal.clone();
// Start our test from 1900, Jan 1.
// Check every 29 days in exhausted mode.
int incrementDays = 29;
int startYear = 1900;
int stopYear = 2400;
boolean quick = TestFmwk.getExhaustiveness() <= 5;
if (quick) {
incrementDays = 317;
stopYear = 2100;
}
int yearForHasLeapMonth = -1;
boolean hasLeapMonth = false;
for (gc.set(startYear, Calendar.JANUARY, 1);
gc.get(Calendar.YEAR) <= stopYear;
gc.add(Calendar.DATE, incrementDays)) {
cal.setTime(gc.getTime());
int cal_year = cal.get(Calendar.EXTENDED_YEAR);
if (yearForHasLeapMonth != cal_year) {
// If that year has exactly x days, it is a leap year.
hasLeapMonth = cal.getActualMaximum(Calendar.DAY_OF_YEAR) == x;
yearForHasLeapMonth = cal_year;
}
boolean actualInLeap = cal.inTemporalLeapYear();
if (hasLeapMonth != actualInLeap) {
logln("Gregorian y=" + gc.get(Calendar.YEAR) +
" m=" + gc.get(Calendar.MONTH) +
" d=" + gc.get(Calendar.DATE) +
" => cal y=" + cal.get(Calendar.EXTENDED_YEAR) +
" m=" + (cal.get(Calendar.IS_LEAP_MONTH) == 1 ? "L" : "") +
cal.get(Calendar.MONTH) +
" d=" + cal.get(Calendar.DAY_OF_MONTH) +
" expected:" + (hasLeapMonth ? "true" : "false") +
" actual:" + (actualInLeap ? "true" : "false"));
}
assertEquals("inTemporalLeapYear", hasLeapMonth, actualInLeap);
}
}
@Test
public void TestTaiwan() {
RunXDaysIsLeap(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "roc")), 366);
}
@Test
public void TestJapanese() {
RunXDaysIsLeap(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "japanese")), 366);
}
@Test
public void TestBuddhist() {
RunXDaysIsLeap(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "buddhist")), 366);
}
@Test
public void TestPersian() {
RunXDaysIsLeap(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "persian")), 366);
}
@Test
public void TestIndian() {
RunXDaysIsLeap(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "indian")), 366);
}
@Test
public void TestCoptic() {
RunXDaysIsLeap(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "coptic")), 366);
}
@Test
public void TestEthiopic() {
RunXDaysIsLeap(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "ethiopic")), 366);
}
@Test
public void TestEthiopicAmeteAlem() {
RunXDaysIsLeap(Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "ethiopic-amete-alem")), 366);
}
}

View file

@ -0,0 +1,488 @@
// © 2023 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package com.ibm.icu.dev.test.calendar;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.CopticCalendar;
import com.ibm.icu.util.EthiopicCalendar;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.HebrewCalendar;
import com.ibm.icu.util.IslamicCalendar;
import com.ibm.icu.util.ULocale;
@RunWith(JUnit4.class)
public class OrdinalMonthTest extends com.ibm.icu.dev.test.TestFmwk {
private void VerifyMonth(String message, Calendar cc, int expectedMonth,
int expectedOrdinalMonth, boolean expectedLeapMonth,
String expectedMonthCode) {
assertEquals(message + " get(MONTH)",
expectedMonth, cc.get(Calendar.MONTH));
assertEquals(message + " get(ORDINAL_MONTH)",
expectedOrdinalMonth, cc.get(Calendar.ORDINAL_MONTH));
assertEquals(message + " get(IS_LEAP_MONTH)",
expectedLeapMonth ? 1 : 0, cc.get(Calendar.IS_LEAP_MONTH));
assertEquals(message + " getTemporalMonthCode()",
expectedMonthCode, cc.getTemporalMonthCode());
}
void assertSetTemporalMonthCodeThrowIllegalArgumentException(Calendar cal, String monthCode) {
try {
cal.setTemporalMonthCode(monthCode);
fail("setTemporalMonthCode(\"" + monthCode + "\") should get IllegalArgumentException");
} catch (IllegalArgumentException expected) {
// expect to catch IllegalArgumentException
}
}
void assertSetTemporalMonthCodeThrowIllegalArgumentException(
Calendar cal, String [] invalidMonthCodes) {
for (String monthCode : invalidMonthCodes) {
assertSetTemporalMonthCodeThrowIllegalArgumentException(cal, monthCode);
}
}
@Test
public void TestMostCalendarsSet() {
GregorianCalendar gc = new GregorianCalendar();
gc.set(2022, Calendar.DECEMBER, 16);
String [] calendars = Calendar.getKeywordValuesForLocale(
"calendar", ULocale.ROOT, false);
for (String calendar : calendars) {
// Test these three calendars differently.
if (calendar == "chinese") continue; // work around ICU-22444
if (calendar == "dangi") continue; // work around ICU-22444
if (calendar == "hebrew") continue; // work around ICU-22444
Calendar cc1 = Calendar.getInstance(
ULocale.ROOT.setKeywordValue("calendar", calendar));
Calendar cc2 = (Calendar)cc1.clone();
Calendar cc3 = (Calendar)cc1.clone();
cc1.set(Calendar.EXTENDED_YEAR, 2134);
cc2.set(Calendar.EXTENDED_YEAR, 2134);
cc3.set(Calendar.EXTENDED_YEAR, 2134);
cc1.set(Calendar.MONTH, 5);
cc2.set(Calendar.ORDINAL_MONTH, 5);
cc3.setTemporalMonthCode("M06");
cc1.set(Calendar.DATE, 23);
cc2.set(Calendar.DATE, 23);
cc3.set(Calendar.DATE, 23);
assertEquals("M06 cc2==cc1 set month by Calendar.MONTH and Calendar.ORDINAL_MONTH",
cc1, cc2);
assertEquals("M06 cc2==cc3 set month by Calendar.MONTH and setTemporalMonthCode",
cc2, cc3);
VerifyMonth("cc1", cc1, 5, 5, false, "M06");
VerifyMonth("cc2", cc2, 5, 5, false, "M06");
VerifyMonth("cc3", cc3, 5, 5, false, "M06");
cc1.set(Calendar.ORDINAL_MONTH, 6);
cc2.setTemporalMonthCode("M07");
cc3.set(Calendar.MONTH, 6);
assertEquals("M07 cc1==cc3 set month by Calendar.MONTH and Calendar.ORDINAL_MONTH",
cc1, cc3);
assertEquals("M07 cc2==cc3 set month by Calendar.MONTH and setTemporalMonthCode",
cc2, cc3);
VerifyMonth("cc1", cc1, 6, 6, false, "M07");
VerifyMonth("cc2", cc2, 6, 6, false, "M07");
VerifyMonth("cc3", cc3, 6, 6, false, "M07");
cc1.setTemporalMonthCode("M08");
cc2.set(Calendar.MONTH, 7);
cc3.set(Calendar.ORDINAL_MONTH, 7);
assertEquals("M08 cc1==cc2 set month by Calendar.MONTH and setTemporalMonthCode",
cc1, cc2);
assertEquals("M08 cc2==cc3 set month by Calendar.MONTH and Calendar.ORDINAL_MONTH",
cc2, cc3);
VerifyMonth("cc1", cc1, 7, 7, false, "M08");
VerifyMonth("cc2", cc2, 7, 7, false, "M08");
VerifyMonth("cc3", cc3, 7, 7, false, "M08");
cc1.set(Calendar.DATE, 3);
// For "M13", do not return error for these three calendars.
if (calendar == "coptic" || calendar == "ethiopic" ||
calendar == "ethiopic-amete-alem") {
cc1.setTemporalMonthCode("M13");
assertEquals("get(Calendar.MONTH) after setTemporalMonthCode(\"M13\")",
12, cc1.get(Calendar.MONTH));
assertEquals("get(Calendar.ORDINAL_MONTH) after setTemporalMonthCode(\"M13\")",
12, cc1.get(Calendar.ORDINAL_MONTH));
} else {
assertSetTemporalMonthCodeThrowIllegalArgumentException(cc1, "M13");
}
// Out of bound monthCodes should return error.
// These are not valid for calendar do not have a leap month
String [] invalidMonthCodes = {
"M00", "M14", "M01L", "M02L", "M03L", "M04L", "M05L", "M06L", "M07L",
"M08L", "M09L", "M10L", "M11L", "M12L"
};
assertSetTemporalMonthCodeThrowIllegalArgumentException(cc1, invalidMonthCodes);
}
}
private void RunTestChineseCalendarSet(String calendar, int notLeapYear, int leapMarchYear) {
GregorianCalendar gc = new GregorianCalendar();
gc.set(2022, Calendar.DECEMBER, 16);
Calendar cc1 = Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", calendar));
Calendar cc2 = (Calendar)cc1.clone();
Calendar cc3 = (Calendar)cc1.clone();
cc1.set(Calendar.EXTENDED_YEAR, leapMarchYear);
cc2.set(Calendar.EXTENDED_YEAR, leapMarchYear);
cc3.set(Calendar.EXTENDED_YEAR, leapMarchYear);
cc1.set(Calendar.MONTH, Calendar.MARCH);
cc1.set(Calendar.IS_LEAP_MONTH, 1);
cc2.set(Calendar.ORDINAL_MONTH, 3);
cc3.setTemporalMonthCode("M03L");
cc1.set(Calendar.DATE, 1);
cc2.set(Calendar.DATE, 1);
cc3.set(Calendar.DATE, 1);
assertEquals("" + leapMarchYear + " M03L cc2==cc1 set month by Calendar.MONTH and Calendar.ORDINAL_MONTH",
cc1, cc2);
assertEquals("" + leapMarchYear + " M03L cc2==cc3 set month by Calendar.ORDINAL_MONTH and setTemporalMonthCode",
cc2, cc3);
VerifyMonth("" + leapMarchYear + " M03L cc1", cc1, Calendar.MARCH, 3, true, "M03L");
VerifyMonth("" + leapMarchYear + " M03L cc2", cc2, Calendar.MARCH, 3, true, "M03L");
VerifyMonth("" + leapMarchYear + " M03L cc3", cc3, Calendar.MARCH, 3, true, "M03L");
cc1.set(Calendar.EXTENDED_YEAR, notLeapYear);
cc2.set(Calendar.EXTENDED_YEAR, notLeapYear);
cc3.set(Calendar.EXTENDED_YEAR, notLeapYear);
cc1.set(Calendar.ORDINAL_MONTH, 5);
cc2.setTemporalMonthCode("M06");
cc3.set(Calendar.MONTH, Calendar.JUNE);
cc3.set(Calendar.IS_LEAP_MONTH, 0);
assertEquals("" + notLeapYear + " M06 cc1==cc2 set month by Calendar.ORDINAL_MONTH and setTemporalMonthCode",
cc1, cc2);
assertEquals("" + notLeapYear + " M06 cc2==cc3 set month by Calendar.MONTH and setTemporalMonthCode",
cc2, cc3);
VerifyMonth("" + notLeapYear + " M06 cc1", cc1, Calendar.JUNE, 5, false, "M06");
VerifyMonth("" + notLeapYear + " M06 cc2", cc2, Calendar.JUNE, 5, false, "M06");
VerifyMonth("" + notLeapYear + " M06 cc3", cc3, Calendar.JUNE, 5, false, "M06");
cc1.set(Calendar.EXTENDED_YEAR, leapMarchYear);
cc2.set(Calendar.EXTENDED_YEAR, leapMarchYear);
cc3.set(Calendar.EXTENDED_YEAR, leapMarchYear);
cc1.setTemporalMonthCode("M04");
cc2.set(Calendar.MONTH, Calendar.APRIL);
cc2.set(Calendar.IS_LEAP_MONTH, 0);
cc3.set(Calendar.ORDINAL_MONTH, 4);
assertEquals("" + leapMarchYear + " M04 cc2==cc1 set month by setTemporalMonthCode and Calendar.MONTH",
cc1, cc2);
assertEquals("" + leapMarchYear + " M04 cc2==cc3 set month by Calendar.MONTH and Calendar.ORDINAL_MONTH",
cc2, cc3);
// 4592 has leap March so April is the 5th month in that year.
VerifyMonth("" + leapMarchYear + " M04 cc1", cc1, Calendar.APRIL, 4, false, "M04");
VerifyMonth("" + leapMarchYear + " M04 cc2", cc2, Calendar.APRIL, 4, false, "M04");
VerifyMonth("" + leapMarchYear + " M04 cc3", cc3, Calendar.APRIL, 4, false, "M04");
cc1.set(Calendar.EXTENDED_YEAR, notLeapYear);
cc2.set(Calendar.EXTENDED_YEAR, notLeapYear);
cc3.set(Calendar.EXTENDED_YEAR, notLeapYear);
assertEquals("" + notLeapYear + " M04 cc2==cc1 set month by setTemporalMonthCode and Calendar.MONTH",
cc1, cc2);
assertEquals("" + notLeapYear + " M04 cc2==cc3 set month by Calendar.MONTH and Calendar.ORDINAL_MONTH",
cc2, cc3);
// 4592 has no leap month before April so April is the 4th month in that year.
VerifyMonth("" + leapMarchYear + " M04 cc1", cc1, Calendar.APRIL, 3, false, "M04");
VerifyMonth("" + leapMarchYear + " M04 cc2", cc2, Calendar.APRIL, 3, false, "M04");
VerifyMonth("" + leapMarchYear + " M04 cc3", cc3, Calendar.APRIL, 3, false, "M04");
// Out of bound monthCodes should return error.
// These are not valid for calendar do not have a leap month
String [] invalidMonthCodes = {"M00", "M13", "M14"};
assertSetTemporalMonthCodeThrowIllegalArgumentException(cc1, invalidMonthCodes);
}
@Test
public void TestChineseCalendarSet() {
RunTestChineseCalendarSet("chinese", 4591, 4592);
}
@Test
public void TestDangiCalendarSet() {
RunTestChineseCalendarSet("dangi", 4287, 4288);
}
@Test
public void TestHebrewCalendarSet() {
GregorianCalendar gc = new GregorianCalendar();
gc.set(2022, Calendar.DECEMBER, 16);
Calendar cc1 = Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "hebrew"));
Calendar cc2 = (Calendar)cc1.clone();
Calendar cc3 = (Calendar)cc1.clone();
// 5782 is leap year, 5781 is NOT.
int leapYear = 5782;
int notLeapYear = 5781;
cc1.set(Calendar.EXTENDED_YEAR, leapYear);
cc2.set(Calendar.EXTENDED_YEAR, leapYear);
cc3.set(Calendar.EXTENDED_YEAR, leapYear);
cc1.set(Calendar.MONTH, HebrewCalendar.ADAR_1);
cc2.set(Calendar.ORDINAL_MONTH, 5);
cc3.setTemporalMonthCode("M05L");
cc1.set(Calendar.DATE, 1);
cc2.set(Calendar.DATE, 1);
cc3.set(Calendar.DATE, 1);
assertEquals("5782 M05L cc2==cc1 set month by Calendar.MONTH and Calendar.ORDINAL_MONTH",
cc1, cc2);
assertEquals("5782 M05L cc2==cc3 set month by Calendar.MONTH and setTemporalMonthCode",
cc2, cc3);
VerifyMonth("cc1", cc1, HebrewCalendar.ADAR_1, 5, false, "M05L");
VerifyMonth("cc2", cc2, HebrewCalendar.ADAR_1, 5, false, "M05L");
VerifyMonth("cc3", cc3, HebrewCalendar.ADAR_1, 5, false, "M05L");
cc1.set(Calendar.ORDINAL_MONTH, 4);
cc2.setTemporalMonthCode("M05");
cc3.set(Calendar.MONTH, HebrewCalendar.SHEVAT);
assertEquals("5782 M05 cc2==cc3 set month by Calendar.ORDINAL_MONTH and setTemporalMonthCode",
cc1, cc2);
assertEquals("5782 M05 cc2==cc3 set month by Calendar.MONTH and setTemporalMonthCode",
cc2, cc3);
VerifyMonth("cc1", cc1, HebrewCalendar.SHEVAT, 4, false, "M05");
VerifyMonth("cc2", cc2, HebrewCalendar.SHEVAT, 4, false, "M05");
VerifyMonth("cc3", cc3, HebrewCalendar.SHEVAT, 4, false, "M05");
cc1.set(Calendar.EXTENDED_YEAR, notLeapYear);
cc2.set(Calendar.EXTENDED_YEAR, notLeapYear);
cc3.set(Calendar.EXTENDED_YEAR, notLeapYear);
cc1.setTemporalMonthCode("M06");
cc2.set(Calendar.MONTH, HebrewCalendar.ADAR);
cc3.set(Calendar.ORDINAL_MONTH, 5);
assertEquals("5781 M06 cc1==cc2 set month by Calendar.MONTH and setTemporalMonthCode",
cc1, cc2);
assertEquals("5781 M06 cc2==cc3 set month by Calendar.MONTH and Calendar.ORDINAL_MONTH",
cc2, cc3);
VerifyMonth("cc1", cc1, HebrewCalendar.ADAR, 5, false, "M06");
VerifyMonth("cc2", cc2, HebrewCalendar.ADAR, 5, false, "M06");
VerifyMonth("cc3", cc3, HebrewCalendar.ADAR, 5, false, "M06");
cc1.set(Calendar.EXTENDED_YEAR, leapYear);
cc2.set(Calendar.EXTENDED_YEAR, leapYear);
cc3.set(Calendar.EXTENDED_YEAR, leapYear);
assertEquals("5782 M06 cc1==cc2 set month by Calendar.MONTH and setTemporalMonthCode",
cc1, cc2);
assertEquals("5782 M06 cc2==cc3 set month by Calendar.MONTH and Calendar.ORDINAL_MONTH",
cc2, cc3);
assertEquals("5782 M06 cc2==cc3 set month by Calendar.MONTH and setTemporalMonthCode",
cc2, cc3);
VerifyMonth("cc1", cc1, HebrewCalendar.ADAR, 6, false, "M06");
VerifyMonth("cc2", cc2, HebrewCalendar.ADAR, 6, false, "M06");
VerifyMonth("cc3", cc3, HebrewCalendar.ADAR, 6, false, "M06");
cc1.set(Calendar.ORDINAL_MONTH, 7);
cc2.setTemporalMonthCode("M07");
cc3.set(Calendar.MONTH, HebrewCalendar.NISAN);
assertEquals("5782 M07 cc1==cc2 set month by Calendar.ORDINAL_MONTH and setTemporalMonthCode",
cc1, cc2);
assertEquals("5782 M07 cc2==cc3 set month by Calendar.MONTH and setTemporalMonthCode",
cc2, cc3);
VerifyMonth("cc1", cc1, HebrewCalendar.NISAN, 7, false, "M07");
VerifyMonth("cc2", cc2, HebrewCalendar.NISAN, 7, false, "M07");
VerifyMonth("cc3", cc3, HebrewCalendar.NISAN, 7, false, "M07");
// Out of bound monthCodes should return error.
// These are not valid for calendar do not have a leap month
String [] invalidMonthCodes = {
"M00", "M13", "M14", "M01L", "M02L", "M03L", "M04L",
/* M05L could be legal */
"M06L", "M07L", "M08L", "M09L", "M10L", "M11L", "M12L"};
assertSetTemporalMonthCodeThrowIllegalArgumentException(cc1, invalidMonthCodes);
}
@Test
public void TestAdd() {
GregorianCalendar gc = new GregorianCalendar();
gc.set(2022, Calendar.DECEMBER, 16);
String [] calendars = Calendar.getKeywordValuesForLocale(
"calendar", ULocale.ROOT, false);
for (String calendar : calendars) {
Calendar cc1 = Calendar.getInstance(
ULocale.ROOT.setKeywordValue("calendar", calendar));
cc1.setTime(gc.getTime());
Calendar cc2 = (Calendar)cc1.clone();
for (int i = 0; i < 8; i++) {
for (int j = 1; j < 8; j++) {
cc1.add(Calendar.MONTH, j);
cc2.add(Calendar.ORDINAL_MONTH, j);
assertEquals("two add produce the same result", cc1, cc2);
}
for (int j = 1; j < 8; j++) {
cc1.add(Calendar.MONTH, -j);
cc2.add(Calendar.ORDINAL_MONTH, -j);
assertEquals("two add produce the same result", cc1, cc2);
}
}
}
}
@Test
public void TestRoll() {
GregorianCalendar gc = new GregorianCalendar();
gc.set(2022, Calendar.DECEMBER, 16);
String [] calendars = Calendar.getKeywordValuesForLocale(
"calendar", ULocale.ROOT, false);
for (String calendar : calendars) {
Calendar cc1 = Calendar.getInstance(
ULocale.ROOT.setKeywordValue("calendar", calendar));
cc1.setTime(gc.getTime());
Calendar cc2 = (Calendar)cc1.clone();
for (int i = 0; i < 8; i++) {
for (int j = 1; j < 8; j++) {
cc1.roll(Calendar.MONTH, j);
cc2.roll(Calendar.ORDINAL_MONTH, j);
assertEquals("two roll produce the same result", cc1, cc2);
}
for (int j = 1; j < 8; j++) {
cc1.roll(Calendar.MONTH, -j);
cc2.roll(Calendar.ORDINAL_MONTH, -j);
assertEquals("two roll produce the same result", cc1, cc2);
}
for (int j = 1; j < 3; j++) {
cc1.roll(Calendar.MONTH, true);
cc2.roll(Calendar.ORDINAL_MONTH, true);
assertEquals("two roll produce the same result", cc1, cc2);
}
for (int j = 1; j < 3; j++) {
cc1.roll(Calendar.MONTH, false);
cc2.roll(Calendar.ORDINAL_MONTH, false);
assertEquals("two roll produce the same result", cc1, cc2);
}
}
}
}
@Test
public void TestLimits() {
GregorianCalendar gc = new GregorianCalendar();
gc.set(2022, Calendar.DECEMBER, 16);
String [] calendars = Calendar.getKeywordValuesForLocale(
"calendar", ULocale.ROOT, false);
Object[][] cases = {
{ "gregorian", 0, 11, 0, 11 },
{ "japanese", 0, 11, 0, 11 },
{ "buddhist", 0, 11, 0, 11 },
{ "roc", 0, 11, 0, 11 },
{ "persian", 0, 11, 0, 11 },
{ "islamic-civil", 0, 11, 0, 11 },
{ "islamic", 0, 11, 0, 11 },
{ "hebrew", 0, 12, 0, 11 },
{ "chinese", 0, 12, 0, 11 },
{ "indian", 0, 11, 0, 11 },
{ "coptic", 0, 12, 0, 12 },
{ "ethiopic", 0, 12, 0, 12 },
{ "ethiopic-amete-alem", 0, 12, 0, 12 },
{ "iso8601", 0, 11, 0, 11 },
{ "dangi", 0, 12, 0, 11 },
{ "islamic-umalqura", 0, 11, 0, 11 },
{ "islamic-tbla", 0, 11, 0, 11 },
{ "islamic-rgsa", 0, 11, 0, 11 },
};
for (String calendar : calendars) {
Calendar cc1 = Calendar.getInstance(
ULocale.ROOT.setKeywordValue("calendar", calendar));
boolean found = false;
for (Object[] cas : cases) {
if (calendar.equals((String) cas[0])) {
int min = (Integer) cas[1];
int max = (Integer) cas[2];
int greatestMin = (Integer) cas[3];
int leastMax = (Integer) cas[4];
assertEquals("getMinimum(Calendar.ORDINAL_MONTH)",
min, cc1.getMinimum(Calendar.ORDINAL_MONTH));
assertEquals("getMaximum(Calendar.ORDINAL_MONTH)",
max, cc1.getMaximum(Calendar.ORDINAL_MONTH));
assertEquals("getMinimum(Calendar.ORDINAL_MONTH)",
greatestMin, cc1.getGreatestMinimum(Calendar.ORDINAL_MONTH));
assertEquals("getMinimum(Calendar.ORDINAL_MONTH)",
leastMax, cc1.getLeastMaximum(Calendar.ORDINAL_MONTH));
found = true;
break;
}
}
if (!found) {
errln("Cannot find expectation" + calendar);
}
}
}
@Test
public void TestActaulLimits() {
GregorianCalendar gc = new GregorianCalendar();
gc.set(2022, Calendar.DECEMBER, 16);
Object[][] cases = {
{ "gregorian", 2021, 0, 11 },
{ "gregorian", 2022, 0, 11 },
{ "gregorian", 2023, 0, 11 },
{ "japanese", 2021, 0, 11 },
{ "japanese", 2022, 0, 11 },
{ "japanese", 2023, 0, 11 },
{ "buddhist", 2021, 0, 11 },
{ "buddhist", 2022, 0, 11 },
{ "buddhist", 2023, 0, 11 },
{ "roc", 2021, 0, 11 },
{ "roc", 2022, 0, 11 },
{ "roc", 2023, 0, 11 },
{ "persian", 1400, 0, 11 },
{ "persian", 1401, 0, 11 },
{ "persian", 1402, 0, 11 },
{ "hebrew", 5782, 0, 12 },
{ "hebrew", 5783, 0, 11 },
{ "hebrew", 5789, 0, 11 },
{ "hebrew", 5790, 0, 12 },
{ "chinese", 4645, 0, 11 },
{ "chinese", 4646, 0, 12 },
{ "chinese", 4647, 0, 11 },
{ "dangi", 4645 + 304, 0, 11 },
{ "dangi", 4646 + 304, 0, 12 },
{ "dangi", 4647 + 304, 0, 11 },
{ "indian", 1944, 0, 11 },
{ "indian", 1945, 0, 11 },
{ "indian", 1946, 0, 11 },
{ "coptic", 1737, 0, 12 },
{ "coptic", 1738, 0, 12 },
{ "coptic", 1739, 0, 12 },
{ "ethiopic", 2013, 0, 12 },
{ "ethiopic", 2014, 0, 12 },
{ "ethiopic", 2015, 0, 12 },
{ "ethiopic-amete-alem", 2014, 0, 12 },
{ "ethiopic-amete-alem", 2015, 0, 12 },
{ "ethiopic-amete-alem", 2016, 0, 12 },
{ "iso8601", 2022, 0, 11 },
{ "islamic-civil", 1443, 0, 11 },
{ "islamic-civil", 1444, 0, 11 },
{ "islamic-civil", 1445, 0, 11 },
{ "islamic", 1443, 0, 11 },
{ "islamic", 1444, 0, 11 },
{ "islamic", 1445, 0, 11 },
{ "islamic-umalqura", 1443, 0, 11 },
{ "islamic-umalqura", 1444, 0, 11 },
{ "islamic-umalqura", 1445, 0, 11 },
{ "islamic-tbla", 1443, 0, 11 },
{ "islamic-tbla", 1444, 0, 11 },
{ "islamic-tbla", 1445, 0, 11 },
{ "islamic-rgsa", 1443, 0, 11 },
{ "islamic-rgsa", 1444, 0, 11 },
{ "islamic-rgsa", 1445, 0, 11 },
};
for (Object[] cas : cases) {
String calendar = (String) cas[0];
int extended_year = (Integer) cas[1];
int actualMinOrdinalMonth = (Integer) cas[2];
int actualMaxOrdinalMonth = (Integer) cas[3];
Calendar cc1 = Calendar.getInstance(
ULocale.ROOT.setKeywordValue("calendar", calendar));
cc1.set(Calendar.EXTENDED_YEAR, extended_year);
cc1.set(Calendar.ORDINAL_MONTH, 0);
cc1.set(Calendar.DATE, 1);
assertEquals("getActualMinimum(Calendar.ORDINAL_MONTH)",
actualMinOrdinalMonth, cc1.getActualMinimum(Calendar.ORDINAL_MONTH));
assertEquals("getActualMaximum(Calendar.ORDINAL_MONTH)",
actualMaxOrdinalMonth, cc1.getActualMaximum(Calendar.ORDINAL_MONTH));
}
}
}

View file

@ -0,0 +1,447 @@
// © 2023 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package com.ibm.icu.dev.test.calendar;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.CopticCalendar;
import com.ibm.icu.util.EthiopicCalendar;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.HebrewCalendar;
import com.ibm.icu.util.IslamicCalendar;
import com.ibm.icu.util.ULocale;
@RunWith(JUnit4.class)
public class TemporalMonthCodeTest extends com.ibm.icu.dev.test.TestFmwk {
@Test
public void TestChineseCalendarGetTemporalMonthCode() {
RunChineseGetTemporalMonthCode(
Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "chinese")));
}
@Test
public void TestDangiCalendarGetTemporalMonthCode() {
RunChineseGetTemporalMonthCode(
Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "dangi")));
}
private String MonthCode(int month, boolean leap) {
return String.format("M%02d%s", month, leap ? "L" : "");
}
private String HebrewMonthCode(int month) {
if (month == HebrewCalendar.ADAR_1) {
return MonthCode(month, true);
}
return MonthCode(month < HebrewCalendar.ADAR_1 ? month+1 : month, false);
}
private void RunChineseGetTemporalMonthCode(Calendar cal) {
GregorianCalendar gc = new GregorianCalendar();
// Start our test from 1900, Jan 1.
// Check every 29 days in exhausted mode.
int incrementDays = 29;
int startYear = 1900;
int stopYear = 2400;
boolean quick = TestFmwk.getExhaustiveness() <= 5;
if (quick) {
incrementDays = 317;
startYear = 1950;
stopYear = 2050;
}
for (gc.set(startYear, Calendar.JANUARY, 1);
gc.get(Calendar.YEAR) <= stopYear;
gc.add(Calendar.DATE, incrementDays)) {
cal.setTime(gc.getTime());
int cal_month = cal.get(Calendar.MONTH);
String expected = MonthCode(cal_month+1, cal.get(Calendar.IS_LEAP_MONTH) != 0);
assertEquals("getTemporalMonthCode", expected, cal.getTemporalMonthCode());
}
}
@Test
public void TestHebrewCalendarGetTemporalMonthCode() {
Calendar cal = Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "hebrew"));
GregorianCalendar gc = new GregorianCalendar();
// Start our test from 1900, Jan 1.
// Check every 29 days in exhausted mode.
int incrementDays = 29;
int startYear = 1900;
int stopYear = 2400;
boolean quick = TestFmwk.getExhaustiveness() <= 5;
if (quick) {
incrementDays = 317;
stopYear = 2100;
}
for (gc.set(startYear, Calendar.JANUARY, 1);
gc.get(Calendar.YEAR) <= stopYear;
gc.add(Calendar.DATE, incrementDays)) {
cal.setTime(gc.getTime());
int cal_month = cal.get(Calendar.MONTH);
String expected = HebrewMonthCode(cal_month);
assertEquals("getTemporalMonthCode", expected, cal.getTemporalMonthCode());
}
}
private void RunCEGetTemporalMonthCode(Calendar cal) {
GregorianCalendar gc = new GregorianCalendar();
// Start our test from 1900, Jan 1.
// // Start testing from 1900
gc.set(1900, Calendar.JANUARY, 1);
cal.setTime(gc.getTime());
int year = cal.get(Calendar.YEAR);
for (int m = 0; m < 13; m++) {
String expected = MonthCode(m+1, false);
for (int y = year; y < year + 500 ; y++) {
cal.set(y, m, 1);
assertEquals("getTemporalMonthCode", expected, cal.getTemporalMonthCode());
}
}
}
@Test
public void TestCopticCalendarGetTemporalMonthCode() {
RunCEGetTemporalMonthCode(
Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "coptic")));
}
@Test
public void TestEthiopicCalendarGetTemporalMonthCode() {
RunCEGetTemporalMonthCode(
Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "ethiopic")));
}
@Test
public void TestEthiopicAmeteAlemCalendarGetTemporalMonthCode() {
RunCEGetTemporalMonthCode(
Calendar.getInstance(ULocale.ROOT.setKeywordValue("calendar", "ethiopic-amete-alem")));
}
@Test
public void TestGregorianCalendarSetTemporalMonthCode() {
Object[][] cases = {
{ 1911, Calendar.JANUARY, 31, "M01", 0 },
{ 1970, Calendar.FEBRUARY, 22, "M02", 1 },
{ 543, Calendar.MARCH, 3, "M03", 2 },
{ 2340, Calendar.APRIL, 21, "M04", 3 },
{ 1234, Calendar.MAY, 21, "M05", 4 },
{ 1931, Calendar.JUNE, 17, "M06", 5 },
{ 2000, Calendar.JULY, 1, "M07", 6 },
{ 2033, Calendar.AUGUST, 3, "M08", 7 },
{ 2013, Calendar.SEPTEMBER, 9, "M09", 8 },
{ 1849, Calendar.OCTOBER, 31, "M10", 9 },
{ 1433, Calendar.NOVEMBER, 30, "M11", 10 },
{ 2022, Calendar.DECEMBER, 25, "M12", 11 },
};
GregorianCalendar gc1 = new GregorianCalendar();
GregorianCalendar gc2 = new GregorianCalendar();
for (Object[] cas : cases) {
int year = (Integer) cas[0];
int month = (Integer) cas[1];
int date = (Integer) cas[2];
String monthCode = (String) cas[3];
int ordinalMonth = (Integer) cas[4];
gc1.clear();
gc2.clear();
gc1.set(year, month, date);
gc2.set(Calendar.YEAR, year);
gc2.setTemporalMonthCode(monthCode);
gc2.set(Calendar.DATE, date);
assertEquals("by set and setTemporalMonthCode()", gc1, gc2);
String actualMonthCode1 = gc1.getTemporalMonthCode();
String actualMonthCode2 = gc2.getTemporalMonthCode();
assertEquals("getTemporalMonthCode()", actualMonthCode1, actualMonthCode2);
assertEquals("getTemporalMonthCode()", monthCode, actualMonthCode2);
assertEquals("ordinalMonth", ordinalMonth, gc2.get(Calendar.ORDINAL_MONTH));
assertEquals("ordinalMonth",
gc1.get(Calendar.ORDINAL_MONTH), gc2.get(Calendar.ORDINAL_MONTH));
}
}
@Test
public void TestChineseCalendarSetTemporalMonthCode() {
Calendar cc1 = Calendar.getInstance(
ULocale.ROOT.setKeywordValue("calendar", "chinese"));
Calendar cc2 = (Calendar)cc1.clone();
GregorianCalendar gc1 = new GregorianCalendar();
Object[][] cases = {
// https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2022.pdf
{ 2022, Calendar.DECEMBER, 15, 4659, Calendar.NOVEMBER, 22, "M11", false, 10},
// M01L is very hard to find. Cannot find a year has M01L in these several
// centuries.
// M02L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2004.pdf
{ 2004, Calendar.MARCH, 20, 4641, Calendar.FEBRUARY, 30, "M02", false, 1},
{ 2004, Calendar.MARCH, 21, 4641, Calendar.FEBRUARY, 1, "M02L", true, 2},
{ 2004, Calendar.APRIL, 18, 4641, Calendar.FEBRUARY, 29, "M02L", true, 2},
{ 2004, Calendar.APRIL, 19, 4641, Calendar.MARCH, 1, "M03", false, 3},
// M03L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/1995.pdf
{ 1955, Calendar.APRIL, 21, 4592, Calendar.MARCH, 29, "M03", false, 2},
{ 1955, Calendar.APRIL, 22, 4592, Calendar.MARCH, 1, "M03L", true, 3},
{ 1955, Calendar.MAY, 21, 4592, Calendar.MARCH, 30, "M03L", true, 3},
{ 1955, Calendar.MAY, 22, 4592, Calendar.APRIL, 1, "M04", false, 4},
// M12 https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/1996.pdf
{ 1956, Calendar.FEBRUARY, 11, 4592, Calendar.DECEMBER, 30, "M12", false, 12},
// M04L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2001.pdf
{ 2001, Calendar.MAY, 22, 4638, Calendar.APRIL, 30, "M04", false, 3},
{ 2001, Calendar.MAY, 23, 4638, Calendar.APRIL, 1, "M04L", true, 4},
{ 2001, Calendar.JUNE, 20, 4638, Calendar.APRIL, 29, "M04L", true, 4},
{ 2001, Calendar.JUNE, 21, 4638, Calendar.MAY, 1, "M05", false, 5},
// M05L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2009.pdf
{ 2009, Calendar.JUNE, 22, 4646, Calendar.MAY, 30, "M05", false, 4},
{ 2009, Calendar.JUNE, 23, 4646, Calendar.MAY, 1, "M05L", true, 5},
{ 2009, Calendar.JULY, 21, 4646, Calendar.MAY, 29, "M05L", true, 5},
{ 2009, Calendar.JULY, 22, 4646, Calendar.JUNE, 1, "M06", false, 6},
// M06L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2017.pdf
{ 2017, Calendar.JULY, 22, 4654, Calendar.JUNE, 29, "M06", false, 5},
{ 2017, Calendar.JULY, 23, 4654, Calendar.JUNE, 1, "M06L", true, 6},
{ 2017, Calendar.AUGUST, 21, 4654, Calendar.JUNE, 30, "M06L", true, 6},
{ 2017, Calendar.AUGUST, 22, 4654, Calendar.JULY, 1, "M07", false, 7},
// M07L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2006.pdf
{ 2006, Calendar.AUGUST, 23, 4643, Calendar.JULY, 30, "M07", false, 6},
{ 2006, Calendar.AUGUST, 24, 4643, Calendar.JULY, 1, "M07L", true, 7},
{ 2006, Calendar.SEPTEMBER, 21, 4643, Calendar.JULY, 29, "M07L", true, 7},
{ 2006, Calendar.SEPTEMBER, 22, 4643, Calendar.AUGUST, 1, "M08", false, 8},
// M08L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/1995.pdf
{ 1995, Calendar.SEPTEMBER, 24, 4632, Calendar.AUGUST, 30, "M08", false, 7},
{ 1995, Calendar.SEPTEMBER, 25, 4632, Calendar.AUGUST, 1, "M08L", true, 8},
{ 1995, Calendar.OCTOBER, 23, 4632, Calendar.AUGUST, 29, "M08L", true, 8},
{ 1995, Calendar.OCTOBER, 24, 4632, Calendar.SEPTEMBER, 1, "M09", false, 9},
// M09L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2014.pdf
{ 2014, Calendar.OCTOBER, 23, 4651, Calendar.SEPTEMBER, 30, "M09", false, 8},
{ 2014, Calendar.OCTOBER, 24, 4651, Calendar.SEPTEMBER, 1, "M09L", true, 9},
{ 2014, Calendar.NOVEMBER, 21, 4651, Calendar.SEPTEMBER, 29, "M09L", true, 9},
{ 2014, Calendar.NOVEMBER, 22, 4651, Calendar.OCTOBER, 1, "M10", false, 10},
// M10L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/1984.pdf
{ 1984, Calendar.NOVEMBER, 22, 4621, Calendar.OCTOBER, 30, "M10", false, 9},
{ 1984, Calendar.NOVEMBER, 23, 4621, Calendar.OCTOBER, 1, "M10L", true, 10},
{ 1984, Calendar.DECEMBER, 21, 4621, Calendar.OCTOBER, 29, "M10L", true, 10},
{ 1984, Calendar.DECEMBER, 22, 4621, Calendar.NOVEMBER, 1, "M11", false, 11},
// M11L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2033.pdf
// https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2034.pdf
{ 2033, Calendar.DECEMBER, 21, 4670, Calendar.NOVEMBER, 30, "M11", false, 10},
{ 2033, Calendar.DECEMBER, 22, 4670, Calendar.NOVEMBER, 1, "M11L", true, 11},
{ 2034, Calendar.JANUARY, 19, 4670, Calendar.NOVEMBER, 29, "M11L", true, 11},
{ 2034, Calendar.JANUARY, 20, 4670, Calendar.DECEMBER, 1, "M12", false, 12},
// M12L is very hard to find. Cannot find a year has M01L in these several
// centuries.
};
for (Object[] cas : cases) {
int gYear = (Integer) cas[0];
int gMonth = (Integer) cas[1];
int gDate = (Integer) cas[2];
int cYear = (Integer) cas[3];
int cMonth = (Integer) cas[4];
int cDate = (Integer) cas[5];
String cMonthCode = (String) cas[6];
boolean cLeapMonth = (Boolean) cas[7];
int cOrdinalMonth = (Integer) cas[8];
gc1.clear();
cc1.clear();
cc2.clear();
gc1.set(gYear, gMonth, gDate);
cc1.setTime(gc1.getTime());
cc2.set(Calendar.EXTENDED_YEAR, cYear);
cc2.setTemporalMonthCode(cMonthCode);
cc2.set(Calendar.DATE, cDate);
assertEquals("year", cYear, cc1.get(Calendar.EXTENDED_YEAR));
assertEquals("month", cMonth, cc1.get(Calendar.MONTH));
assertEquals("date", cDate, cc1.get(Calendar.DATE));
assertEquals("is_leap_month", cLeapMonth ? 1 : 0,
cc1.get(Calendar.IS_LEAP_MONTH));
assertEquals("getTemporalMonthCode()", cMonthCode,
cc1.getTemporalMonthCode());
assertEquals("ordinalMonth", cOrdinalMonth, cc1.get(Calendar.ORDINAL_MONTH));
assertEquals("by set() and setTemporalMonthCode()", cc1, cc2);
}
}
@Test
public void TestHebrewCalendarSetTemporalMonthCode() {
Calendar hc1 = Calendar.getInstance(
ULocale.ROOT.setKeywordValue("calendar", "hebrew"));
Calendar hc2 = (Calendar)hc1.clone();
GregorianCalendar gc1 = new GregorianCalendar();
Object[][] cases = {
{ 2022, Calendar.JANUARY, 11, 5782, HebrewCalendar.SHEVAT, 9, "M05", 4},
{ 2022, Calendar.FEBRUARY, 12, 5782, HebrewCalendar.ADAR_1, 11, "M05L", 5},
{ 2022, Calendar.MARCH, 13, 5782, HebrewCalendar.ADAR, 10, "M06", 6},
{ 2022, Calendar.APRIL, 14, 5782, HebrewCalendar.NISAN, 13, "M07", 7},
{ 2022, Calendar.MAY, 15, 5782, HebrewCalendar.IYAR, 14, "M08", 8},
{ 2022, Calendar.JUNE, 16, 5782, HebrewCalendar.SIVAN, 17, "M09", 9},
{ 2022, Calendar.JULY, 17, 5782, HebrewCalendar.TAMUZ, 18, "M10", 10},
{ 2022, Calendar.AUGUST, 18, 5782, HebrewCalendar.AV, 21, "M11", 11},
{ 2022, Calendar.SEPTEMBER, 19, 5782, HebrewCalendar.ELUL, 23, "M12", 12},
{ 2022, Calendar.OCTOBER, 20, 5783, HebrewCalendar.TISHRI, 25, "M01", 0},
{ 2022, Calendar.NOVEMBER, 21, 5783, HebrewCalendar.HESHVAN, 27, "M02", 1},
{ 2022, Calendar.DECEMBER, 22, 5783, HebrewCalendar.KISLEV, 28, "M03", 2},
{ 2023, Calendar.JANUARY, 20, 5783, HebrewCalendar.TEVET, 27, "M04", 3},
};
for (Object[] cas : cases) {
int gYear = (Integer) cas[0];
int gMonth = (Integer) cas[1];
int gDate = (Integer) cas[2];
int hYear = (Integer) cas[3];
int hMonth = (Integer) cas[4];
int hDate = (Integer) cas[5];
String hMonthCode = (String) cas[6];
int hOrdinalMonth = (Integer) cas[7];
gc1.clear();
hc1.clear();
hc2.clear();
gc1.set(gYear, gMonth, gDate);
hc1.setTime(gc1.getTime());
hc2.set(Calendar.EXTENDED_YEAR, hYear);
hc2.setTemporalMonthCode(hMonthCode);
hc2.set(Calendar.DATE, hDate);
assertEquals("year", hYear, hc1.get(Calendar.EXTENDED_YEAR));
assertEquals("month", hMonth, hc1.get(Calendar.MONTH));
assertEquals("date", hDate, hc1.get(Calendar.DATE));
assertEquals("getTemporalMonthCode()", hMonthCode,
hc1.getTemporalMonthCode());
assertEquals("by set() and setTemporalMonthCode()", hc1, hc2);
assertEquals("ordinalMonth", hOrdinalMonth, hc1.get(Calendar.ORDINAL_MONTH));
assertEquals("ordinalMonth", hOrdinalMonth, hc2.get(Calendar.ORDINAL_MONTH));
}
}
@Test
public void TestCopticCalendarSetTemporalMonthCode() {
Calendar cc1 = Calendar.getInstance(
ULocale.ROOT.setKeywordValue("calendar", "coptic"));
Calendar cc2 = (Calendar)cc1.clone();
GregorianCalendar gc1 = new GregorianCalendar();
Object[][] cases = {
{ 1900, Calendar.JANUARY, 1, 1616, CopticCalendar.KIAHK, 23, "M04", 3},
{ 1900, Calendar.SEPTEMBER, 6, 1616, CopticCalendar.NASIE, 1, "M13", 12},
{ 1900, Calendar.SEPTEMBER, 10, 1616, CopticCalendar.NASIE, 5, "M13", 12},
{ 1900, Calendar.SEPTEMBER, 11, 1617, CopticCalendar.TOUT, 1, "M01", 0},
{ 2022, Calendar.JANUARY, 11, 1738, CopticCalendar.TOBA, 3, "M05", 4},
{ 2022, Calendar.FEBRUARY, 12, 1738, CopticCalendar.AMSHIR, 5, "M06", 5},
{ 2022, Calendar.MARCH, 13, 1738, CopticCalendar.BARAMHAT, 4, "M07", 6},
{ 2022, Calendar.APRIL, 14, 1738, CopticCalendar.BARAMOUDA, 6, "M08", 7},
{ 2022, Calendar.MAY, 15, 1738, CopticCalendar.BASHANS, 7, "M09", 8},
{ 2022, Calendar.JUNE, 16, 1738, CopticCalendar.PAONA, 9, "M10", 9},
{ 2022, Calendar.JULY, 17, 1738, CopticCalendar.EPEP, 10, "M11", 10},
{ 2022, Calendar.AUGUST, 18, 1738, CopticCalendar.MESRA, 12, "M12", 11},
{ 2022, Calendar.SEPTEMBER, 6, 1738, CopticCalendar.NASIE, 1, "M13", 12},
{ 2022, Calendar.SEPTEMBER, 10, 1738, CopticCalendar.NASIE, 5, "M13", 12},
{ 2022, Calendar.SEPTEMBER, 11, 1739, CopticCalendar.TOUT, 1, "M01", 0},
{ 2022, Calendar.SEPTEMBER, 19, 1739, CopticCalendar.TOUT, 9, "M01", 0},
{ 2022, Calendar.OCTOBER, 20, 1739, CopticCalendar.BABA, 10, "M02", 1},
{ 2022, Calendar.NOVEMBER, 21, 1739, CopticCalendar.HATOR, 12, "M03", 2},
{ 2022, Calendar.DECEMBER, 22, 1739, CopticCalendar.KIAHK, 13, "M04", 3},
{ 2023, Calendar.JANUARY, 1, 1739, CopticCalendar.KIAHK, 23, "M04", 3},
{ 2023, Calendar.SEPTEMBER, 6, 1739, CopticCalendar.NASIE, 1, "M13", 12},
{ 2023, Calendar.SEPTEMBER, 11, 1739, CopticCalendar.NASIE, 6, "M13", 12},
{ 2023, Calendar.SEPTEMBER, 12, 1740, CopticCalendar.TOUT, 1, "M01", 0},
{ 2030, Calendar.JANUARY, 1, 1746, CopticCalendar.KIAHK, 23, "M04", 3},
{ 2030, Calendar.SEPTEMBER, 6, 1746, CopticCalendar.NASIE, 1, "M13", 12},
{ 2030, Calendar.SEPTEMBER, 10, 1746, CopticCalendar.NASIE, 5, "M13", 12},
{ 2030, Calendar.SEPTEMBER, 11, 1747, CopticCalendar.TOUT, 1, "M01", 0},
};
for (Object[] cas : cases) {
int gYear = (Integer) cas[0];
int gMonth = (Integer) cas[1];
int gDate = (Integer) cas[2];
int cYear = (Integer) cas[3];
int cMonth = (Integer) cas[4];
int cDate = (Integer) cas[5];
String cMonthCode = (String) cas[6];
int cOrdinalMonth = (Integer) cas[7];
gc1.clear();
cc1.clear();
cc2.clear();
gc1.set(gYear, gMonth, gDate);
cc1.setTime(gc1.getTime());
cc2.set(Calendar.EXTENDED_YEAR, cYear);
cc2.setTemporalMonthCode(cMonthCode);
cc2.set(Calendar.DATE, cDate);
assertEquals("year", cYear, cc1.get(Calendar.EXTENDED_YEAR));
assertEquals("month", cMonth, cc1.get(Calendar.MONTH));
assertEquals("date", cDate, cc1.get(Calendar.DATE));
assertEquals("getTemporalMonthCode()", cMonthCode,
cc1.getTemporalMonthCode());
assertEquals("getTimeInMillis()", cc1.getTimeInMillis(), cc2.getTimeInMillis());
assertEquals("by set() and setTemporalMonthCode()", cc1, cc2);
assertEquals("ordinalMonth", cOrdinalMonth, cc1.get(Calendar.ORDINAL_MONTH));
assertEquals("ordinalMonth", cOrdinalMonth, cc2.get(Calendar.ORDINAL_MONTH));
}
}
@Test
public void TestEthiopicCalendarSetTemporalMonthCode() {
Calendar ec1 = Calendar.getInstance(
ULocale.ROOT.setKeywordValue("calendar", "ethiopic"));
Calendar ec2 = (Calendar)ec1.clone();
GregorianCalendar gc1 = new GregorianCalendar();
Object[][] cases = {
{ 1900, Calendar.JANUARY, 1, 1892, EthiopicCalendar.TAHSAS, 23, "M04", 3},
{ 1900, Calendar.SEPTEMBER, 6, 1892, EthiopicCalendar.PAGUMEN, 1, "M13", 12},
{ 1900, Calendar.SEPTEMBER, 10, 1892, EthiopicCalendar.PAGUMEN, 5, "M13", 12},
{ 1900, Calendar.SEPTEMBER, 11, 1893, EthiopicCalendar.MESKEREM, 1, "M01", 0},
{ 2022, Calendar.JANUARY, 11, 2014, EthiopicCalendar.TER, 3, "M05", 4},
{ 2022, Calendar.FEBRUARY, 12, 2014, EthiopicCalendar.YEKATIT, 5, "M06", 5},
{ 2022, Calendar.MARCH, 13, 2014, EthiopicCalendar.MEGABIT, 4, "M07", 6},
{ 2022, Calendar.APRIL, 14, 2014, EthiopicCalendar.MIAZIA, 6, "M08", 7},
{ 2022, Calendar.MAY, 15, 2014, EthiopicCalendar.GENBOT, 7, "M09", 8},
{ 2022, Calendar.JUNE, 16, 2014, EthiopicCalendar.SENE, 9, "M10", 9},
{ 2022, Calendar.JULY, 17, 2014, EthiopicCalendar.HAMLE, 10, "M11", 10},
{ 2022, Calendar.AUGUST, 18, 2014, EthiopicCalendar.NEHASSE, 12, "M12", 11},
{ 2022, Calendar.SEPTEMBER, 6, 2014, EthiopicCalendar.PAGUMEN, 1, "M13", 12},
{ 2022, Calendar.SEPTEMBER, 10, 2014, EthiopicCalendar.PAGUMEN, 5, "M13", 12},
{ 2022, Calendar.SEPTEMBER, 11, 2015, EthiopicCalendar.MESKEREM, 1, "M01", 0},
{ 2022, Calendar.SEPTEMBER, 19, 2015, EthiopicCalendar.MESKEREM, 9, "M01", 0},
{ 2022, Calendar.OCTOBER, 20, 2015, EthiopicCalendar.TEKEMT, 10, "M02", 1},
{ 2022, Calendar.NOVEMBER, 21, 2015, EthiopicCalendar.HEDAR, 12, "M03", 2},
{ 2022, Calendar.DECEMBER, 22, 2015, EthiopicCalendar.TAHSAS, 13, "M04", 3},
{ 2023, Calendar.JANUARY, 1, 2015, EthiopicCalendar.TAHSAS, 23, "M04", 3},
{ 2023, Calendar.SEPTEMBER, 6, 2015, EthiopicCalendar.PAGUMEN, 1, "M13", 12},
{ 2023, Calendar.SEPTEMBER, 11, 2015, EthiopicCalendar.PAGUMEN, 6, "M13", 12},
{ 2023, Calendar.SEPTEMBER, 12, 2016, EthiopicCalendar.MESKEREM, 1, "M01", 0},
{ 2030, Calendar.JANUARY, 1, 2022, EthiopicCalendar.TAHSAS, 23, "M04", 3},
{ 2030, Calendar.SEPTEMBER, 6, 2022, EthiopicCalendar.PAGUMEN, 1, "M13", 12},
{ 2030, Calendar.SEPTEMBER, 10, 2022, EthiopicCalendar.PAGUMEN, 5, "M13", 12},
{ 2030, Calendar.SEPTEMBER, 11, 2023, EthiopicCalendar.MESKEREM, 1, "M01", 0},
};
for (Object[] cas : cases) {
int gYear = (Integer) cas[0];
int gMonth = (Integer) cas[1];
int gDate = (Integer) cas[2];
int eYear = (Integer) cas[3];
int eMonth = (Integer) cas[4];
int eDate = (Integer) cas[5];
String eMonthCode = (String) cas[6];
int eOrdinalMonth = (Integer) cas[7];
gc1.clear();
ec1.clear();
ec2.clear();
gc1.set(gYear, gMonth, gDate);
ec1.setTime(gc1.getTime());
ec2.set(Calendar.EXTENDED_YEAR, eYear);
ec2.setTemporalMonthCode(eMonthCode);
ec2.set(Calendar.DATE, eDate);
assertEquals("year", eYear, ec1.get(Calendar.EXTENDED_YEAR));
assertEquals("month", eMonth, ec1.get(Calendar.MONTH));
assertEquals("date", eDate, ec1.get(Calendar.DATE));
assertEquals("getTemporalMonthCode()", eMonthCode,
ec1.getTemporalMonthCode());
assertEquals("by set() and setTemporalMonthCode()",
ec1, ec2);
assertEquals("ordinalMonth", eOrdinalMonth, ec1.get(Calendar.ORDINAL_MONTH));
assertEquals("ordinalMonth", eOrdinalMonth, ec2.get(Calendar.ORDINAL_MONTH));
}
}
}