mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 22:44:49 +00:00
ICU-20629 Fixing DTPG hour-format locale and region fallback logic.
Includes changes in C and J. Makes region.cpp no longer depend on DecimalFormat. See also: CLDR-13071, ICU-20640, ICU-9982.
This commit is contained in:
parent
633a975849
commit
46c86b47cc
13 changed files with 886 additions and 674 deletions
|
@ -276,6 +276,16 @@ int32_t ICU_Utility::parsePattern(const UnicodeString& pat,
|
|||
return -1; // text ended before end of pat
|
||||
}
|
||||
|
||||
int32_t ICU_Utility::parseAsciiInteger(const UnicodeString& str, int32_t& pos) {
|
||||
int32_t result = 0;
|
||||
UChar c;
|
||||
while (pos < str.length() && (c = str.charAt(pos)) >= u'0' && c <= u'9') {
|
||||
result = result * 10 + (c - u'0');
|
||||
pos++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a character to a rule that is being built up. To flush
|
||||
* the quoteBuf to rule, make one final call with isLiteral == TRUE.
|
||||
|
|
|
@ -179,12 +179,21 @@ class U_COMMON_API ICU_Utility /* not : public UObject because all methods are s
|
|||
* Parse an integer at pos, either of the form \d+ or of the form
|
||||
* 0x[0-9A-Fa-f]+ or 0[0-7]+, that is, in standard decimal, hex,
|
||||
* or octal format.
|
||||
* @param pos INPUT-OUTPUT parameter. On input, the first
|
||||
* character to parse. On output, the character after the last
|
||||
* parsed character.
|
||||
* @param pos INPUT-OUTPUT parameter. On input, the index of the first
|
||||
* character to parse. On output, the index of the character after the
|
||||
* last parsed character.
|
||||
*/
|
||||
static int32_t parseInteger(const UnicodeString& rule, int32_t& pos, int32_t limit);
|
||||
|
||||
/**
|
||||
* Parse an integer at pos using only ASCII digits.
|
||||
* Base 10 only.
|
||||
* @param pos INPUT-OUTPUT parameter. On input, the index of the first
|
||||
* character to parse. On output, the index of the character after the
|
||||
* last parsed character.
|
||||
*/
|
||||
static int32_t parseAsciiInteger(const UnicodeString& str, int32_t& pos);
|
||||
|
||||
/**
|
||||
* Parse a Unicode identifier from the given string at the given
|
||||
* position. Return the identifier, or an empty string if there
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -65,6 +65,7 @@ likelySubtags:table(nofallback){
|
|||
arn{"arn_Latn_CL"}
|
||||
aro{"aro_Latn_BO"}
|
||||
arq{"arq_Arab_DZ"}
|
||||
ars{"ars_Arab_SA"}
|
||||
ary{"ary_Arab_MA"}
|
||||
arz{"arz_Arab_EG"}
|
||||
as{"as_Beng_IN"}
|
||||
|
|
|
@ -268,6 +268,8 @@ static ECalType getCalendarTypeForLocale(const char *locid) {
|
|||
|
||||
// canonicalize, so grandfathered variant will be transformed to keywords
|
||||
// e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese
|
||||
// NOTE: Since ICU-20187, ja_JP_TRADITIONAL no longer canonicalizes, and
|
||||
// the Gregorian calendar is returned instead.
|
||||
int32_t canonicalLen = uloc_canonicalize(locid, canonicalName, sizeof(canonicalName) - 1, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
return CALTYPE_GREGORIAN;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "unicode/ures.h"
|
||||
#include "unicode/ustring.h"
|
||||
#include "unicode/rep.h"
|
||||
#include "unicode/region.h"
|
||||
#include "cpputils.h"
|
||||
#include "mutex.h"
|
||||
#include "umutex.h"
|
||||
|
@ -613,29 +614,56 @@ U_CFUNC void U_CALLCONV DateTimePatternGenerator::loadAllowedHourFormatsData(UEr
|
|||
ures_getAllItemsWithFallback(rb.getAlias(), "timeData", sink, status);
|
||||
}
|
||||
|
||||
void DateTimePatternGenerator::getAllowedHourFormats(const Locale &locale, UErrorCode &status) {
|
||||
if (U_FAILURE(status)) { return; }
|
||||
Locale maxLocale(locale);
|
||||
maxLocale.addLikelySubtags(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *country = maxLocale.getCountry();
|
||||
if (*country == '\0') { country = "001"; }
|
||||
const char *language = maxLocale.getLanguage();
|
||||
|
||||
static int32_t* getAllowedHourFormatsLangCountry(const char* language, const char* country, UErrorCode& status) {
|
||||
CharString langCountry;
|
||||
langCountry.append(language, static_cast<int32_t>(uprv_strlen(language)), status);
|
||||
langCountry.append(language, status);
|
||||
langCountry.append('_', status);
|
||||
langCountry.append(country, static_cast<int32_t>(uprv_strlen(country)), status);
|
||||
langCountry.append(country, status);
|
||||
|
||||
int32_t *allowedFormats;
|
||||
int32_t* allowedFormats;
|
||||
allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, langCountry.data());
|
||||
if (allowedFormats == nullptr) {
|
||||
allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, const_cast<char *>(country));
|
||||
}
|
||||
|
||||
return allowedFormats;
|
||||
}
|
||||
|
||||
void DateTimePatternGenerator::getAllowedHourFormats(const Locale &locale, UErrorCode &status) {
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
const char *language = locale.getLanguage();
|
||||
const char *country = locale.getCountry();
|
||||
Locale maxLocale; // must be here for correct lifetime
|
||||
if (*language == '\0' || *country == '\0') {
|
||||
maxLocale = locale;
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
maxLocale.addLikelySubtags(localStatus);
|
||||
if (U_SUCCESS(localStatus)) {
|
||||
language = maxLocale.getLanguage();
|
||||
country = maxLocale.getCountry();
|
||||
}
|
||||
}
|
||||
if (*language == '\0') {
|
||||
// Unexpected, but fail gracefully
|
||||
language = "und";
|
||||
}
|
||||
if (*country == '\0') {
|
||||
country = "001";
|
||||
}
|
||||
|
||||
int32_t* allowedFormats = getAllowedHourFormatsLangCountry(language, country, status);
|
||||
|
||||
// Check if the region has an alias
|
||||
if (allowedFormats == nullptr) {
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
const Region* region = Region::getInstance(country, localStatus);
|
||||
if (U_SUCCESS(localStatus)) {
|
||||
country = region->getRegionCode(); // the real region code
|
||||
allowedFormats = getAllowedHourFormatsLangCountry(language, country, status);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowedFormats != nullptr) { // Lookup is successful
|
||||
// Here allowedFormats points to a list consisting of key for preferredFormat,
|
||||
// followed by one or more keys for allowedFormats, then followed by ALLOWED_HOUR_FORMAT_UNKNOWN.
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "unicode/uobject.h"
|
||||
#include "unicode/unistr.h"
|
||||
#include "unicode/ures.h"
|
||||
#include "unicode/decimfmt.h"
|
||||
#include "ucln_in.h"
|
||||
#include "cstring.h"
|
||||
#include "mutex.h"
|
||||
|
@ -33,6 +32,7 @@
|
|||
#include "umutex.h"
|
||||
#include "uresimp.h"
|
||||
#include "region_impl.h"
|
||||
#include "util.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
|
@ -87,7 +87,6 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) {
|
|||
LocalUHashtablePointer newRegionIDMap(uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status));
|
||||
LocalUHashtablePointer newNumericCodeMap(uhash_open(uhash_hashLong,uhash_compareLong,NULL,&status));
|
||||
LocalUHashtablePointer newRegionAliases(uhash_open(uhash_hashUnicodeString,uhash_compareUnicodeString,NULL,&status));
|
||||
LocalPointer<DecimalFormat> df(new DecimalFormat(status), status);
|
||||
|
||||
LocalPointer<UVector> continents(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status);
|
||||
LocalPointer<UVector> groupings(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status);
|
||||
|
@ -115,7 +114,6 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) {
|
|||
}
|
||||
|
||||
// now, initialize
|
||||
df->setParseIntegerOnly(TRUE);
|
||||
uhash_setValueDeleter(newRegionIDMap.getAlias(), deleteRegion); // regionIDMap owns objs
|
||||
uhash_setKeyDeleter(newRegionAliases.getAlias(), uprv_deleteUObject); // regionAliases owns the string keys
|
||||
|
||||
|
@ -192,11 +190,10 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) {
|
|||
r->idStr.extract(0,r->idStr.length(),r->id,sizeof(r->id),US_INV);
|
||||
r->fType = URGN_TERRITORY; // Only temporary - figure out the real type later once the aliases are known.
|
||||
|
||||
Formattable result;
|
||||
UErrorCode ps = U_ZERO_ERROR;
|
||||
df->parse(r->idStr,result,ps);
|
||||
if ( U_SUCCESS(ps) ) {
|
||||
r->code = result.getLong(); // Convert string to number
|
||||
int32_t pos = 0;
|
||||
int32_t result = ICU_Utility::parseAsciiInteger(r->idStr, pos);
|
||||
if (pos > 0) {
|
||||
r->code = result; // Convert string to number
|
||||
uhash_iput(newNumericCodeMap.getAlias(),r->code,(void *)(r.getAlias()),&status);
|
||||
r->fType = URGN_SUBCONTINENT;
|
||||
} else {
|
||||
|
@ -230,11 +227,10 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) {
|
|||
aliasFromRegion->idStr.setTo(*aliasFromStr);
|
||||
aliasFromRegion->idStr.extract(0,aliasFromRegion->idStr.length(),aliasFromRegion->id,sizeof(aliasFromRegion->id),US_INV);
|
||||
uhash_put(newRegionIDMap.getAlias(),(void *)&(aliasFromRegion->idStr),(void *)aliasFromRegion,&status);
|
||||
Formattable result;
|
||||
UErrorCode ps = U_ZERO_ERROR;
|
||||
df->parse(aliasFromRegion->idStr,result,ps);
|
||||
if ( U_SUCCESS(ps) ) {
|
||||
aliasFromRegion->code = result.getLong(); // Convert string to number
|
||||
int32_t pos = 0;
|
||||
int32_t result = ICU_Utility::parseAsciiInteger(aliasFromRegion->idStr, pos);
|
||||
if ( pos > 0 ) {
|
||||
aliasFromRegion->code = result; // Convert string to number
|
||||
uhash_iput(newNumericCodeMap.getAlias(),aliasFromRegion->code,(void *)aliasFromRegion,&status);
|
||||
} else {
|
||||
aliasFromRegion->code = -1;
|
||||
|
@ -279,11 +275,10 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) {
|
|||
|
||||
Region *r = (Region *)uhash_get(newRegionIDMap.getAlias(),(void *)&codeMappingID);
|
||||
if ( r ) {
|
||||
Formattable result;
|
||||
UErrorCode ps = U_ZERO_ERROR;
|
||||
df->parse(codeMappingNumber,result,ps);
|
||||
if ( U_SUCCESS(ps) ) {
|
||||
r->code = result.getLong(); // Convert string to number
|
||||
int32_t pos = 0;
|
||||
int32_t result = ICU_Utility::parseAsciiInteger(codeMappingNumber, pos);
|
||||
if ( pos > 0 ) {
|
||||
r->code = result; // Convert string to number
|
||||
uhash_iput(newNumericCodeMap.getAlias(),r->code,(void *)r,&status);
|
||||
}
|
||||
LocalPointer<UnicodeString> code3(new UnicodeString(codeMapping3Letter), status);
|
||||
|
@ -516,15 +511,8 @@ Region::getInstance (int32_t code, UErrorCode &status) {
|
|||
Region *r = (Region *)uhash_iget(numericCodeMap,code);
|
||||
|
||||
if ( !r ) { // Just in case there's an alias that's numeric, try to find it.
|
||||
UnicodeString pat = UNICODE_STRING_SIMPLE("0");
|
||||
LocalPointer<DecimalFormat> df(new DecimalFormat(pat,status), status);
|
||||
if( U_FAILURE(status) ) {
|
||||
return NULL;
|
||||
}
|
||||
UnicodeString id;
|
||||
id.remove();
|
||||
FieldPosition posIter;
|
||||
df->format(code,id, posIter, status);
|
||||
ICU_Utility::appendNumber(id, code, 10, 1);
|
||||
r = (Region *)uhash_get(regionAliases,&id);
|
||||
}
|
||||
|
||||
|
|
|
@ -849,7 +849,7 @@ library: i18n
|
|||
group: region
|
||||
region.o uregion.o
|
||||
deps
|
||||
formatting # Temporary, TODO: Ticket #9982 class Region should use low-level ASCII-integer functions, and probably be moved to the common library.
|
||||
icu_utility
|
||||
resourcebundle
|
||||
uvector uclean_i18n
|
||||
|
||||
|
@ -1018,6 +1018,7 @@ group: formatting
|
|||
trigonometry # for astro.o
|
||||
sharedbreakiterator # for reldatefmt.o
|
||||
uclean_i18n
|
||||
region
|
||||
|
||||
group: sharedbreakiterator
|
||||
sharedbreakiterator.o
|
||||
|
|
|
@ -41,12 +41,13 @@ void IntlTestDateTimePatternGeneratorAPI::runIndexedTest( int32_t index, UBool e
|
|||
TESTCASE(5, testSkeletonsWithDayPeriods);
|
||||
TESTCASE(6, testGetFieldDisplayNames);
|
||||
TESTCASE(7, testJjMapping);
|
||||
TESTCASE(8, testFallbackWithDefaultRootLocale);
|
||||
TESTCASE(8, test20640_HourCyclArsEnNH);
|
||||
TESTCASE(9, testFallbackWithDefaultRootLocale);
|
||||
default: name = ""; break;
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_LOCALE 11
|
||||
#define MAX_LOCALE 12
|
||||
|
||||
/**
|
||||
* Test various generic API methods of DateTimePatternGenerator for API coverage.
|
||||
|
@ -85,11 +86,12 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
{"zh", "Hans", "CN", ""}, // 7
|
||||
{"zh", "TW", "", "calendar=roc"}, // 8
|
||||
{"ru", "", "", ""}, // 9
|
||||
{"zh", "", "", "calendar=chinese"}, // 10
|
||||
{"zh", "", "", "calendar=chinese"}, // 10
|
||||
{"ja", "JP", "TRADITIONAL", ""}, // 11
|
||||
};
|
||||
|
||||
// For Weds, Jan 13, 1999, 23:58:59
|
||||
UnicodeString patternResults[] = {
|
||||
UnicodeString patternResults_en_US[] = {
|
||||
// en_US // 0 en_US
|
||||
UnicodeString("1/1999"), // 00: yM
|
||||
UnicodeString("Jan 1999"), // 01: yMMM
|
||||
|
@ -108,7 +110,9 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
UnicodeString("13 Wed"), // 14: Ed -> d EEE
|
||||
UnicodeString("11:58:59.123 PM"), // 15: jmmssSSS -> "h:mm:ss.SSS a"
|
||||
UnicodeString("11:58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString patternResults_en_US_japanese[] = {
|
||||
// en_US@calendar=japanese // 1 en_US@calendar=japanese
|
||||
UnicodeString("1/11 H"), // 0: yM
|
||||
UnicodeString("Jan 11 Heisei"), // 1: yMMM
|
||||
|
@ -127,7 +131,9 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
UnicodeString("13 Wed"), // 14: Ed -> d EEE
|
||||
UnicodeString("11:58:59.123 PM"), // 15: jmmssSSS -> "h:mm:ss.SSS a"
|
||||
UnicodeString("11:58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString patternResults_de_DE[] = {
|
||||
// de_DE // 2 de_DE
|
||||
UnicodeString("1.1999"), // 00: yM
|
||||
UnicodeString("Jan. 1999"), // 01: yMMM
|
||||
|
@ -146,7 +152,9 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
UnicodeString("Mi., 13."), // 14: Ed -> EEE d.
|
||||
UnicodeString("23:58:59,123"), // 15: jmmssSSS -> "HH:mm:ss,SSS"
|
||||
UnicodeString("23:58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString patternResults_fi[] = {
|
||||
// fi // 3 fi
|
||||
UnicodeString("1.1999"), // 00: yM (fixed expected result per ticket:6626:)
|
||||
UnicodeString("tammi 1999"), // 01: yMMM
|
||||
|
@ -165,7 +173,9 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
UnicodeString("ke 13."), // 14: Ed -> ccc d.
|
||||
UnicodeString("23.58.59,123"), // 15: jmmssSSS -> "H.mm.ss,SSS"
|
||||
UnicodeString("23.58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString patternResults_es[] = {
|
||||
// es // 4 es
|
||||
UnicodeString("1/1999"), // 00: yM -> "M/y"
|
||||
UnicodeString("ene. 1999"), // 01: yMMM -> "MMM y"
|
||||
|
@ -184,7 +194,9 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
CharsToUnicodeString("mi\\u00E9. 13"), // 14: Ed -> "EEE d"
|
||||
UnicodeString("23:58:59,123"), // 15: jmmssSSS -> "H:mm:ss,SSS"
|
||||
UnicodeString("23:58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString patternResults_ja[] = {
|
||||
// ja // 5 ja
|
||||
UnicodeString("1999/1"), // 00: yM -> y/M
|
||||
CharsToUnicodeString("1999\\u5E741\\u6708"), // 01: yMMM -> y\u5E74M\u6708
|
||||
|
@ -203,7 +215,9 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
CharsToUnicodeString("13\\u65E5(\\u6C34)"), // 14: Ed -> d\u65E5(EEE)
|
||||
UnicodeString("23:58:59.123"), // 15: jmmssSSS -> "H:mm:ss.SSS"
|
||||
UnicodeString("23:58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString patternResults_ja_japanese[] = {
|
||||
// ja@calendar=japanese // 6 ja@calendar=japanese
|
||||
UnicodeString("H11/1"), // 00: yM -> GGGGGy/m
|
||||
CharsToUnicodeString("\\u5E73\\u621011\\u5E741\\u6708"), // 01: yMMM -> Gy\u5E74M\u6708
|
||||
|
@ -222,7 +236,9 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
CharsToUnicodeString("13\\u65E5(\\u6C34)"), // 14: Ed -> d\u65E5(EEE)
|
||||
UnicodeString("23:58:59.123"), // 15: jmmssSSS -> "H:mm:ss.SSS"
|
||||
UnicodeString("23:58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString patternResults_zh_Hans_CN[] = {
|
||||
// zh_Hans_CN // 7 zh_Hans_CN
|
||||
CharsToUnicodeString("1999\\u5E741\\u6708"), // 00: yM -> y\u5E74M\u6708
|
||||
CharsToUnicodeString("1999\\u5E741\\u6708"), // 01: yMMM -> yyyy\u5E74MMM (fixed expected result per ticket:6626:)
|
||||
|
@ -241,7 +257,9 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
CharsToUnicodeString("13\\u65E5\\u5468\\u4E09"), // 14: Ed -> d\u65E5EEE
|
||||
CharsToUnicodeString("\\u4E0B\\u534811:58:59.123"), // 15: jmmssSSS -> "ah:mm:ss.SSS"
|
||||
UnicodeString("11:58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString patternResults_zh_TW_roc[] = {
|
||||
// zh_TW@calendar=roc // 8 zh_TW@calendar=roc
|
||||
CharsToUnicodeString("\\u6C11\\u570B88/1"), // 00: yM -> Gy/M
|
||||
CharsToUnicodeString("\\u6C11\\u570B88\\u5E741\\u6708"), // 01: yMMM -> Gy\u5E74M\u6708
|
||||
|
@ -260,7 +278,9 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
CharsToUnicodeString("13 \\u9031\\u4E09"), // 14: Ed -> d E
|
||||
CharsToUnicodeString("\\u4E0B\\u534811:58:59.123"), // 15: jmmssSSS -> "ah:mm:ss.SSS"
|
||||
UnicodeString("11:58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString patternResults_ru[] = {
|
||||
// ru // 9 ru
|
||||
UnicodeString("01.1999"), // 00: yM -> MM.y
|
||||
CharsToUnicodeString("\\u044F\\u043D\\u0432. 1999 \\u0433."), // 01: yMMM -> LLL y
|
||||
|
@ -279,7 +299,9 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
CharsToUnicodeString("\\u0441\\u0440, 13"), // 14: Ed -> EEE, d
|
||||
UnicodeString("23:58:59,123"), // 15: jmmssSSS -> "H:mm:ss,SSS"
|
||||
UnicodeString("23:58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString patternResults_zh_chinese[] = {
|
||||
// zh@calendar=chinese // 10 zh@calendar=chinese
|
||||
CharsToUnicodeString("1998\\u620A\\u5BC5\\u5E74\\u5341\\u4E00\\u6708"), // 00: yMMM
|
||||
CharsToUnicodeString("1998\\u620A\\u5BC5\\u5E74\\u5341\\u4E00\\u6708"), // 01: yMMM
|
||||
|
@ -298,8 +320,42 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
CharsToUnicodeString("26\\u65E5\\u5468\\u4E09"), // 14: Ed -> d\u65E5EEE
|
||||
CharsToUnicodeString("\\u4E0B\\u534811:58:59.123"), // 15: jmmssSS
|
||||
UnicodeString("11:58"), // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString(),
|
||||
UnicodeString patternResults_ja_jp_traditional[] = {
|
||||
// ja_JP_TRADITIONAL // 11 ja_JP_TRADITIONAL
|
||||
u"AD1999/1", // 00: yM
|
||||
u"西暦1999年1月", // 01: yMMM
|
||||
u"1999年1月13日", // 02: yMd
|
||||
u"西暦1999年1月13日", // 03: yMMMd
|
||||
u"1/13", // 04: Md
|
||||
u"1月13日", // 05: MMMd
|
||||
u"1月13日", // 06: MMMMd
|
||||
u"西暦1999/Q1", // 07: yQQQ
|
||||
u"午後11:58", // 08: hhmm
|
||||
u"23:58", // 09: HHmm
|
||||
u"23:58", // 10: jjmm
|
||||
u"58:59", // 11: mmss
|
||||
u"西暦1999年1月", // 12: yyyyMMMM
|
||||
u"1月13日(水)", // 13: MMMEd
|
||||
u"13日(水)", // 14: Ed
|
||||
u"23:58:59.123", // 15: jmmssSSS
|
||||
u"23:58", // 16: JJmm
|
||||
};
|
||||
|
||||
UnicodeString* patternResults[] = {
|
||||
patternResults_en_US, // 0
|
||||
patternResults_en_US_japanese, // 1
|
||||
patternResults_de_DE, // 2
|
||||
patternResults_fi, // 3
|
||||
patternResults_es, // 4
|
||||
patternResults_ja, // 5
|
||||
patternResults_ja_japanese, // 6
|
||||
patternResults_zh_Hans_CN, // 7
|
||||
patternResults_zh_TW_roc, // 8
|
||||
patternResults_ru, // 9
|
||||
patternResults_zh_chinese, // 10
|
||||
patternResults_ja_jp_traditional, // 11
|
||||
};
|
||||
|
||||
UnicodeString patternTests2[] = {
|
||||
|
@ -635,6 +691,7 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
UDate testDate= LocaleTest::date(99, 0, 13, 23, 58, 59) + 123.0;
|
||||
while (localeIndex < MAX_LOCALE )
|
||||
{
|
||||
resultIndex=0;
|
||||
int32_t dataIndex=0;
|
||||
UnicodeString bestPattern;
|
||||
|
||||
|
@ -653,9 +710,12 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
|
|||
SimpleDateFormat sdf(bestPattern, loc, status);
|
||||
resultDate.remove();
|
||||
resultDate = sdf.format(testDate, resultDate);
|
||||
if ( resultDate != patternResults[resultIndex] ) {
|
||||
if ( resultDate != patternResults[localeIndex][resultIndex] ) {
|
||||
auto* calendar = sdf.getCalendar();
|
||||
errln(UnicodeString("\nERROR: Test various skeletons[") + (dataIndex-1) + UnicodeString("], localeIndex ") + localeIndex +
|
||||
UnicodeString(". Got: \"") + resultDate + UnicodeString("\" Expected: \"") + patternResults[resultIndex] + "\"" );
|
||||
u". Got: \"" + resultDate +
|
||||
u"\" with calendar " + calendar->getType() +
|
||||
u" Expected: \"" + patternResults[localeIndex][resultIndex] + u"\"");
|
||||
}
|
||||
|
||||
resultIndex++;
|
||||
|
@ -1329,12 +1389,13 @@ void IntlTestDateTimePatternGeneratorAPI::testJjMapping() {
|
|||
for (; *charPtr != (UChar)0; charPtr++) {
|
||||
if (jPatSkeleton.indexOf(*charPtr) >= 0) {
|
||||
if (shortPatSkeleton.indexOf(*charPtr) < 0) {
|
||||
char jcBuf[2], spBuf[32];
|
||||
char jcBuf[2], spBuf[32], jpBuf[32];
|
||||
u_austrncpy(jcBuf, charPtr, 1);
|
||||
jcBuf[1] = 0;
|
||||
shortPattern.extract(0, shortPattern.length(), spBuf, 32);
|
||||
jPattern.extract(0, jPattern.length(), jpBuf, 32);
|
||||
const char* dfmtCalType = (dfmt->getCalendar())->getType();
|
||||
errln("ERROR: locale %s, expected j resolved char %s to occur in short time pattern %s for %s", localeID, jcBuf, spBuf, dfmtCalType);
|
||||
errln("ERROR: locale %s, expected j resolved char %s to occur in short time pattern '%s' for %s (best pattern: '%s')", localeID, jcBuf, spBuf, dfmtCalType, jpBuf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1342,6 +1403,46 @@ void IntlTestDateTimePatternGeneratorAPI::testJjMapping() {
|
|||
}
|
||||
}
|
||||
|
||||
void IntlTestDateTimePatternGeneratorAPI::test20640_HourCyclArsEnNH() {
|
||||
IcuTestErrorCode status(*this, "test20640_HourCyclArsEnNH");
|
||||
|
||||
const struct TestCase {
|
||||
const char* localeName;
|
||||
const char16_t* expectedDtpgPattern;
|
||||
const char16_t* expectedTimePattern;
|
||||
} cases[] = {
|
||||
// ars is interesting because it does not have a region, but it aliases
|
||||
// to ar_SA, which has a region.
|
||||
{"ars", u"h a", u"h:mm a"},
|
||||
// en_NH is interesting because NH is a depregated region code.
|
||||
{"en_NH", u"h a", u"h:mm a"},
|
||||
};
|
||||
|
||||
for (auto& cas : cases) {
|
||||
status.setScope(cas.localeName);
|
||||
|
||||
Locale loc(cas.localeName);
|
||||
LocalPointer<DateFormat> dtf(DateFormat::createTimeInstance(DateFormat::kShort, loc), status);
|
||||
LocalPointer<DateTimePatternGenerator> dtpg(DateTimePatternGenerator::createInstance(loc, status));
|
||||
if (status.errIfFailureAndReset()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UnicodeString timePattern;
|
||||
dynamic_cast<SimpleDateFormat*>(dtf.getAlias())->toPattern(timePattern);
|
||||
UnicodeString dtpgPattern = dtpg->getBestPattern(u"j", status);
|
||||
if (status.errIfFailureAndReset()) {
|
||||
return;
|
||||
}
|
||||
|
||||
assertEquals(UnicodeString("dtpgPattern ") + cas.localeName,
|
||||
cas.expectedDtpgPattern, dtpgPattern);
|
||||
assertEquals(UnicodeString("timePattern ") + cas.localeName,
|
||||
cas.expectedTimePattern, timePattern);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void IntlTestDateTimePatternGeneratorAPI::testFallbackWithDefaultRootLocale() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
char original[ULOC_FULLNAME_CAPACITY];
|
||||
|
|
|
@ -33,6 +33,7 @@ private:
|
|||
void testSkeletonsWithDayPeriods();
|
||||
void testGetFieldDisplayNames();
|
||||
void testJjMapping();
|
||||
void test20640_HourCyclArsEnNH();
|
||||
void testFallbackWithDefaultRootLocale();
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import com.ibm.icu.impl.UResource;
|
|||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.Freezable;
|
||||
import com.ibm.icu.util.ICUCloneNotSupportedException;
|
||||
import com.ibm.icu.util.Region;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.ULocale.Category;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
|
@ -319,6 +320,15 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
|||
|
||||
private static final String[] LAST_RESORT_ALLOWED_HOUR_FORMAT = {"H"};
|
||||
|
||||
private String[] getAllowedHourFormatsLangCountry(String language, String country) {
|
||||
String langCountry = language + "_" + country;
|
||||
String[] list = LOCALE_TO_ALLOWED_HOUR.get(langCountry);
|
||||
if (list == null) {
|
||||
list = LOCALE_TO_ALLOWED_HOUR.get(country);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void getAllowedHourFormats(ULocale uLocale) {
|
||||
// key can be either region or locale (lang_region)
|
||||
// ZW{
|
||||
|
@ -338,23 +348,38 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
|||
// preferred{"h"}
|
||||
// }
|
||||
|
||||
ULocale max = ULocale.addLikelySubtags(uLocale);
|
||||
String country = max.getCountry();
|
||||
String language = uLocale.getLanguage();
|
||||
String country = uLocale.getCountry();
|
||||
if (language.isEmpty() || country.isEmpty()) {
|
||||
ULocale max = ULocale.addLikelySubtags(uLocale);
|
||||
language = max.getLanguage();
|
||||
country = max.getCountry();
|
||||
}
|
||||
|
||||
if (language.isEmpty()) {
|
||||
// Unexpected, but fail gracefully
|
||||
language = "und";
|
||||
}
|
||||
if (country.isEmpty()) {
|
||||
country = "001";
|
||||
}
|
||||
String langCountry = max.getLanguage() + "_" + country;
|
||||
String[] list = LOCALE_TO_ALLOWED_HOUR.get(langCountry);
|
||||
|
||||
String[] list = getAllowedHourFormatsLangCountry(language, country);
|
||||
|
||||
// Check if the region has an alias
|
||||
if (list == null) {
|
||||
list = LOCALE_TO_ALLOWED_HOUR.get(country);
|
||||
Region region = Region.getInstance(country);
|
||||
country = region.toString();
|
||||
list = getAllowedHourFormatsLangCountry(language, country);
|
||||
}
|
||||
|
||||
if (list != null) {
|
||||
defaultHourFormatChar = list[0].charAt(0);
|
||||
allowedHourFormats = Arrays.copyOfRange(list, 1, list.length - 1);
|
||||
} else {
|
||||
allowedHourFormats = LAST_RESORT_ALLOWED_HOUR_FORMAT;
|
||||
defaultHourFormatChar = allowedHourFormats[0].charAt(0);
|
||||
}
|
||||
if (list != null) {
|
||||
defaultHourFormatChar = list[0].charAt(0);
|
||||
allowedHourFormats = Arrays.copyOfRange(list, 1, list.length-1);
|
||||
} else {
|
||||
allowedHourFormats = LAST_RESORT_ALLOWED_HOUR_FORMAT;
|
||||
defaultHourFormatChar = allowedHourFormats[0].charAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DayPeriodAllowedHoursSink extends UResource.Sink {
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c7156a5d2863005845db80b4bee68a1b7ad078af805e166897f07fdcf2c09f62
|
||||
size 12841498
|
||||
oid sha256:a502f64af80cdac743cbf045a3aa89c4b3c08d54980e38e79082c2a647325877
|
||||
size 12841510
|
||||
|
|
|
@ -576,6 +576,26 @@ public class DateTimeGeneratorTest extends TestFmwk {
|
|||
new String[] {"Ed", "26\u65E5\u5468\u4E09"},
|
||||
new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
|
||||
new String[] {"JJmm", "11:58"},
|
||||
|
||||
new ULocale("ja_JP_TRADITIONAL"),
|
||||
// TODO: This is different in C++ and Java.
|
||||
new String[] {"yM", "1999/1",},
|
||||
new String[] {"yMMM", "1999年1月"},
|
||||
new String[] {"yMd", "1999/1/13"},
|
||||
new String[] {"yMMMd", "1999年1月13日"},
|
||||
new String[] {"Md", "1/13"},
|
||||
new String[] {"MMMd", "1月13日"},
|
||||
new String[] {"MMMMd", "1月13日"},
|
||||
new String[] {"yQQQ", "1999/Q1"},
|
||||
new String[] {"hhmm", "午後11:58"},
|
||||
new String[] {"HHmm", "23:58"},
|
||||
new String[] {"jjmm", "23:58"},
|
||||
new String[] {"mmss", "58:59"},
|
||||
new String[] {"yyyyMMMM", "1999年1月"},
|
||||
new String[] {"MMMEd", "1月13日(水)"},
|
||||
new String[] {"Ed", "13日(水)"},
|
||||
new String[] {"jmmssSSS", "23:58:59.123"},
|
||||
new String[] {"JJmm", "23:58"}
|
||||
};
|
||||
|
||||
@Test
|
||||
|
@ -1707,4 +1727,29 @@ public class DateTimeGeneratorTest extends TestFmwk {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test20640_HourCyclArsEnNH() {
|
||||
String[][] cases = new String[][]{
|
||||
// ars is interesting because it does not have a region, but it aliases
|
||||
// to ar_SA, which has a region.
|
||||
{"ars", "h a", "h:mm a"},
|
||||
// en_NH is interesting because NH is a depregated region code.
|
||||
{"en_NH", "h a", "h:mm a"},
|
||||
};
|
||||
|
||||
for (String[] cas : cases) {
|
||||
ULocale loc = new ULocale(cas[0]);
|
||||
DateFormat dtf = DateFormat.getTimeInstance(DateFormat.SHORT, loc);
|
||||
DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(loc);
|
||||
|
||||
String timePattern = ((SimpleDateFormat)dtf).toPattern();
|
||||
String dtpgPattern = dtpg.getBestPattern("j");
|
||||
|
||||
assertEquals("dtpgPattern " + cas[0],
|
||||
cas[1], dtpgPattern);
|
||||
assertEquals("timePattern " + cas[1],
|
||||
cas[2], timePattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue