ICU-9255 Adding the traditional Korean calendar (Dangi) support to ICU4J.
X-SVN-Rev: 32426
This commit is contained in:
parent
f75135af94
commit
331d9f169f
@ -34,6 +34,10 @@ public class CalendarData {
|
|||||||
fMainType = "gregorian";
|
fMainType = "gregorian";
|
||||||
fFallbackType = null;
|
fFallbackType = null;
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: Until CLDR supports "dangi" calendar type
|
||||||
|
if (type.equalsIgnoreCase("dangi")) {
|
||||||
|
type = "chinese";
|
||||||
|
}
|
||||||
fMainType = type;
|
fMainType = type;
|
||||||
fFallbackType = "gregorian";
|
fFallbackType = "gregorian";
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,12 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
|||||||
if ( calendarTypeToUse == null ) {
|
if ( calendarTypeToUse == null ) {
|
||||||
calendarTypeToUse = "gregorian"; // fallback
|
calendarTypeToUse = "gregorian"; // fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Until CLDR supports "dangi" calendar type
|
||||||
|
if (calendarTypeToUse.equalsIgnoreCase("dangi")) {
|
||||||
|
calendarTypeToUse = "chinese";
|
||||||
|
}
|
||||||
|
|
||||||
// Get data for that calendar
|
// Get data for that calendar
|
||||||
ICUResourceBundle calBundle = rb.getWithFallback("calendar");
|
ICUResourceBundle calBundle = rb.getWithFallback("calendar");
|
||||||
ICUResourceBundle calTypeBundle = calBundle.getWithFallback(calendarTypeToUse);
|
ICUResourceBundle calTypeBundle = calBundle.getWithFallback(calendarTypeToUse);
|
||||||
|
@ -1739,6 +1739,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||||||
"ethiopic",
|
"ethiopic",
|
||||||
"ethiopic-amete-alem",
|
"ethiopic-amete-alem",
|
||||||
"iso8601",
|
"iso8601",
|
||||||
|
"dangi",
|
||||||
};
|
};
|
||||||
|
|
||||||
// must be in the order of calTypes above
|
// must be in the order of calTypes above
|
||||||
@ -1756,6 +1757,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||||||
private static final int CALTYPE_ETHIOPIC = 11;
|
private static final int CALTYPE_ETHIOPIC = 11;
|
||||||
private static final int CALTYPE_ETHIOPIC_AMETE_ALEM = 12;
|
private static final int CALTYPE_ETHIOPIC_AMETE_ALEM = 12;
|
||||||
private static final int CALTYPE_ISO8601 = 13;
|
private static final int CALTYPE_ISO8601 = 13;
|
||||||
|
private static final int CALTYPE_DANGI = 14;
|
||||||
private static final int CALTYPE_UNKNOWN = -1;
|
private static final int CALTYPE_UNKNOWN = -1;
|
||||||
|
|
||||||
private static int getCalendarTypeForLocale(ULocale l) {
|
private static int getCalendarTypeForLocale(ULocale l) {
|
||||||
@ -1901,6 +1903,9 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||||||
cal = new EthiopicCalendar(zone, locale);
|
cal = new EthiopicCalendar(zone, locale);
|
||||||
((EthiopicCalendar)cal).setAmeteAlemEra(true);
|
((EthiopicCalendar)cal).setAmeteAlemEra(true);
|
||||||
break;
|
break;
|
||||||
|
case CALTYPE_DANGI:
|
||||||
|
cal = new DangiCalendar(zone, locale);
|
||||||
|
break;
|
||||||
case CALTYPE_ISO8601:
|
case CALTYPE_ISO8601:
|
||||||
// Only differs week numbering rule from Gregorian
|
// Only differs week numbering rule from Gregorian
|
||||||
cal = new GregorianCalendar(zone, locale);
|
cal = new GregorianCalendar(zone, locale);
|
||||||
|
@ -101,6 +101,17 @@ public class ChineseCalendar extends Calendar {
|
|||||||
// the object, not class level, under the assumption that typical
|
// the object, not class level, under the assumption that typical
|
||||||
// usage will be to have one instance of ChineseCalendar at a time.
|
// usage will be to have one instance of ChineseCalendar at a time.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The start year of this Chinese calendar instance.
|
||||||
|
*/
|
||||||
|
private int epochYear;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The zone used for the astronomical calculation of this Chinese
|
||||||
|
* calendar instance.
|
||||||
|
*/
|
||||||
|
private TimeZone zoneAstro;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We have one instance per object, and we don't synchronize it because
|
* We have one instance per object, and we don't synchronize it because
|
||||||
* Calendar doesn't support multithreaded execution in the first place.
|
* Calendar doesn't support multithreaded execution in the first place.
|
||||||
@ -135,8 +146,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
* @stable ICU 2.8
|
* @stable ICU 2.8
|
||||||
*/
|
*/
|
||||||
public ChineseCalendar() {
|
public ChineseCalendar() {
|
||||||
super();
|
this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT), CHINESE_EPOCH_YEAR, CHINA_ZONE);
|
||||||
setTimeInMillis(System.currentTimeMillis());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,7 +156,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
* @stable ICU 4.0
|
* @stable ICU 4.0
|
||||||
*/
|
*/
|
||||||
public ChineseCalendar(Date date) {
|
public ChineseCalendar(Date date) {
|
||||||
super();
|
this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT), CHINESE_EPOCH_YEAR, CHINA_ZONE);
|
||||||
setTime(date);
|
setTime(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,19 +174,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
* @stable ICU 4.0
|
* @stable ICU 4.0
|
||||||
*/
|
*/
|
||||||
public ChineseCalendar(int year, int month, int isLeapMonth, int date) {
|
public ChineseCalendar(int year, int month, int isLeapMonth, int date) {
|
||||||
super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
|
this(year, month, isLeapMonth, date, 0, 0, 0);
|
||||||
|
|
||||||
// We need to set the current time once to initialize the ChineseCalendar's
|
|
||||||
// ERA field to be the current era.
|
|
||||||
setTimeInMillis(System.currentTimeMillis());
|
|
||||||
// Then we need to clean up time fields
|
|
||||||
this.set(MILLISECONDS_IN_DAY, 0);
|
|
||||||
|
|
||||||
// Then set the given field values.
|
|
||||||
this.set(YEAR, year);
|
|
||||||
this.set(MONTH, month);
|
|
||||||
this.set(IS_LEAP_MONTH, isLeapMonth);
|
|
||||||
this.set(DATE, date);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -201,12 +199,12 @@ public class ChineseCalendar extends Calendar {
|
|||||||
public ChineseCalendar(int year, int month, int isLeapMonth, int date, int hour,
|
public ChineseCalendar(int year, int month, int isLeapMonth, int date, int hour,
|
||||||
int minute, int second)
|
int minute, int second)
|
||||||
{
|
{
|
||||||
super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
|
this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT), CHINESE_EPOCH_YEAR, CHINA_ZONE);
|
||||||
|
|
||||||
// We need to set the current time once to initialize the ChineseCalendar's
|
// The current time is set at this point, so ERA field is already
|
||||||
// ERA field to be the current era.
|
// set to the current era.
|
||||||
setTimeInMillis(System.currentTimeMillis());
|
|
||||||
// Then set 0 to millisecond field
|
// Then we need to clean up time fields
|
||||||
this.set(MILLISECOND, 0);
|
this.set(MILLISECOND, 0);
|
||||||
|
|
||||||
// Then, set the given field values.
|
// Then, set the given field values.
|
||||||
@ -235,21 +233,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
*/
|
*/
|
||||||
public ChineseCalendar(int era, int year, int month, int isLeapMonth, int date)
|
public ChineseCalendar(int era, int year, int month, int isLeapMonth, int date)
|
||||||
{
|
{
|
||||||
super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
|
this(era, year, month, isLeapMonth, 0, 0, 0);
|
||||||
|
|
||||||
// We need to set the current time once to initialize the ChineseCalendar's
|
|
||||||
// ERA field to be the current era.
|
|
||||||
setTimeInMillis(System.currentTimeMillis());
|
|
||||||
|
|
||||||
// Then we need to clean up time fields
|
|
||||||
this.set(MILLISECONDS_IN_DAY, 0);
|
|
||||||
|
|
||||||
// Then set the given field values.
|
|
||||||
this.set(ERA, era);
|
|
||||||
this.set(YEAR, year);
|
|
||||||
this.set(MONTH, month);
|
|
||||||
this.set(IS_LEAP_MONTH, isLeapMonth);
|
|
||||||
this.set(DATE, date);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -275,13 +259,9 @@ public class ChineseCalendar extends Calendar {
|
|||||||
public ChineseCalendar(int era, int year, int month, int isLeapMonth, int date, int hour,
|
public ChineseCalendar(int era, int year, int month, int isLeapMonth, int date, int hour,
|
||||||
int minute, int second)
|
int minute, int second)
|
||||||
{
|
{
|
||||||
super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
|
this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT), CHINESE_EPOCH_YEAR, CHINA_ZONE);
|
||||||
|
|
||||||
// We need to set the current time once to initialize the ChineseCalendar's
|
// Set 0 to millisecond field
|
||||||
// ERA field to be the current era.
|
|
||||||
setTimeInMillis(System.currentTimeMillis());
|
|
||||||
|
|
||||||
// Then set 0 to millisecond field
|
|
||||||
this.set(MILLISECOND, 0);
|
this.set(MILLISECOND, 0);
|
||||||
|
|
||||||
// Then, set the given field values.
|
// Then, set the given field values.
|
||||||
@ -302,8 +282,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
* @stable ICU 4.0
|
* @stable ICU 4.0
|
||||||
*/
|
*/
|
||||||
public ChineseCalendar(Locale aLocale) {
|
public ChineseCalendar(Locale aLocale) {
|
||||||
this(TimeZone.getDefault(), aLocale);
|
this(TimeZone.getDefault(), ULocale.forLocale(aLocale), CHINESE_EPOCH_YEAR, CHINA_ZONE);
|
||||||
setTimeInMillis(System.currentTimeMillis());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -314,8 +293,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
* @stable ICU 4.0
|
* @stable ICU 4.0
|
||||||
*/
|
*/
|
||||||
public ChineseCalendar(TimeZone zone) {
|
public ChineseCalendar(TimeZone zone) {
|
||||||
super(zone, ULocale.getDefault(Category.FORMAT));
|
this(zone, ULocale.getDefault(Category.FORMAT), CHINESE_EPOCH_YEAR, CHINA_ZONE);
|
||||||
setTimeInMillis(System.currentTimeMillis());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -326,8 +304,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
* @stable ICU 2.8
|
* @stable ICU 2.8
|
||||||
*/
|
*/
|
||||||
public ChineseCalendar(TimeZone zone, Locale aLocale) {
|
public ChineseCalendar(TimeZone zone, Locale aLocale) {
|
||||||
super(zone, aLocale);
|
this(zone, ULocale.forLocale(aLocale), CHINESE_EPOCH_YEAR, CHINA_ZONE);
|
||||||
setTimeInMillis(System.currentTimeMillis());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -338,8 +315,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
* @stable ICU 4.0
|
* @stable ICU 4.0
|
||||||
*/
|
*/
|
||||||
public ChineseCalendar(ULocale locale) {
|
public ChineseCalendar(ULocale locale) {
|
||||||
this(TimeZone.getDefault(), locale);
|
this(TimeZone.getDefault(), locale, CHINESE_EPOCH_YEAR, CHINA_ZONE);
|
||||||
setTimeInMillis(System.currentTimeMillis());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -350,7 +326,20 @@ public class ChineseCalendar extends Calendar {
|
|||||||
* @stable ICU 3.2
|
* @stable ICU 3.2
|
||||||
*/
|
*/
|
||||||
public ChineseCalendar(TimeZone zone, ULocale locale) {
|
public ChineseCalendar(TimeZone zone, ULocale locale) {
|
||||||
|
this(zone, locale, CHINESE_EPOCH_YEAR, CHINA_ZONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a <code>ChineseCalenar</code> based on the current time
|
||||||
|
* with the given time zone, the locale, the epoch year and the time zone
|
||||||
|
* used for astronomical calculation.
|
||||||
|
* @internal
|
||||||
|
* @deprecated This API is ICU internal only.
|
||||||
|
*/
|
||||||
|
protected ChineseCalendar(TimeZone zone, ULocale locale, int epochYear, TimeZone zoneAstroCalc) {
|
||||||
super(zone, locale);
|
super(zone, locale);
|
||||||
|
this.epochYear = epochYear;
|
||||||
|
this.zoneAstro = zoneAstroCalc;
|
||||||
setTimeInMillis(System.currentTimeMillis());
|
setTimeInMillis(System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,7 +444,8 @@ public class ChineseCalendar extends Calendar {
|
|||||||
year = internalGet(EXTENDED_YEAR, 1); // Default to year 1
|
year = internalGet(EXTENDED_YEAR, 1); // Default to year 1
|
||||||
} else {
|
} else {
|
||||||
int cycle = internalGet(ERA, 1) - 1; // 0-based cycle
|
int cycle = internalGet(ERA, 1) - 1; // 0-based cycle
|
||||||
year = cycle * 60 + internalGet(YEAR, 1);
|
// adjust to the instance specific epoch
|
||||||
|
year = cycle * 60 + internalGet(YEAR, 1) - (epochYear - CHINESE_EPOCH_YEAR);
|
||||||
}
|
}
|
||||||
return year;
|
return year;
|
||||||
}
|
}
|
||||||
@ -652,11 +642,11 @@ public class ChineseCalendar extends Calendar {
|
|||||||
private static final int CHINESE_EPOCH_YEAR = -2636; // Gregorian year
|
private static final int CHINESE_EPOCH_YEAR = -2636; // Gregorian year
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The offset from GMT in milliseconds at which we perform astronomical
|
* The time zone used for performing astronomical computations.
|
||||||
* computations. Some sources use a different historically accurate
|
* Some sources use a different historically accurate
|
||||||
* offset of GMT+7:45:40 for years before 1929; we do not do this.
|
* offset of GMT+7:45:40 for years before 1929; we do not do this.
|
||||||
*/
|
*/
|
||||||
private static final long CHINA_OFFSET = 8*ONE_HOUR;
|
private static final TimeZone CHINA_ZONE = new SimpleTimeZone(8 * ONE_HOUR, "CHINA_ZONE").freeze();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value to be added or subtracted from the local days of a new moon to
|
* Value to be added or subtracted from the local days of a new moon to
|
||||||
@ -667,20 +657,28 @@ public class ChineseCalendar extends Calendar {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert local days to UTC epoch milliseconds.
|
* Convert local days to UTC epoch milliseconds.
|
||||||
* @param days days after January 1, 1970 0:00 Asia/Shanghai
|
* This is not an accurate conversion in terms that getTimezoneOffset
|
||||||
|
* takes the milliseconds in GMT (not local time). In theory, more
|
||||||
|
* accurate algorithm can be implemented but practically we do not need
|
||||||
|
* to go through that complication as long as the historically timezone
|
||||||
|
* changes did not happen around the 'tricky' new moon (new moon around
|
||||||
|
* the midnight).
|
||||||
|
*
|
||||||
|
* @param days days after January 1, 1970 0:00 in the astronomical base zone
|
||||||
* @return milliseconds after January 1, 1970 0:00 GMT
|
* @return milliseconds after January 1, 1970 0:00 GMT
|
||||||
*/
|
*/
|
||||||
private static final long daysToMillis(int days) {
|
private final long daysToMillis(int days) {
|
||||||
return (days * ONE_DAY) - CHINA_OFFSET;
|
long millis = days * ONE_DAY;
|
||||||
|
return millis - zoneAstro.getOffset(millis);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert UTC epoch milliseconds to local days.
|
* Convert UTC epoch milliseconds to local days.
|
||||||
* @param millis milliseconds after January 1, 1970 0:00 GMT
|
* @param millis milliseconds after January 1, 1970 0:00 GMT
|
||||||
* @return days after January 1, 1970 0:00 Asia/Shanghai
|
* @return days days after January 1, 1970 0:00 in the astronomical base zone
|
||||||
*/
|
*/
|
||||||
private static final int millisToDays(long millis) {
|
private final int millisToDays(long millis) {
|
||||||
return (int) floorDivide(millis + CHINA_OFFSET, ONE_DAY);
|
return (int) floorDivide(millis + zoneAstro.getOffset(millis), ONE_DAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
@ -787,9 +785,9 @@ public class ChineseCalendar extends Calendar {
|
|||||||
/**
|
/**
|
||||||
* Return true if there is a leap month on or after month newMoon1 and
|
* Return true if there is a leap month on or after month newMoon1 and
|
||||||
* at or before month newMoon2.
|
* at or before month newMoon2.
|
||||||
* @param newMoon1 days after January 1, 1970 0:00 Asia/Shanghai of a
|
* @param newMoon1 days after January 1, 1970 0:00 astronomical base zone of a
|
||||||
* new moon
|
* new moon
|
||||||
* @param newMoon2 days after January 1, 1970 0:00 Asia/Shanghai of a
|
* @param newMoon2 days after January 1, 1970 0:00 astronomical base zone of a
|
||||||
* new moon
|
* new moon
|
||||||
*/
|
*/
|
||||||
private boolean isLeapMonthBetween(int newMoon1, int newMoon2) {
|
private boolean isLeapMonthBetween(int newMoon1, int newMoon2) {
|
||||||
@ -841,7 +839,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
* <code>handleComputeMonthStart()</code>.
|
* <code>handleComputeMonthStart()</code>.
|
||||||
*
|
*
|
||||||
* <p>As a side effect, this method sets {@link #isLeapYear}.
|
* <p>As a side effect, this method sets {@link #isLeapYear}.
|
||||||
* @param days days after January 1, 1970 0:00 Asia/Shanghai of the
|
* @param days days after January 1, 1970 0:00 astronomical base zone of the
|
||||||
* date to compute fields for
|
* date to compute fields for
|
||||||
* @param gyear the Gregorian year of the given date
|
* @param gyear the Gregorian year of the given date
|
||||||
* @param gmonth the Gregorian month of the given date
|
* @param gmonth the Gregorian month of the given date
|
||||||
@ -891,18 +889,21 @@ public class ChineseCalendar extends Calendar {
|
|||||||
|
|
||||||
if (setAllFields) {
|
if (setAllFields) {
|
||||||
|
|
||||||
int year = gyear - CHINESE_EPOCH_YEAR;
|
// Extended year and cycle year is based on the epoch year
|
||||||
|
int extended_year = gyear - epochYear;
|
||||||
|
int cycle_year = gyear - CHINESE_EPOCH_YEAR;
|
||||||
if (month < 11 ||
|
if (month < 11 ||
|
||||||
gmonth >= JULY) {
|
gmonth >= JULY) {
|
||||||
year++;
|
extended_year++;
|
||||||
|
cycle_year++;
|
||||||
}
|
}
|
||||||
int dayOfMonth = days - thisMoon + 1;
|
int dayOfMonth = days - thisMoon + 1;
|
||||||
|
|
||||||
internalSet(EXTENDED_YEAR, year);
|
internalSet(EXTENDED_YEAR, extended_year);
|
||||||
|
|
||||||
// 0->0,60 1->1,1 60->1,60 61->2,1 etc.
|
// 0->0,60 1->1,1 60->1,60 61->2,1 etc.
|
||||||
int[] yearOfCycle = new int[1];
|
int[] yearOfCycle = new int[1];
|
||||||
int cycle = floorDivide(year-1, 60, yearOfCycle);
|
int cycle = floorDivide(cycle_year-1, 60, yearOfCycle);
|
||||||
internalSet(ERA, cycle+1);
|
internalSet(ERA, cycle+1);
|
||||||
internalSet(YEAR, yearOfCycle[0]+1);
|
internalSet(YEAR, yearOfCycle[0]+1);
|
||||||
|
|
||||||
@ -927,7 +928,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
/**
|
/**
|
||||||
* Return the Chinese new year of the given Gregorian year.
|
* Return the Chinese new year of the given Gregorian year.
|
||||||
* @param gyear a Gregorian year
|
* @param gyear a Gregorian year
|
||||||
* @return days after January 1, 1970 0:00 Asia/Shanghai of the
|
* @return days after January 1, 1970 0:00 astronomical base zone of the
|
||||||
* Chinese new year of the given year (this will be a new moon)
|
* Chinese new year of the given year (this will be a new moon)
|
||||||
*/
|
*/
|
||||||
private int newYear(int gyear) {
|
private int newYear(int gyear) {
|
||||||
@ -977,7 +978,7 @@ public class ChineseCalendar extends Calendar {
|
|||||||
month = rem[0];
|
month = rem[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
int gyear = eyear + CHINESE_EPOCH_YEAR - 1; // Gregorian year
|
int gyear = eyear + epochYear - 1; // Gregorian year
|
||||||
int newYear = newYear(gyear);
|
int newYear = newYear(gyear);
|
||||||
int newMoon = newMoonNear(newYear + month * 29, true);
|
int newMoon = newMoonNear(newYear + month * 29, true);
|
||||||
|
|
||||||
@ -1022,8 +1023,11 @@ public class ChineseCalendar extends Calendar {
|
|||||||
private void readObject(ObjectInputStream stream)
|
private void readObject(ObjectInputStream stream)
|
||||||
throws IOException, ClassNotFoundException
|
throws IOException, ClassNotFoundException
|
||||||
{
|
{
|
||||||
|
epochYear = CHINESE_EPOCH_YEAR;
|
||||||
|
zoneAstro = CHINA_ZONE;
|
||||||
|
|
||||||
stream.defaultReadObject();
|
stream.defaultReadObject();
|
||||||
|
|
||||||
/* set up the transient caches... */
|
/* set up the transient caches... */
|
||||||
astro = new CalendarAstronomer();
|
astro = new CalendarAstronomer();
|
||||||
winterSolsticeCache = new CalendarCache();
|
winterSolsticeCache = new CalendarCache();
|
||||||
|
125
icu4j/main/classes/core/src/com/ibm/icu/util/DangiCalendar.java
Normal file
125
icu4j/main/classes/core/src/com/ibm/icu/util/DangiCalendar.java
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
*******************************************************************************
|
||||||
|
* Copyright (C) 2012, International Business Machines Corporation and *
|
||||||
|
* others. All Rights Reserved. *
|
||||||
|
*******************************************************************************
|
||||||
|
*/
|
||||||
|
package com.ibm.icu.util;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.ibm.icu.util.ULocale.Category;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>DangiCalendar</code> is a concrete subclass of {@link Calendar}
|
||||||
|
* that implements a traditional Korean calendar.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @deprecated This API is ICU internal only.
|
||||||
|
*/
|
||||||
|
public class DangiCalendar extends ChineseCalendar {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 8156297445349501985L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The start year of the Korean traditional calendar (Dan-gi) is the inaugural
|
||||||
|
* year of Dan-gun (BC 2333).
|
||||||
|
*/
|
||||||
|
private static final int DANGI_EPOCH_YEAR = -2332;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time zone used for performing astronomical computations for
|
||||||
|
* Dangi calendar. In Korea various timezones have been used historically
|
||||||
|
* (cf. http://www.math.snu.ac.kr/~kye/others/lunar.html):
|
||||||
|
*
|
||||||
|
* - 1908/04/01: GMT+8
|
||||||
|
* 1908/04/01 - 1911/12/31: GMT+8.5
|
||||||
|
* 1912/01/01 - 1954/03/20: GMT+9
|
||||||
|
* 1954/03/21 - 1961/08/09: GMT+8.5
|
||||||
|
* 1961/08/10 - : GMT+9
|
||||||
|
*
|
||||||
|
* Note that, in 1908-1911, the government did not apply the timezone change
|
||||||
|
* but used GMT+8. In addition, 1954-1961's timezone change does not affect
|
||||||
|
* the lunar date calculation. Therefore, the following simpler rule works:
|
||||||
|
*
|
||||||
|
* -1911: GMT+8
|
||||||
|
* 1912-: GMT+9
|
||||||
|
*
|
||||||
|
* Unfortunately, our astronomer's approximation doesn't agree with the
|
||||||
|
* references (http://www.math.snu.ac.kr/~kye/others/lunar.html and
|
||||||
|
* http://astro.kasi.re.kr/Life/ConvertSolarLunarForm.aspx?MenuID=115)
|
||||||
|
* in 1897/7/30. So the following ad hoc fix is used here:
|
||||||
|
*
|
||||||
|
* -1896: GMT+8
|
||||||
|
* 1897: GMT+7
|
||||||
|
* 1898-1911: GMT+8
|
||||||
|
* 1912- : GMT+9
|
||||||
|
*/
|
||||||
|
private static final TimeZone KOREA_ZONE;
|
||||||
|
|
||||||
|
static {
|
||||||
|
InitialTimeZoneRule initialTimeZone = new InitialTimeZoneRule("GMT+8", 8 * ONE_HOUR, 0);
|
||||||
|
long[] millis1897 = { (1897 - 1970) * 365L * ONE_DAY }; // some days of error is not a problem here
|
||||||
|
long[] millis1898 = { (1898 - 1970) * 365L * ONE_DAY }; // some days of error is not a problem here
|
||||||
|
long[] millis1912 = { (1912 - 1970) * 365L * ONE_DAY }; // this doesn't create an issue for 1911/12/20
|
||||||
|
TimeZoneRule rule1897 = new TimeArrayTimeZoneRule("Korean 1897", 7 * ONE_HOUR, 0, millis1897,
|
||||||
|
DateTimeRule.STANDARD_TIME);
|
||||||
|
TimeZoneRule rule1898to1911 = new TimeArrayTimeZoneRule("Korean 1898-1911", 8 * ONE_HOUR, 0, millis1898,
|
||||||
|
DateTimeRule.STANDARD_TIME);
|
||||||
|
TimeZoneRule ruleFrom1912 = new TimeArrayTimeZoneRule("Korean 1912-", 9 * ONE_HOUR, 0, millis1912,
|
||||||
|
DateTimeRule.STANDARD_TIME);
|
||||||
|
|
||||||
|
RuleBasedTimeZone tz = new RuleBasedTimeZone("KOREA_ZONE", initialTimeZone);
|
||||||
|
tz.addTransitionRule(rule1897);
|
||||||
|
tz.addTransitionRule(rule1898to1911);
|
||||||
|
tz.addTransitionRule(ruleFrom1912);
|
||||||
|
tz.freeze();
|
||||||
|
KOREA_ZONE = tz;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a <code>DangiCalendar</code> with the default time zone and locale.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @deprecated This API is ICU internal only.
|
||||||
|
*/
|
||||||
|
public DangiCalendar() {
|
||||||
|
this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a <code>DangiCalendar</code> with the give date set in the default time zone
|
||||||
|
* with the default locale.
|
||||||
|
* @param date The date to which the new calendar is set.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @deprecated This API is ICU internal only.
|
||||||
|
*/
|
||||||
|
public DangiCalendar(Date date) {
|
||||||
|
this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
|
||||||
|
setTime(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a <code>DangiCalendar</code> based on the current time
|
||||||
|
* with the given time zone with the given locale.
|
||||||
|
* @param zone the given time zone
|
||||||
|
* @param locale the given locale
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @deprecated This API is ICU internal only.
|
||||||
|
*/
|
||||||
|
public DangiCalendar(TimeZone zone, ULocale locale) {
|
||||||
|
super(zone, locale, DANGI_EPOCH_YEAR, KOREA_ZONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @deprecated This API is ICU internal only.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "dangi";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,427 @@
|
|||||||
|
/*
|
||||||
|
*******************************************************************************
|
||||||
|
* Copyright (C) 2012, International Business Machines Corporation and *
|
||||||
|
* others. All Rights Reserved. *
|
||||||
|
*******************************************************************************
|
||||||
|
*/
|
||||||
|
package com.ibm.icu.dev.test.calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.ibm.icu.text.DateFormat;
|
||||||
|
import com.ibm.icu.util.Calendar;
|
||||||
|
import com.ibm.icu.util.DangiCalendar;
|
||||||
|
import com.ibm.icu.util.GregorianCalendar;
|
||||||
|
import com.ibm.icu.util.TimeZone;
|
||||||
|
import com.ibm.icu.util.ULocale;
|
||||||
|
|
||||||
|
public class DangiTest extends CalendarTest {
|
||||||
|
|
||||||
|
public static void main(String args[]) throws Exception {
|
||||||
|
new DangiTest().run(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test basic mapping to and from Gregorian.
|
||||||
|
*/
|
||||||
|
public void TestMapping() {
|
||||||
|
final int[] DATA = {
|
||||||
|
// (Note: months are 1-based)
|
||||||
|
// Gregorian Korean (Dan-gi)
|
||||||
|
1964, 9, 4, 4297, 7,0, 28,
|
||||||
|
1964, 9, 5, 4297, 7,0, 29,
|
||||||
|
1964, 9, 6, 4297, 8,0, 1,
|
||||||
|
1964, 9, 7, 4297, 8,0, 2,
|
||||||
|
1961, 12, 25, 4294, 11,0, 18,
|
||||||
|
1999, 6, 4, 4332, 4,0, 21,
|
||||||
|
|
||||||
|
1990, 5, 23, 4323, 4,0, 29,
|
||||||
|
1990, 5, 24, 4323, 5,0, 1,
|
||||||
|
1990, 6, 22, 4323, 5,0, 30,
|
||||||
|
1990, 6, 23, 4323, 5,1, 1,
|
||||||
|
1990, 7, 20, 4323, 5,1, 28,
|
||||||
|
1990, 7, 21, 4323, 5,1, 29,
|
||||||
|
1990, 7, 22, 4323, 6,0, 1,
|
||||||
|
|
||||||
|
// Some tricky dates (where GMT+8 doesn't agree with GMT+9)
|
||||||
|
//
|
||||||
|
// The list is from http://www.math.snu.ac.kr/~kye/others/lunar.html ('kye ref').
|
||||||
|
// However, for some dates disagree with the above reference so KASI's
|
||||||
|
// calculation was cross-referenced:
|
||||||
|
// http://astro.kasi.re.kr/Life/ConvertSolarLunarForm.aspx?MenuID=115
|
||||||
|
1880, 11, 3, 4213, 10,0, 1, // astronomer's GMT+8 / KASI disagrees with the kye ref
|
||||||
|
1882, 12, 10, 4215, 11,0, 1,
|
||||||
|
1883, 7, 4, 4216, 6,0, 1,
|
||||||
|
1884, 4, 25, 4217, 4,0, 1,
|
||||||
|
1885, 5, 14, 4218, 4,0, 1,
|
||||||
|
1891, 1, 10, 4223, 12,0, 1,
|
||||||
|
1893, 4, 16, 4226, 3,0, 1,
|
||||||
|
1894, 5, 5, 4227, 4,0, 1,
|
||||||
|
1897, 7, 29, 4230, 7,0, 1, // astronomer's GMT+8 disagrees with all other ref (looks like our astronomer's error, see ad hoc fix at ChineseCalendar::getTimezoneOffset)
|
||||||
|
1903, 10, 20, 4236, 9,0, 1,
|
||||||
|
1904, 1, 17, 4236, 12,0, 1,
|
||||||
|
1904, 11, 7, 4237, 10,0, 1,
|
||||||
|
1905, 5, 4, 4238, 4,0, 1,
|
||||||
|
1907, 7, 10, 4240, 6,0, 1,
|
||||||
|
1908, 4, 30, 4241, 4,0, 1,
|
||||||
|
1908, 9, 25, 4241, 9,0, 1,
|
||||||
|
1909, 9, 14, 4242, 8,0, 1,
|
||||||
|
1911, 12, 20, 4244, 11,0, 1,
|
||||||
|
1976, 11, 22, 4309, 10,0, 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi"));
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
|
||||||
|
logln("Gregorian -> Korean Lunar (Dangi)");
|
||||||
|
|
||||||
|
Calendar grego = Calendar.getInstance();
|
||||||
|
grego.clear();
|
||||||
|
for (int i = 0; i < DATA.length;) {
|
||||||
|
grego.set(DATA[i++], DATA[i++] - 1, DATA[i++]);
|
||||||
|
Date date = grego.getTime();
|
||||||
|
cal.setTime(date);
|
||||||
|
int y = cal.get(Calendar.EXTENDED_YEAR);
|
||||||
|
int m = cal.get(Calendar.MONTH) + 1; // 0-based -> 1-based
|
||||||
|
int L = cal.get(Calendar.IS_LEAP_MONTH);
|
||||||
|
int d = cal.get(Calendar.DAY_OF_MONTH);
|
||||||
|
int yE = DATA[i++]; // Expected y, m, isLeapMonth, d
|
||||||
|
int mE = DATA[i++]; // 1-based
|
||||||
|
int LE = DATA[i++];
|
||||||
|
int dE = DATA[i++];
|
||||||
|
buf.setLength(0);
|
||||||
|
buf.append(date + " -> ");
|
||||||
|
buf.append(y + "/" + m + (L == 1 ? "(leap)" : "") + "/" + d);
|
||||||
|
if (y == yE && m == mE && L == LE && d == dE) {
|
||||||
|
logln("OK: " + buf.toString());
|
||||||
|
} else {
|
||||||
|
errln("Fail: " + buf.toString() + ", expected " + yE + "/" + mE + (LE == 1 ? "(leap)" : "") + "/" + dE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logln("Korean Lunar (Dangi) -> Gregorian");
|
||||||
|
for (int i = 0; i < DATA.length;) {
|
||||||
|
grego.set(DATA[i++], DATA[i++] - 1, DATA[i++]);
|
||||||
|
Date dexp = grego.getTime();
|
||||||
|
int cyear = DATA[i++];
|
||||||
|
int cmonth = DATA[i++];
|
||||||
|
int cisleapmonth = DATA[i++];
|
||||||
|
int cdayofmonth = DATA[i++];
|
||||||
|
cal.clear();
|
||||||
|
cal.set(Calendar.EXTENDED_YEAR, cyear);
|
||||||
|
cal.set(Calendar.MONTH, cmonth - 1);
|
||||||
|
cal.set(Calendar.IS_LEAP_MONTH, cisleapmonth);
|
||||||
|
cal.set(Calendar.DAY_OF_MONTH, cdayofmonth);
|
||||||
|
Date date = cal.getTime();
|
||||||
|
buf.setLength(0);
|
||||||
|
buf.append(cyear + "/" + cmonth + (cisleapmonth == 1 ? "(leap)" : "") + "/" + cdayofmonth);
|
||||||
|
buf.append(" -> " + date);
|
||||||
|
if (date.equals(dexp)) {
|
||||||
|
logln("OK: " + buf.toString());
|
||||||
|
} else {
|
||||||
|
errln("Fail: " + buf.toString() + ", expected " + dexp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure no Gregorian dates map to Chinese 1-based day of
|
||||||
|
* month zero. This was a problem with some of the astronomical
|
||||||
|
* new moon determinations.
|
||||||
|
*/
|
||||||
|
public void TestZeroDOM() {
|
||||||
|
Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi"));
|
||||||
|
GregorianCalendar greg = new GregorianCalendar(1989, Calendar.SEPTEMBER, 1);
|
||||||
|
logln("Start: " + greg.getTime());
|
||||||
|
for (int i=0; i<1000; ++i) {
|
||||||
|
cal.setTimeInMillis(greg.getTimeInMillis());
|
||||||
|
if (cal.get(Calendar.DAY_OF_MONTH) == 0) {
|
||||||
|
errln("Fail: " + greg.getTime() + " -> " +
|
||||||
|
cal.get(Calendar.YEAR) + "/" +
|
||||||
|
cal.get(Calendar.MONTH) +
|
||||||
|
(cal.get(Calendar.IS_LEAP_MONTH)==1?"(leap)":"") +
|
||||||
|
"/" + cal.get(Calendar.DAY_OF_MONTH));
|
||||||
|
}
|
||||||
|
greg.add(Calendar.DAY_OF_YEAR, 1);
|
||||||
|
}
|
||||||
|
logln("End: " + greg.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test minimum and maximum functions.
|
||||||
|
*/
|
||||||
|
public void TestLimits() {
|
||||||
|
// The number of days and the start date can be adjusted
|
||||||
|
// arbitrarily to either speed up the test or make it more
|
||||||
|
// thorough, but try to test at least a full year, preferably a
|
||||||
|
// full non-leap and a full leap year.
|
||||||
|
|
||||||
|
// Final parameter is either number of days, if > 0, or test
|
||||||
|
// duration in seconds, if < 0.
|
||||||
|
Calendar tempcal = Calendar.getInstance();
|
||||||
|
tempcal.clear();
|
||||||
|
tempcal.set(1989, Calendar.NOVEMBER, 1);
|
||||||
|
Calendar dangi = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi"));
|
||||||
|
doLimitsTest(dangi, null, tempcal.getTime());
|
||||||
|
doTheoreticalLimitsTest(dangi, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure IS_LEAP_MONTH participates in field resolution.
|
||||||
|
*/
|
||||||
|
public void TestResolution() {
|
||||||
|
Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi"));
|
||||||
|
DateFormat fmt = DateFormat.getDateInstance(cal, DateFormat.DEFAULT);
|
||||||
|
|
||||||
|
// May 22 4334 = y4334 m4 d30 doy119
|
||||||
|
// May 23 4334 = y4334 m4* d1 doy120
|
||||||
|
|
||||||
|
final int THE_YEAR = 4334;
|
||||||
|
final int END = -1;
|
||||||
|
|
||||||
|
int[] DATA = {
|
||||||
|
// Format:
|
||||||
|
// (field, value)+, END, exp.month, exp.isLeapMonth, exp.DOM
|
||||||
|
// Note: exp.month is ONE-BASED
|
||||||
|
|
||||||
|
// If we set DAY_OF_YEAR only, that should be used
|
||||||
|
Calendar.DAY_OF_YEAR, 1,
|
||||||
|
END,
|
||||||
|
1,0,1, // Expect 1-1
|
||||||
|
|
||||||
|
// If we set MONTH only, that should be used
|
||||||
|
Calendar.IS_LEAP_MONTH, 1,
|
||||||
|
Calendar.DAY_OF_MONTH, 1,
|
||||||
|
Calendar.MONTH, 3,
|
||||||
|
END,
|
||||||
|
4,1,1, // Expect 4*-1
|
||||||
|
|
||||||
|
// If we set the DOY last, that should take precedence
|
||||||
|
Calendar.MONTH, 1, // Should ignore
|
||||||
|
Calendar.IS_LEAP_MONTH, 1, // Should ignore
|
||||||
|
Calendar.DAY_OF_MONTH, 1, // Should ignore
|
||||||
|
Calendar.DAY_OF_YEAR, 121,
|
||||||
|
END,
|
||||||
|
4,1,2, // Expect 4*-2
|
||||||
|
|
||||||
|
// If we set IS_LEAP_MONTH last, that should take precedence
|
||||||
|
Calendar.MONTH, 3,
|
||||||
|
Calendar.DAY_OF_MONTH, 1,
|
||||||
|
Calendar.DAY_OF_YEAR, 5, // Should ignore
|
||||||
|
Calendar.IS_LEAP_MONTH, 1,
|
||||||
|
END,
|
||||||
|
4,1,1, // Expect 4*-1
|
||||||
|
};
|
||||||
|
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
for (int i=0; i<DATA.length; ) {
|
||||||
|
cal.clear();
|
||||||
|
cal.set(Calendar.EXTENDED_YEAR, THE_YEAR);
|
||||||
|
buf.setLength(0);
|
||||||
|
buf.append("EXTENDED_YEAR=" + THE_YEAR);
|
||||||
|
while (DATA[i] != END) {
|
||||||
|
cal.set(DATA[i++], DATA[i++]);
|
||||||
|
buf.append(" " + fieldName(DATA[i-2]) + "=" + DATA[i-1]);
|
||||||
|
}
|
||||||
|
++i; // Skip over END mark
|
||||||
|
int expMonth = DATA[i++]-1;
|
||||||
|
int expIsLeapMonth = DATA[i++];
|
||||||
|
int expDOM = DATA[i++];
|
||||||
|
int month = cal.get(Calendar.MONTH);
|
||||||
|
int isLeapMonth = cal.get(Calendar.IS_LEAP_MONTH);
|
||||||
|
int dom = cal.get(Calendar.DAY_OF_MONTH);
|
||||||
|
if (expMonth == month && expIsLeapMonth == isLeapMonth &&
|
||||||
|
dom == expDOM) {
|
||||||
|
logln("OK: " + buf + " => " + fmt.format(cal.getTime()));
|
||||||
|
} else {
|
||||||
|
String s = fmt.format(cal.getTime());
|
||||||
|
cal.clear();
|
||||||
|
cal.set(Calendar.EXTENDED_YEAR, THE_YEAR);
|
||||||
|
cal.set(Calendar.MONTH, expMonth);
|
||||||
|
cal.set(Calendar.IS_LEAP_MONTH, expIsLeapMonth);
|
||||||
|
cal.set(Calendar.DAY_OF_MONTH, expDOM);
|
||||||
|
errln("Fail: " + buf + " => " + s +
|
||||||
|
"=" + (month+1) + "," + isLeapMonth + "," + dom +
|
||||||
|
", expected " + fmt.format(cal.getTime()) +
|
||||||
|
"=" + (expMonth+1) + "," + expIsLeapMonth + "," + expDOM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the behavior of fields that are out of range.
|
||||||
|
*/
|
||||||
|
public void TestOutOfRange() {
|
||||||
|
int[] DATA = new int[] {
|
||||||
|
// Input Output
|
||||||
|
4334, 13, 1, 4335, 1, 1,
|
||||||
|
4334, 18, 1, 4335, 6, 1,
|
||||||
|
4335, 0, 1, 4334, 12, 1,
|
||||||
|
4335, -6, 1, 4334, 6, 1,
|
||||||
|
4334, 1, 32, 4334, 2, 2, // 1-4334 has 30 days
|
||||||
|
4334, 2, -1, 4334, 1, 29,
|
||||||
|
};
|
||||||
|
Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi"));
|
||||||
|
for (int i = 0; i < DATA.length;) {
|
||||||
|
int y1 = DATA[i++];
|
||||||
|
int m1 = DATA[i++] - 1;
|
||||||
|
int d1 = DATA[i++];
|
||||||
|
int y2 = DATA[i++];
|
||||||
|
int m2 = DATA[i++] - 1;
|
||||||
|
int d2 = DATA[i++];
|
||||||
|
cal.clear();
|
||||||
|
cal.set(Calendar.EXTENDED_YEAR, y1);
|
||||||
|
cal.set(MONTH, m1);
|
||||||
|
cal.set(DATE, d1);
|
||||||
|
int y = cal.get(Calendar.EXTENDED_YEAR);
|
||||||
|
int m = cal.get(MONTH);
|
||||||
|
int d = cal.get(DATE);
|
||||||
|
if (y != y2 || m != m2 || d != d2) {
|
||||||
|
errln("Fail: " + y1 + "/" + (m1 + 1) + "/" + d1 + " resolves to " + y + "/" + (m + 1) + "/" + d
|
||||||
|
+ ", expected " + y2 + "/" + (m2 + 1) + "/" + d2);
|
||||||
|
} else if (isVerbose()) {
|
||||||
|
logln("OK: " + y1 + "/" + (m1 + 1) + "/" + d1 + " resolves to " + y + "/" + (m + 1) + "/" + d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the behavior of KoreanLunarCalendar.add(). The only real
|
||||||
|
* nastiness with roll is the MONTH field around leap months.
|
||||||
|
*/
|
||||||
|
public void TestAdd() {
|
||||||
|
int[][] tests = new int[][] {
|
||||||
|
// MONTHS ARE 1-BASED HERE
|
||||||
|
// input add output
|
||||||
|
// year mon day field amount year mon day
|
||||||
|
{ 4338, 3,0, 15, MONTH, 3, 4338, 6,0, 15 }, // normal
|
||||||
|
{ 4335, 12,0, 15, MONTH, 1, 4336, 1,0, 15 }, // across year
|
||||||
|
{ 4336, 1,0, 15, MONTH, -1, 4335, 12,0, 15 }, // across year
|
||||||
|
{ 4334, 3,0, 15, MONTH, 3, 4334, 5,0, 15 }, // 4=leap
|
||||||
|
{ 4334, 3,0, 15, MONTH, 2, 4334, 4,1, 15 }, // 4=leap
|
||||||
|
{ 4334, 4,0, 15, MONTH, 1, 4334, 4,1, 15 }, // 4=leap
|
||||||
|
{ 4334, 4,1, 15, MONTH, 1, 4334, 5,0, 15 }, // 4=leap
|
||||||
|
{ 4334, 3,0, 30, MONTH, 2, 4334, 4,1, 29 }, // dom should pin
|
||||||
|
{ 4334, 3,0, 30, MONTH, 3, 4334, 5,0, 30 }, // no dom pin
|
||||||
|
{ 4334, 3,0, 30, MONTH, 4, 4334, 6,0, 29 }, // dom should pin
|
||||||
|
};
|
||||||
|
|
||||||
|
Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi"));
|
||||||
|
doRollAddDangi(ADD, cal, tests);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the behavior of KoreanLunarCalendar.roll(). The only real
|
||||||
|
* nastiness with roll is the MONTH field around leap months.
|
||||||
|
*/
|
||||||
|
public void TestRoll() {
|
||||||
|
int[][] tests = new int[][] {
|
||||||
|
// MONTHS ARE 1-BASED HERE
|
||||||
|
// input add output
|
||||||
|
// year mon day field amount year mon day
|
||||||
|
{ 4338, 3,0, 15, MONTH, 3, 4338, 6,0, 15 }, // normal
|
||||||
|
{ 4338, 3,0, 15, MONTH, 11, 4338, 2,0, 15 }, // normal
|
||||||
|
{ 4335, 12,0, 15, MONTH, 1, 4335, 1,0, 15 }, // across year
|
||||||
|
{ 4336, 1,0, 15, MONTH, -1, 4336, 12,0, 15 }, // across year
|
||||||
|
{ 4334, 3,0, 15, MONTH, 3, 4334, 5,0, 15 }, // 4=leap
|
||||||
|
{ 4334, 3,0, 15, MONTH, 16, 4334, 5,0, 15 }, // 4=leap
|
||||||
|
{ 4334, 3,0, 15, MONTH, 2, 4334, 4,1, 15 }, // 4=leap
|
||||||
|
{ 4334, 3,0, 15, MONTH, 28, 4334, 4,1, 15 }, // 4=leap
|
||||||
|
{ 4334, 4,0, 15, MONTH, 1, 4334, 4,1, 15 }, // 4=leap
|
||||||
|
{ 4334, 4,0, 15, MONTH, -12, 4334, 4,1, 15 }, // 4=leap
|
||||||
|
{ 4334, 4,1, 15, MONTH, 1, 4334, 5,0, 15 }, // 4=leap
|
||||||
|
{ 4334, 4,1, 15, MONTH, -25, 4334, 5,0, 15 }, // 4=leap
|
||||||
|
{ 4334, 3,0, 30, MONTH, 2, 4334, 4,1, 29 }, // dom should pin
|
||||||
|
{ 4334, 3,0, 30, MONTH, 15, 4334, 4,1, 29 }, // dom should pin
|
||||||
|
{ 4334, 3,0, 30, MONTH, 16, 4334, 5,0, 30 }, // no dom pin
|
||||||
|
{ 4334, 3,0, 30, MONTH, -9, 4334, 6,0, 29 }, // dom should pin
|
||||||
|
};
|
||||||
|
|
||||||
|
Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi"));
|
||||||
|
doRollAddDangi(ROLL, cal, tests);
|
||||||
|
}
|
||||||
|
|
||||||
|
void doRollAddDangi(boolean roll, Calendar cal, int[][] tests) {
|
||||||
|
String name = roll ? "rolling" : "adding";
|
||||||
|
|
||||||
|
for (int i = 0; i < tests.length; i++) {
|
||||||
|
int[] test = tests[i];
|
||||||
|
|
||||||
|
cal.clear();
|
||||||
|
cal.set(Calendar.EXTENDED_YEAR, test[0]);
|
||||||
|
cal.set(Calendar.MONTH, test[1] - 1);
|
||||||
|
cal.set(Calendar.IS_LEAP_MONTH, test[2]);
|
||||||
|
cal.set(Calendar.DAY_OF_MONTH, test[3]);
|
||||||
|
if (roll) {
|
||||||
|
cal.roll(test[4], test[5]);
|
||||||
|
} else {
|
||||||
|
cal.add(test[4], test[5]);
|
||||||
|
}
|
||||||
|
if (cal.get(Calendar.EXTENDED_YEAR) != test[6] || cal.get(MONTH) != (test[7] - 1)
|
||||||
|
|| cal.get(Calendar.IS_LEAP_MONTH) != test[8] || cal.get(DATE) != test[9]) {
|
||||||
|
errln("Fail: " + name + " " + ymdToString(test[0], test[1] - 1, test[2], test[3]) + " "
|
||||||
|
+ fieldName(test[4]) + " by " + test[5] + ": expected "
|
||||||
|
+ ymdToString(test[6], test[7] - 1, test[8], test[9]) + ", got " + ymdToString(cal));
|
||||||
|
} else if (isVerbose()) {
|
||||||
|
logln("OK: " + name + " " + ymdToString(test[0], test[1] - 1, test[2], test[3]) + " "
|
||||||
|
+ fieldName(test[4]) + " by " + test[5] + ": got " + ymdToString(cal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert year,month,day values to the form "year/month/day".
|
||||||
|
* On input the month value is zero-based, but in the result string it is one-based.
|
||||||
|
*/
|
||||||
|
static public String ymdToString(int year, int month, int isLeapMonth, int day) {
|
||||||
|
return "" + year + "/" + (month + 1) + ((isLeapMonth != 0) ? "(leap)" : "") + "/" + day;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TestCoverage() {
|
||||||
|
// DangiCalendar()
|
||||||
|
// DangiCalendar(Date)
|
||||||
|
// DangiCalendar(TimeZone, ULocale)
|
||||||
|
Date d = new Date();
|
||||||
|
|
||||||
|
DangiCalendar cal1 = new DangiCalendar();
|
||||||
|
cal1.setTime(d);
|
||||||
|
|
||||||
|
DangiCalendar cal2 = new DangiCalendar(d);
|
||||||
|
|
||||||
|
DangiCalendar cal3 = new DangiCalendar(TimeZone.getDefault(), ULocale.getDefault());
|
||||||
|
cal3.setTime(d);
|
||||||
|
|
||||||
|
assertEquals("DangiCalendar() and DangiCalendar(Date)", cal1, cal2);
|
||||||
|
assertEquals("DangiCalendar() and DangiCalendar(TimeZone,ULocale)", cal1, cal3);
|
||||||
|
|
||||||
|
// String getType()
|
||||||
|
String type = cal1.getType();
|
||||||
|
assertEquals("getType()", "dangi", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TestInitWithCurrentTime() {
|
||||||
|
// If the chinese calendar current millis isn't called, the default year is wrong.
|
||||||
|
// this test is assuming the 'year' is the current cycle
|
||||||
|
// so when we cross a cycle boundary, the target will need to change
|
||||||
|
// that shouldn't be for awhile yet...
|
||||||
|
|
||||||
|
Calendar cc = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi"));
|
||||||
|
cc.set(Calendar.EXTENDED_YEAR, 4338);
|
||||||
|
cc.set(Calendar.MONTH, 0);
|
||||||
|
// need to set leap month flag off, otherwise, the test case always fails when
|
||||||
|
// current time is in a leap month
|
||||||
|
cc.set(Calendar.IS_LEAP_MONTH, 0);
|
||||||
|
cc.set(Calendar.DATE, 19);
|
||||||
|
cc.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
cc.set(Calendar.MINUTE, 0);
|
||||||
|
cc.set(Calendar.SECOND, 0);
|
||||||
|
cc.set(Calendar.MILLISECOND, 0);
|
||||||
|
|
||||||
|
cc.add(Calendar.DATE, 1);
|
||||||
|
|
||||||
|
Calendar cal = new GregorianCalendar(2005, Calendar.FEBRUARY, 28);
|
||||||
|
Date target = cal.getTime();
|
||||||
|
Date result = cc.getTime();
|
||||||
|
|
||||||
|
assertEquals("chinese and gregorian date should match", target, result);
|
||||||
|
}
|
||||||
|
}
|
@ -3391,6 +3391,7 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
|||||||
new ULocale("en@calendar=islamic"),
|
new ULocale("en@calendar=islamic"),
|
||||||
new ULocale("ja_JP@calendar=japanese"),
|
new ULocale("ja_JP@calendar=japanese"),
|
||||||
new ULocale("zh_Hans_CN@calendar=bogus"),
|
new ULocale("zh_Hans_CN@calendar=bogus"),
|
||||||
|
new ULocale("ko_KR@calendar=dangi"),
|
||||||
};
|
};
|
||||||
|
|
||||||
SimpleDateFormat[] formatters = new SimpleDateFormat[5];
|
SimpleDateFormat[] formatters = new SimpleDateFormat[5];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
* Copyright (C) 1996-2007, International Business Machines Corporation and *
|
* Copyright (C) 1996-2012, International Business Machines Corporation and *
|
||||||
* others. All Rights Reserved. *
|
* others. All Rights Reserved. *
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
*
|
*
|
||||||
@ -14,6 +14,7 @@ import com.ibm.icu.util.BuddhistCalendar;
|
|||||||
import com.ibm.icu.util.Calendar;
|
import com.ibm.icu.util.Calendar;
|
||||||
import com.ibm.icu.util.ChineseCalendar;
|
import com.ibm.icu.util.ChineseCalendar;
|
||||||
import com.ibm.icu.util.CopticCalendar;
|
import com.ibm.icu.util.CopticCalendar;
|
||||||
|
import com.ibm.icu.util.DangiCalendar;
|
||||||
import com.ibm.icu.util.EthiopicCalendar;
|
import com.ibm.icu.util.EthiopicCalendar;
|
||||||
import com.ibm.icu.util.GregorianCalendar;
|
import com.ibm.icu.util.GregorianCalendar;
|
||||||
import com.ibm.icu.util.HebrewCalendar;
|
import com.ibm.icu.util.HebrewCalendar;
|
||||||
@ -22,6 +23,7 @@ import com.ibm.icu.util.IslamicCalendar;
|
|||||||
import com.ibm.icu.util.JapaneseCalendar;
|
import com.ibm.icu.util.JapaneseCalendar;
|
||||||
import com.ibm.icu.util.TaiwanCalendar;
|
import com.ibm.icu.util.TaiwanCalendar;
|
||||||
import com.ibm.icu.util.TimeZone;
|
import com.ibm.icu.util.TimeZone;
|
||||||
|
import com.ibm.icu.util.ULocale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author emader
|
* @author emader
|
||||||
@ -110,6 +112,22 @@ public class CalendarTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class DangiCalendarHandler extends CalendarHandler
|
||||||
|
{
|
||||||
|
public Object[] getTestObjects()
|
||||||
|
{
|
||||||
|
Locale locales[] = SerializableTest.getLocales();
|
||||||
|
TimeZone kst = TimeZone.getTimeZone("Asia/Seoul");
|
||||||
|
DangiCalendar calendars[] = new DangiCalendar[locales.length];
|
||||||
|
|
||||||
|
for (int i = 0; i < locales.length; i += 1) {
|
||||||
|
calendars[i] = new DangiCalendar(kst, ULocale.forLocale(locales[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return calendars;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static class EthiopicCalendarHandler extends CalendarHandler
|
static class EthiopicCalendarHandler extends CalendarHandler
|
||||||
{
|
{
|
||||||
public Object[] getTestObjects()
|
public Object[] getTestObjects()
|
||||||
|
@ -682,6 +682,7 @@ public class SerializableTest extends TestFmwk.TestGroup
|
|||||||
map.put("com.ibm.icu.util.BuddhistCalendar", new CalendarTests.BuddhistCalendarHandler());
|
map.put("com.ibm.icu.util.BuddhistCalendar", new CalendarTests.BuddhistCalendarHandler());
|
||||||
map.put("com.ibm.icu.util.ChineseCalendar", new CalendarTests.ChineseCalendarHandler());
|
map.put("com.ibm.icu.util.ChineseCalendar", new CalendarTests.ChineseCalendarHandler());
|
||||||
map.put("com.ibm.icu.util.CopticCalendar", new CalendarTests.CopticCalendarHandler());
|
map.put("com.ibm.icu.util.CopticCalendar", new CalendarTests.CopticCalendarHandler());
|
||||||
|
map.put("com.ibm.icu.util.DangiCalendar", new CalendarTests.DangiCalendarHandler());
|
||||||
map.put("com.ibm.icu.util.EthiopicCalendar", new CalendarTests.EthiopicCalendarHandler());
|
map.put("com.ibm.icu.util.EthiopicCalendar", new CalendarTests.EthiopicCalendarHandler());
|
||||||
map.put("com.ibm.icu.util.GregorianCalendar", new CalendarTests.GregorianCalendarHandler());
|
map.put("com.ibm.icu.util.GregorianCalendar", new CalendarTests.GregorianCalendarHandler());
|
||||||
map.put("com.ibm.icu.util.HebrewCalendar", new CalendarTests.HebrewCalendarHandler());
|
map.put("com.ibm.icu.util.HebrewCalendar", new CalendarTests.HebrewCalendarHandler());
|
||||||
|
Loading…
Reference in New Issue
Block a user