mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 06:25:30 +00:00
ICU-20441 force Gannen use for ja@calendar=japanese non-numeric formats if no override (#474)
This commit is contained in:
parent
2159e1fb81
commit
9a7bf5cea8
9 changed files with 173 additions and 21 deletions
|
@ -856,6 +856,17 @@ SimpleDateFormat::initialize(const Locale& locale,
|
|||
{
|
||||
if (U_FAILURE(status)) return;
|
||||
|
||||
parsePattern(); // Need this before initNumberFormatters(), to set fHasHanYearChar
|
||||
|
||||
// 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.
|
||||
if (fDateOverride.isBogus() && fHasHanYearChar &&
|
||||
fCalendar != nullptr && uprv_strcmp(fCalendar->getType(),"japanese") == 0 &&
|
||||
uprv_strcmp(fLocale.getLanguage(),"ja") == 0) {
|
||||
fDateOverride.setTo(u"y=jpanyear", -1);
|
||||
}
|
||||
|
||||
// We don't need to check that the row count is >= 1, since all 2d arrays have at
|
||||
// least one row
|
||||
fNumberFormat = NumberFormat::createInstance(locale, status);
|
||||
|
@ -872,8 +883,6 @@ SimpleDateFormat::initialize(const Locale& locale,
|
|||
{
|
||||
status = U_MISSING_RESOURCE_ERROR;
|
||||
}
|
||||
|
||||
parsePattern();
|
||||
}
|
||||
|
||||
/* Initialize the fields we use to disambiguate ambiguous years. Separate
|
||||
|
@ -4216,6 +4225,9 @@ void SimpleDateFormat::parsePattern() {
|
|||
if (ch == QUOTE) {
|
||||
inQuote = !inQuote;
|
||||
}
|
||||
if (ch == 0x5E74) { // don't care whether this is inside quotes
|
||||
fHasHanYearChar = TRUE;
|
||||
}
|
||||
if (!inQuote) {
|
||||
if (ch == 0x6D) { // 0x6D == 'm'
|
||||
fHasMinute = TRUE;
|
||||
|
|
|
@ -1599,6 +1599,7 @@ private:
|
|||
|
||||
UBool fHasMinute;
|
||||
UBool fHasSecond;
|
||||
UBool fHasHanYearChar; // pattern contains the Han year character \u5E74
|
||||
|
||||
/**
|
||||
* Sets fHasMinutes and fHasSeconds.
|
||||
|
|
|
@ -42,6 +42,7 @@ static void TestContext(void);
|
|||
static void TestCalendarDateParse(void);
|
||||
static void TestParseErrorReturnValue(void);
|
||||
static void TestFormatForFields(void);
|
||||
static void TestForceGannenNumbering(void);
|
||||
|
||||
void addDateForTest(TestNode** root);
|
||||
|
||||
|
@ -61,6 +62,7 @@ void addDateForTest(TestNode** root)
|
|||
TESTCASE(TestOverrideNumberFormat);
|
||||
TESTCASE(TestParseErrorReturnValue);
|
||||
TESTCASE(TestFormatForFields);
|
||||
TESTCASE(TestForceGannenNumbering);
|
||||
}
|
||||
/* Testing the DateFormat API */
|
||||
static void TestDateFormat()
|
||||
|
@ -1867,4 +1869,40 @@ static void TestFormatForFields(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static void TestForceGannenNumbering(void) {
|
||||
UErrorCode status;
|
||||
const char* locID = "ja_JP@calendar=japanese";
|
||||
UDate refDate = 600336000000.0; // 1989 Jan 9 Monday = Heisei 1
|
||||
const UChar* testSkeleton = u"yMMMd";
|
||||
|
||||
// Test Gannen year forcing
|
||||
status = U_ZERO_ERROR;
|
||||
UDateTimePatternGenerator* dtpgen = udatpg_open(locID, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
log_data_err("Fail in udatpg_open locale %s: %s", locID, u_errorName(status));
|
||||
} else {
|
||||
UChar pattern[kUbufMax];
|
||||
int32_t patlen = udatpg_getBestPattern(dtpgen, testSkeleton, -1, pattern, kUbufMax, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
log_data_err("Fail in udatpg_getBestPattern locale %s: %s", locID, u_errorName(status));
|
||||
} else {
|
||||
UDateFormat *testFmt = udat_open(UDAT_PATTERN, UDAT_PATTERN, locID, NULL, 0, pattern, patlen, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
log_data_err("Fail in udat_open locale %s: %s", locID, u_errorName(status));
|
||||
} else {
|
||||
UChar testString[kUbufMax];
|
||||
int32_t testStrLen = udat_format(testFmt, refDate, testString, kUbufMax, NULL, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
log_err("Fail in udat_format locale %s: %s", locID, u_errorName(status));
|
||||
} else if (testStrLen < 3 || testString[2] != 0x5143) {
|
||||
char bbuf[kBbufMax];
|
||||
u_austrncpy(bbuf, testString, testStrLen);
|
||||
log_err("Formatting year 1 as Gannen, got%s but expected 3rd char to be 0x5143", bbuf);
|
||||
}
|
||||
udat_close(testFmt);
|
||||
}
|
||||
}
|
||||
udatpg_close(dtpgen);
|
||||
}
|
||||
}
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -1062,9 +1062,9 @@ void DateIntervalFormatTest::testFormat() {
|
|||
|
||||
"ja-u-ca-japanese", "H 31 03 15 09:00:00", "H 31 04 15 09:00:00", "GGGGGyMd", "H31/03/15\\uFF5E31/04/15",
|
||||
|
||||
"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\\u62101\\u5E741\\u670815\\u65E5",
|
||||
"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/1/5\\uFF5EH\\u5143/1/15", // The GGGGG/G forces inheritance from a different pattern, no padding
|
||||
|
||||
"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",
|
||||
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
#include "string.h"
|
||||
#include "unicode/locid.h"
|
||||
#include "japancal.h"
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/datefmt.h"
|
||||
#include "unicode/smpdtfmt.h"
|
||||
#include "unicode/dtptngen.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
|
@ -74,9 +78,10 @@ void IntlCalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &n
|
|||
CASE(4,TestBuddhistFormat);
|
||||
CASE(5,TestJapaneseFormat);
|
||||
CASE(6,TestJapanese3860);
|
||||
CASE(7,TestPersian);
|
||||
CASE(8,TestPersianFormat);
|
||||
CASE(9,TestTaiwan);
|
||||
CASE(7,TestForceGannenNumbering);
|
||||
CASE(8,TestPersian);
|
||||
CASE(9,TestPersianFormat);
|
||||
CASE(10,TestTaiwan);
|
||||
default: name = ""; break;
|
||||
}
|
||||
}
|
||||
|
@ -636,20 +641,20 @@ void IntlCalendarTest::TestJapanese3860()
|
|||
// Test parse with missing era (should default to current era, heisei)
|
||||
// Test parse with incomplete information
|
||||
logln("Testing parse w/ missing era...");
|
||||
SimpleDateFormat *fmt = new SimpleDateFormat(UnicodeString("y.M.d"), Locale("ja_JP@calendar=japanese"), status);
|
||||
SimpleDateFormat *fmt = new SimpleDateFormat(UnicodeString("y/M/d"), Locale("ja_JP@calendar=japanese"), status);
|
||||
CHECK(status, "creating date format instance");
|
||||
if(!fmt) {
|
||||
errln("Couldn't create en_US instance");
|
||||
} else {
|
||||
UErrorCode s2 = U_ZERO_ERROR;
|
||||
cal2->clear();
|
||||
UnicodeString samplestr("1.1.9");
|
||||
UnicodeString samplestr("1/5/9");
|
||||
logln(UnicodeString() + "Test Year: " + samplestr);
|
||||
aDate = fmt->parse(samplestr, s2);
|
||||
ParsePosition pp=0;
|
||||
fmt->parse(samplestr, *cal2, pp);
|
||||
CHECK(s2, "parsing the 1.1.9 string");
|
||||
logln("*cal2 after 119 parse:");
|
||||
CHECK(s2, "parsing the 1/5/9 string");
|
||||
logln("*cal2 after 159 parse:");
|
||||
str.remove();
|
||||
fmt2->format(aDate, str);
|
||||
logln(UnicodeString() + "as Gregorian Calendar: " + str);
|
||||
|
@ -660,7 +665,7 @@ void IntlCalendarTest::TestJapanese3860()
|
|||
int32_t expectYear = 1;
|
||||
int32_t expectEra = JapaneseCalendar::getCurrentEra();
|
||||
if((gotYear!=1) || (gotEra != expectEra)) {
|
||||
errln(UnicodeString("parse "+samplestr+" of 'y.m.d' as Japanese Calendar, expected year ") + expectYear +
|
||||
errln(UnicodeString("parse "+samplestr+" of 'y/M/d' as Japanese Calendar, expected year ") + expectYear +
|
||||
UnicodeString(" and era ") + expectEra +", but got year " + gotYear + " and era " + gotEra + " (Gregorian:" + str +")");
|
||||
} else {
|
||||
logln(UnicodeString() + " year: " + gotYear + ", era: " + gotEra);
|
||||
|
@ -714,8 +719,50 @@ void IntlCalendarTest::TestJapanese3860()
|
|||
delete fmt2;
|
||||
}
|
||||
|
||||
void IntlCalendarTest::TestForceGannenNumbering()
|
||||
{
|
||||
UErrorCode status;
|
||||
const char* locID = "ja_JP@calendar=japanese";
|
||||
Locale loc(locID);
|
||||
UDate refDate = 600336000000.0; // 1989 Jan 9 Monday = Heisei 1
|
||||
UnicodeString testSkeleton("yMMMd");
|
||||
|
||||
// Test Gannen year forcing
|
||||
status = U_ZERO_ERROR;
|
||||
LocalPointer<DateFormat> testFmt1(DateFormat::createInstanceForSkeleton(testSkeleton, loc, status));
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Fail in DateFormat::createInstanceForSkeleton locale %s: %s", locID, u_errorName(status));
|
||||
} else {
|
||||
UnicodeString testString1;
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
// Test disabling of Gannen year forcing
|
||||
status = U_ZERO_ERROR;
|
||||
LocalPointer<DateTimePatternGenerator> dtpgen(DateTimePatternGenerator::createInstance(loc, status));
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Fail in DateTimePatternGenerator::createInstance locale %s: %s", locID, u_errorName(status));
|
||||
} else {
|
||||
UnicodeString pattern = dtpgen->getBestPattern(testSkeleton, 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));
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the Persian Calendar.
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
void TestJapanese(void);
|
||||
void TestJapaneseFormat(void);
|
||||
void TestJapanese3860(void);
|
||||
void TestForceGannenNumbering(void);
|
||||
|
||||
void TestPersian(void);
|
||||
void TestPersianFormat(void);
|
||||
|
|
|
@ -939,6 +939,11 @@ public class SimpleDateFormat extends DateFormat {
|
|||
*/
|
||||
private transient boolean hasSecond;
|
||||
|
||||
/**
|
||||
* DateFormat pattern contains the Han year character \u5E74=年, => non-numeric E Asian format.
|
||||
*/
|
||||
private transient boolean hasHanYearChar;
|
||||
|
||||
/*
|
||||
* Capitalization setting, introduced in ICU 50
|
||||
* Special serialization, see writeObject & readObject below
|
||||
|
@ -1136,11 +1141,20 @@ public class SimpleDateFormat extends DateFormat {
|
|||
setLocale(calendar.getLocale(ULocale.VALID_LOCALE ), calendar.getLocale(ULocale.ACTUAL_LOCALE));
|
||||
initLocalZeroPaddingNumberFormat();
|
||||
|
||||
parsePattern(); // Need this before initNumberFormatters(), to set hasHanYearChar
|
||||
|
||||
// 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.
|
||||
if (override == null && hasHanYearChar &&
|
||||
calendar != null && calendar.getType().equals("japanese") &&
|
||||
locale != null && locale.getLanguage().equals("ja")) {
|
||||
override = "y=jpanyear";
|
||||
}
|
||||
|
||||
if (override != null) {
|
||||
initNumberFormatters(locale);
|
||||
}
|
||||
|
||||
parsePattern();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4555,6 +4569,7 @@ public class SimpleDateFormat extends DateFormat {
|
|||
private void parsePattern() {
|
||||
hasMinute = false;
|
||||
hasSecond = false;
|
||||
hasHanYearChar = false;
|
||||
|
||||
boolean inQuote = false;
|
||||
for (int i = 0; i < pattern.length(); ++i) {
|
||||
|
@ -4562,6 +4577,9 @@ public class SimpleDateFormat extends DateFormat {
|
|||
if (ch == '\'') {
|
||||
inQuote = !inQuote;
|
||||
}
|
||||
if (ch == '\u5E74') { // don't care whether this is inside quotes
|
||||
hasHanYearChar = true;
|
||||
}
|
||||
if (!inQuote) {
|
||||
if (ch == 'm') {
|
||||
hasMinute = true;
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.junit.runners.JUnit4;
|
|||
|
||||
import com.ibm.icu.impl.LocaleUtility;
|
||||
import com.ibm.icu.text.DateFormat;
|
||||
import com.ibm.icu.text.DateTimePatternGenerator;
|
||||
import com.ibm.icu.text.SimpleDateFormat;
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.JapaneseCalendar;
|
||||
|
@ -150,13 +151,17 @@ public class JapaneseTest extends CalendarTestFmwk {
|
|||
@Test
|
||||
public void Test3860()
|
||||
{
|
||||
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,
|
||||
// the year will be in the previous era.
|
||||
ULocale loc = new ULocale("ja_JP@calendar=japanese");
|
||||
Calendar cal = new JapaneseCalendar(loc);
|
||||
DateFormat enjformat = cal.getDateTimeFormat(DateFormat.FULL,DateFormat.FULL,new ULocale("en_JP@calendar=japanese"));
|
||||
DateFormat format = cal.getDateTimeFormat(DateFormat.SHORT,DateFormat.SHORT,loc); // SHORT => no jpanyear since we apply "y.M.d" anyway
|
||||
((SimpleDateFormat)format).applyPattern("y.M.d"); // Note: just 'y' doesn't work here.
|
||||
DateFormat format = cal.getDateTimeFormat(DateFormat.SHORT,DateFormat.SHORT,loc); // SHORT => no jpanyear since we will apply a short pattern
|
||||
((SimpleDateFormat)format).applyPattern(jCalShortPattern);
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
Date aDate = format.parse("1.5.9", pos); // after accession (Heisei or next). Before accession in the same year would format with the previous era.
|
||||
Date aDate = format.parse(jCalGannenDate, pos);
|
||||
String inEn = enjformat.format(aDate);
|
||||
|
||||
cal.clear();
|
||||
|
@ -207,7 +212,7 @@ public class JapaneseTest extends CalendarTestFmwk {
|
|||
|
||||
// Tests for formats with gannen numbering Gy年
|
||||
pos.setIndex(0);
|
||||
aDate = format.parse("1.5.9", pos); // reset
|
||||
aDate = format.parse(jCalGannenDate, pos); // reset
|
||||
DateFormat fmtWithGannen = DateFormat.getDateInstance(cal, DateFormat.MEDIUM, loc);
|
||||
String aString = fmtWithGannen.format(aDate);
|
||||
if (aString.charAt(2) != '\u5143') { // 元
|
||||
|
@ -227,6 +232,36 @@ 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,
|
||||
// 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";
|
||||
|
||||
// Test Gannen year forcing
|
||||
DateFormat testFmt1 = DateFormat.getInstanceForSkeleton(testSkeleton, 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");
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void Test5345parse() {
|
||||
// Test parse with incomplete information
|
||||
|
|
|
@ -710,11 +710,11 @@ public class DateIntervalFormatTest extends TestFmwk {
|
|||
|
||||
"ja-u-ca-japanese", "H 31 03 15 09:00:00", "H 31 04 15 09:00:00", "GGGGGyMd", "H31/03/15\uFF5E31/04/15",
|
||||
|
||||
"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\u62101\u5E741\u670815\u65E5",
|
||||
"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\uFF5EH1\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\u5E741\u67085\u65E5\uFF5EH\u5143\u5E741\u670815\u65E5", // The GGGGG/G forces inheritance from a different pattern, non-numeric-only
|
||||
|
||||
"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+"1\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\u5E744\u670815\u65E5\uFF5E"+DateFormat.JP_ERA_2019_NARROW+"\u5143\u5E745\u670815\u65E5",
|
||||
|
||||
};
|
||||
expect(DATA, DATA.length);
|
||||
|
|
Loading…
Add table
Reference in a new issue