ICU-20537 jaEra: DateIntvFmt load fmts with G, SimpDateFmt.applyPattern update Gannen use, fix tests

This commit is contained in:
Peter Edberg 2019-04-07 16:14:07 -07:00 committed by pedberg-icu
parent 2b828fe198
commit 2f06d1de55
12 changed files with 143 additions and 53 deletions

View file

@ -1130,7 +1130,9 @@ DateIntervalFormat::setSeparateDateTimePtn(
}
setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo,
&extendedSkeleton, &extendedBestSkeleton);
} else {
setIntervalPattern(UCAL_ERA, skeleton, bestSkeleton, differenceInfo,
&extendedSkeleton, &extendedBestSkeleton);
} else {
setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo);
setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo);
setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo);

View file

@ -326,7 +326,9 @@ struct DateIntervalInfo::DateIntervalSink : public ResourceSink {
char c0;
if ((c0 = patternLetter[0]) != 0 && patternLetter[1] == 0) {
// Check that the pattern letter is accepted
if (c0 == 'y') {
if (c0 == 'G') {
return UCAL_ERA;
} else if (c0 == 'y') {
return UCAL_YEAR;
} else if (c0 == 'M') {
return UCAL_MONTH;

View file

@ -863,7 +863,7 @@ SimpleDateFormat::initialize(const Locale& locale,
// Simple-minded hack to force Gannen year numbering for ja@calendar=japanese
// if format is non-numeric (includes 年) and fDateOverride is not already specified.
// This does not update if applyPattern subsequently changes the pattern type.
// Now this does get updated if applyPattern subsequently changes the pattern type.
if (fDateOverride.isBogus() && fHasHanYearChar &&
fCalendar != nullptr && uprv_strcmp(fCalendar->getType(),"japanese") == 0 &&
uprv_strcmp(fLocale.getLanguage(),"ja") == 0) {
@ -3885,6 +3885,42 @@ SimpleDateFormat::applyPattern(const UnicodeString& pattern)
{
fPattern = pattern;
parsePattern();
// Hack to update use of Gannen year numbering for ja@calendar=japanese -
// use only if format is non-numeric (includes 年) and no other fDateOverride.
if (fCalendar != nullptr && uprv_strcmp(fCalendar->getType(),"japanese") == 0 &&
uprv_strcmp(fLocale.getLanguage(),"ja") == 0) {
if (fDateOverride==UnicodeString(u"y=jpanyear") && !fHasHanYearChar) {
// Gannen numbering is set but new pattern should not use it, unset;
// use procedure from adoptNumberFormat to clear overrides
if (fSharedNumberFormatters) {
freeSharedNumberFormatters(fSharedNumberFormatters);
fSharedNumberFormatters = NULL;
}
fDateOverride.setToBogus(); // record status
} else if (fDateOverride.isBogus() && fHasHanYearChar) {
// No current override (=> no Gannen numbering) but new pattern needs it;
// use procedures from initNUmberFormatters / adoptNumberFormat
umtx_lock(LOCK());
if (fSharedNumberFormatters == NULL) {
fSharedNumberFormatters = allocSharedNumberFormatters();
}
umtx_unlock(LOCK());
if (fSharedNumberFormatters != NULL) {
Locale ovrLoc(fLocale.getLanguage(),fLocale.getCountry(),fLocale.getVariant(),"numbers=jpanyear");
UErrorCode status = U_ZERO_ERROR;
const SharedNumberFormat *snf = NULL;
SharedObject::copyPtr(createSharedNumberFormat(ovrLoc, status), snf);
if (U_SUCCESS(status)) {
// Now that we have an appropriate number formatter, fill in the
// appropriate slot in the number formatters table.
UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(u'y');
SharedObject::copyPtr(snf, fSharedNumberFormatters[patternCharIndex]);
fDateOverride.setTo(u"y=jpanyear", -1); // record status
}
}
}
}
}
//----------------------------------------------------------------------

View file

@ -488,19 +488,19 @@ typedef enum UDateFormatStyle {
* root/English abbreviated version (ASCII-range characters).
* @internal
*/
#define JP_ERA_2019_ROOT "Qqqq"
#define JP_ERA_2019_ROOT "Reiwa"
/**
* Constant for Unicode string name of new (in 2019) Japanese calendar era,
* Japanese abbreviated version (Han, or fullwidth Latin for testing).
* @internal
*/
#define JP_ERA_2019_JA "\\uFF31\\uFF31"
#define JP_ERA_2019_JA "\\u4EE4\\u548C"
/**
* Constant for Unicode string name of new (in 2019) Japanese calendar era,
* root and Japanese narrow version (ASCII-range characters).
* @internal
*/
#define JP_ERA_2019_NARROW "Q"
#define JP_ERA_2019_NARROW "R"
#endif // U_HIDE_INTERNAL_API
/**

View file

@ -1064,9 +1064,9 @@ void DateIntervalFormatTest::testFormat() {
"ja-u-ca-japanese", "S 64 01 05 09:00:00", "H 1 01 15 09:00:00", "GyMMMd", "\\u662D\\u548C64\\u5E741\\u67085\\u65E5\\uFF5E\\u5E73\\u6210\\u5143\\u5E741\\u670815\\u65E5",
"ja-u-ca-japanese", "S 64 01 05 09:00:00", "H 1 01 15 09:00:00", "GGGGGyMd", "S64/1/5\\uFF5EH1/1/15", // The GGGGG/G forces inheritance from a different pattern, no padding
"ja-u-ca-japanese", "S 64 01 05 09:00:00", "H 1 01 15 09:00:00", "GGGGGyMd", "S64/01/05\\uFF5EH1/01/15",
"ja-u-ca-japanese", "H 31 04 15 09:00:00", JP_ERA_2019_NARROW " 1 05 15 09:00:00", "GGGGGyMd", "H31/4/15\\uFF5E" JP_ERA_2019_NARROW "1/5/15",
"ja-u-ca-japanese", "H 31 04 15 09:00:00", JP_ERA_2019_NARROW " 1 05 15 09:00:00", "GGGGGyMd", "H31/04/15\\uFF5E" JP_ERA_2019_NARROW "1/05/15",
};
expect(DATA, UPRV_LENGTHOF(DATA));
@ -1077,7 +1077,6 @@ void DateIntervalFormatTest::expect(const char** data, int32_t data_length) {
int32_t i = 0;
UErrorCode ec = U_ZERO_ERROR;
UnicodeString str, str2;
UBool testNewJpanEra = JapaneseCalendar::enableTentativeEra();
const char* pattern = data[i++];
while (i<data_length) {
@ -1093,11 +1092,6 @@ void DateIntervalFormatTest::expect(const char** data, int32_t data_length) {
}
const char* calType = defCal->getType();
if (!testNewJpanEra && uprv_strcmp(calType,"japanese")==0 && uprv_strncmp(datestr_2,JP_ERA_2019_NARROW,1)==0) {
i += 2; // skip the rest of the strings for this item
continue; // skip tests involving future japanese eras if not enabled by environment variable
}
Locale refLoc("root");
if (calType) {
refLoc.setKeywordValue("calendar", calType, ec);

View file

@ -703,7 +703,7 @@ void IntlCalendarTest::TestJapanese3860()
int32_t gotYear = cal2->get(UCAL_YEAR, s2);
int32_t gotEra = cal2->get(UCAL_ERA, s2);
int32_t expectYear = 1;
int32_t expectEra = 235; //JapaneseCalendar::kCurrentEra;
int32_t expectEra = JapaneseCalendar::getCurrentEra();
if((gotYear!=1) || (gotEra != expectEra)) {
errln(UnicodeString("parse "+samplestr+" of 'y' as Japanese Calendar, expected year ") + expectYear +
UnicodeString(" and era ") + expectEra +", but got year " + gotYear + " and era " + gotEra + " (Gregorian:" + str +")");
@ -725,18 +725,38 @@ void IntlCalendarTest::TestForceGannenNumbering()
const char* locID = "ja_JP@calendar=japanese";
Locale loc(locID);
UDate refDate = 600336000000.0; // 1989 Jan 9 Monday = Heisei 1
UnicodeString testSkeleton("yMMMd");
UnicodeString patText(u"Gy年M月d日",-1);
UnicodeString patNumr(u"GGGGGy/MM/dd",-1);
UnicodeString skelText(u"yMMMM",-1);
// Test Gannen year forcing
status = U_ZERO_ERROR;
LocalPointer<DateFormat> testFmt1(DateFormat::createInstanceForSkeleton(testSkeleton, loc, status));
LocalPointer<SimpleDateFormat> testFmt1(new SimpleDateFormat(patText, loc, status));
LocalPointer<SimpleDateFormat> testFmt2(new SimpleDateFormat(patNumr, loc, status));
if (U_FAILURE(status)) {
dataerrln("Fail in DateFormat::createInstanceForSkeleton locale %s: %s", locID, u_errorName(status));
dataerrln("Fail in new SimpleDateFormat locale %s: %s", locID, u_errorName(status));
} else {
UnicodeString testString1;
UnicodeString testString1, testString2;
testString1 = testFmt1->format(refDate, testString1);
if (testString1.length() < 3 || testString1.charAt(2) != 0x5143) {
errln(UnicodeString("Formatting year 1 as Gannen, got " + testString1 + " but expected 3rd char to be 0x5143"));
errln(UnicodeString("Formatting year 1 in created text style, got " + testString1 + " but expected 3rd char to be 0x5143"));
}
testString2 = testFmt2->format(refDate, testString2);
if (testString2.length() < 2 || testString2.charAt(1) != 0x0031) {
errln(UnicodeString("Formatting year 1 in created numeric style, got " + testString2 + " but expected 2nd char to be 1"));
}
// Now switch the patterns and verify that Gannen use follows the pattern
testFmt1->applyPattern(patNumr);
testString1.remove();
testString1 = testFmt1->format(refDate, testString1);
if (testString1.length() < 2 || testString1.charAt(1) != 0x0031) {
errln(UnicodeString("Formatting year 1 in applied numeric style, got " + testString1 + " but expected 2nd char to be 1"));
}
testFmt2->applyPattern(patText);
testString2.remove();
testString2 = testFmt2->format(refDate, testString2);
if (testString2.length() < 3 || testString2.charAt(2) != 0x5143) {
errln(UnicodeString("Formatting year 1 in applied text style, got " + testString2 + " but expected 3rd char to be 0x5143"));
}
}
@ -746,18 +766,19 @@ void IntlCalendarTest::TestForceGannenNumbering()
if (U_FAILURE(status)) {
dataerrln("Fail in DateTimePatternGenerator::createInstance locale %s: %s", locID, u_errorName(status));
} else {
UnicodeString pattern = dtpgen->getBestPattern(testSkeleton, status);
UnicodeString pattern = dtpgen->getBestPattern(skelText, status);
if (U_FAILURE(status)) {
dataerrln("Fail in DateTimePatternGenerator::getBestPattern locale %s: %s", locID, u_errorName(status));
} else {
LocalPointer<SimpleDateFormat> testFmt2(new SimpleDateFormat(pattern, UnicodeString(""), loc, status));
// Use override string of ""
LocalPointer<SimpleDateFormat> testFmt3(new SimpleDateFormat(pattern, UnicodeString(""), loc, status));
if (U_FAILURE(status)) {
dataerrln("Fail in new SimpleDateFormat locale %s: %s", locID, u_errorName(status));
} else {
UnicodeString testString2;
testString2 = testFmt2->format(refDate, testString2);
if (testString2.length() < 3 || testString2.charAt(2) != 0x0031) {
errln(UnicodeString("Formatting year 1 with Gannen disabled, got " + testString2 + " but expected 3rd char to be 1"));
UnicodeString testString3;
testString3 = testFmt3->format(refDate, testString3);
if (testString3.length() < 3 || testString3.charAt(2) != 0x0031) {
errln(UnicodeString("Formatting year 1 with Gannen disabled, got " + testString3 + " but expected 3rd char to be 1"));
}
}
}

View file

@ -1318,7 +1318,7 @@ public abstract class DateFormat extends UFormat {
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final String JP_ERA_2019_ROOT = "Qqqq";
public static final String JP_ERA_2019_ROOT = "Reiwa";
/**
* Constant for Unicode string name of new (in 2019) Japanese calendar era,
@ -1327,7 +1327,7 @@ public abstract class DateFormat extends UFormat {
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final String JP_ERA_2019_JA = "\uFF31\uFF31";
public static final String JP_ERA_2019_JA = "\u4EE4\u548C";
/**
* Constant for Unicode string name of new (in 2019) Japanese calendar era,
@ -1336,7 +1336,7 @@ public abstract class DateFormat extends UFormat {
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final String JP_ERA_2019_NARROW = "Q";
public static final String JP_ERA_2019_NARROW = "R";
/**
* Gets the time formatter with the default formatting style

View file

@ -1804,6 +1804,7 @@ public class DateIntervalFormat extends UFormat {
skeleton = skeletons.bestMatchSkeleton;
}
genIntervalPattern(Calendar.YEAR, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
genIntervalPattern(Calendar.ERA, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
} else {
genIntervalPattern(Calendar.MINUTE, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
genIntervalPattern(Calendar.HOUR, skeleton, bestSkeleton, differenceInfo, intervalPatterns);

View file

@ -426,7 +426,7 @@ public class DateIntervalInfo implements Cloneable, Freezable<DateIntervalInfo>,
* Calendar.MINUTE
* Calendar.SECOND
*/
private static final String ACCEPTED_PATTERN_LETTERS = "yMdahHms";
private static final String ACCEPTED_PATTERN_LETTERS = "GyMdahHms";
// Output data
DateIntervalInfo dateIntervalInfo;

View file

@ -1145,7 +1145,7 @@ public class SimpleDateFormat extends DateFormat {
// Simple-minded hack to force Gannen year numbering for ja@calendar=japanese
// if format is non-numeric (includes ) and overrides are not already specified.
// This does not update if applyPattern subsequently changes the pattern type.
// Now this does get updated if applyPattern subsequently changes the pattern type.
if (override == null && hasHanYearChar &&
calendar != null && calendar.getType().equals("japanese") &&
locale != null && locale.getLanguage().equals("ja")) {
@ -3916,6 +3916,31 @@ public class SimpleDateFormat extends DateFormat {
setLocale(null, null);
// reset parsed pattern items
patternItems = null;
// Hack to update use of Gannen year numbering for ja@calendar=japanese -
// use only if format is non-numeric (includes ) and no other fDateOverride.
if (calendar != null && calendar.getType().equals("japanese") &&
locale != null && locale.getLanguage().equals("ja")) {
if (override != null && override.equals("y=jpanyear") && !hasHanYearChar) {
// Gannen numbering is set but new pattern should not use it, unset;
// use procedure from setNumberFormat(NUmberFormat) to clear overrides
numberFormatters = null;
overrideMap = null;
override = null; // record status
} else if (override == null && hasHanYearChar) {
// No current override (=> no Gannen numbering) but new pattern needs it;
// use procedures from initNumberFormatters / setNumberFormat(String,NumberFormat)
numberFormatters = new HashMap<>();
overrideMap = new HashMap<>();
overrideMap.put('y',"jpanyear");
ULocale ovrLoc = new ULocale(locale.getBaseName()+"@numbers=jpanyear");
NumberFormat nf = NumberFormat.createInstance(ovrLoc,NumberFormat.NUMBERSTYLE);
nf.setGroupingUsed(false);
useLocalZeroPaddingNumberFormat = false;
numberFormatters.put("jpanyear",nf);
override = "y=jpanyear"; // record status
}
}
}
/**

View file

@ -235,30 +235,45 @@ public class JapaneseTest extends CalendarTestFmwk {
@Test
public void TestForceGannenNumbering() {
final String jCalShortPattern = "y/M/d"; // Note: just 'y' doesn't work here.
final String jCalGannenDate = "1/5/9"; // A date in the above format after the accession date for Heisei era (Heisei year 1 Jan 8)
// or the new era in Gregorian 2019 (new era year 1 May 1). If before the accession date,
final String jCalGannenDate = "1/5/9"; // A date in the above format after the accession date for Heisei [1989-] era (Heisei year 1 Jan 8)
// or Reiwa [2019-] era (Reiwa year 1 May 1). If before the accession date,
// the year will be in the previous era.
ULocale loc = new ULocale("ja_JP@calendar=japanese");
DateFormat refFmt = DateFormat.getDateInstance(DateFormat.SHORT, loc);
((SimpleDateFormat)refFmt).applyPattern(jCalShortPattern);
ParsePosition pos = new ParsePosition(0);
Date refDate = refFmt.parse(jCalGannenDate, pos);
final String testSkeleton = "yMMMd";
Date refDate = new Date(600336000000L); // 1989 Jan 9 Monday = Heisei 1
final String patText = "Gy年M月d日";
final String patNumr = "GGGGGy/MM/dd";
final String skelText = "yMMMM";
// Test Gannen year forcing
DateFormat testFmt1 = DateFormat.getInstanceForSkeleton(testSkeleton, loc);
SimpleDateFormat testFmt1 = new SimpleDateFormat(patText, loc);
SimpleDateFormat testFmt2 = new SimpleDateFormat(patNumr, loc);
String testString1 = testFmt1.format(refDate);
if (testString1.length() < 3 || testString1.charAt(2) != '\u5143') { //
errln("Formatting year 1 as Gannen, got " + testString1 + " but expected 3rd char to be \u5143");
errln("Formatting year 1 in created text style, got " + testString1 + " but expected 3rd char to be \u5143");
}
String testString2 = testFmt2.format(refDate);
if (testString2.length() < 2 || testString2.charAt(1) != '1') {
errln("Formatting year 1 in created numeric style, got " + testString2 + " but expected 2nd char to be 1");
}
// Now switch the patterns and verify that Gannen use follows the pattern
testFmt1.applyPattern(patNumr);
testString1 = testFmt1.format(refDate);
if (testString1.length() < 2 || testString1.charAt(1) != '1') { //
errln("Formatting year 1 in applied numeric style, got " + testString1 + " but expected 2nd char to be 1");
}
testFmt2.applyPattern(patText);
testString2 = testFmt2.format(refDate);
if (testString2.length() < 3 || testString2.charAt(2) != '\u5143') { //
errln("Formatting year 1 in applied text style, got " + testString2 + " but expected 3rd char to be \u5143");
}
// Test disabling of Gannen year forcing
DateTimePatternGenerator dtpgen = DateTimePatternGenerator.getInstance(loc);
String pattern = dtpgen.getBestPattern(testSkeleton);
SimpleDateFormat testFmt2 = new SimpleDateFormat(pattern, "", loc); // empty override string to disable Gannen year numbering
String testString2 = testFmt2.format(refDate);
if (testString2.length() < 3 || testString2.charAt(2) != '1') {
errln("Formatting year 1 with Gannen disabled, got " + testString2 + " but expected 3rd char to be 1");
String pattern = dtpgen.getBestPattern(skelText);
SimpleDateFormat testFmt3 = new SimpleDateFormat(pattern, "", loc); // empty override string to disable Gannen year numbering
String testString3 = testFmt3.format(refDate);
if (testString3.length() < 3 || testString3.charAt(2) != '1') {
errln("Formatting year 1 with Gannen disabled, got " + testString3 + " but expected 3rd char to be 1");
}
}

View file

@ -712,9 +712,9 @@ public class DateIntervalFormatTest extends TestFmwk {
"ja-u-ca-japanese", "S 64 01 05 09:00:00", "H 1 01 15 09:00:00", "GyMMMd", "\u662D\u548C64\u5E741\u67085\u65E5\uFF5E\u5E73\u6210\u5143\u5E741\u670815\u65E5",
"ja-u-ca-japanese", "S 64 01 05 09:00:00", "H 1 01 15 09:00:00", "GGGGGyMd", "S64\u5E741\u67085\u65E5\uFF5EH\u5143\u5E741\u670815\u65E5", // The GGGGG/G forces inheritance from a different pattern, non-numeric-only
"ja-u-ca-japanese", "S 64 01 05 09:00:00", "H 1 01 15 09:00:00", "GGGGGyMd", "S64/01/05\uFF5EH1/01/15",
"ja-u-ca-japanese", "H 31 04 15 09:00:00", DateFormat.JP_ERA_2019_NARROW+" 1 05 15 09:00:00", "GGGGGyMd", "H31\u5E744\u670815\u65E5\uFF5E"+DateFormat.JP_ERA_2019_NARROW+"\u5143\u5E745\u670815\u65E5",
"ja-u-ca-japanese", "H 31 04 15 09:00:00", DateFormat.JP_ERA_2019_NARROW+" 1 05 15 09:00:00", "GGGGGyMd", "H31/04/15\uFF5E"+DateFormat.JP_ERA_2019_NARROW+"1/05/15",
};
expect(DATA, DATA.length);
@ -723,7 +723,6 @@ public class DateIntervalFormatTest extends TestFmwk {
private void expect(String[] data, int data_length) {
int i = 0;
boolean testNewJpanEra = JapaneseCalendar.enableTentativeEra();
String pattern = data[i++];
while (i<data_length) {
@ -735,11 +734,6 @@ public class DateIntervalFormatTest extends TestFmwk {
Calendar defCal = Calendar.getInstance(loc);
String calType = defCal.getType();
if (!testNewJpanEra && calType.equals("japanese") && datestr_2.startsWith(DateFormat.JP_ERA_2019_NARROW)) {
i += 2; // skip the rest of the strings for this item
continue; // skip tests involving future japanese eras if not enabled by environment variable
}
ULocale refLoc = ULocale.ROOT.setKeywordValue("calendar", calType);
SimpleDateFormat ref = new SimpleDateFormat(pattern, refLoc);
// 'f'