Skip to content

Commit e50bac8

Browse files
committed
ICU-23056 Integrate CLDR 48 release alpha0, part 4, rework ICU4C eraNames loading
- Now that eraNames data are in map form and EraRules has been updated, rework the ICU4C eraNames code so the CalendarSink treats the eraNamese data as a table, then a new initEras function converts to array and fills in missing values from parent(s). This solves the problem with not loading the narrow modern era names in Japanese, for example. - Then we can remove the logKnownIssue test skips for ICU-23182/ICU-23186 - Also update tests for fixed Meiji start date
1 parent 35b961c commit e50bac8

File tree

4 files changed

+74
-81
lines changed

4 files changed

+74
-81
lines changed

icu4c/source/i18n/dtfmtsym.cpp

Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "cmemory.h"
4040
#include "cstring.h"
4141
#include "charstr.h"
42+
#include "erarules.h"
4243
#include "dt_impl.h"
4344
#include "locbased.h"
4445
#include "gregoimp.h"
@@ -1776,50 +1777,8 @@ struct CalendarDataSink : public ResourceSink {
17761777
arraySizes.puti(path, dataArraySize, errorCode);
17771778
if (U_FAILURE(errorCode)) { return; }
17781779
} else if (value.getType() == URES_TABLE) {
1779-
// We might have an eras table that is replacing an eras leaf array
1780-
if (path.startsWith(u"eras", 4)) {
1781-
// path is one of eras/wide, eras/abbreviated, eras/narrow
1782-
ResourceTable rDataTable = value.getTable(errorCode);
1783-
int32_t dataTableSize = rDataTable.getSize();
1784-
UVector dataList(uprv_deleteUObject, uhash_compareUnicodeString, dataTableSize, errorCode);
1785-
if (U_FAILURE(errorCode)) { return; }
1786-
// Expand the UVector as necessary to have index from 0 up to the max
1787-
// eraCode, and fill in the slots for the eras defined in the resource data
1788-
// (filling in empty strings for other eras as we expand, since they would
1789-
// otherwise not get set to anything in particular such as null).
1790-
for (int32_t dataTableIndex = 0; dataTableIndex < dataTableSize; dataTableIndex++) {
1791-
rDataTable.getKeyAndValue(dataTableIndex, key, value);
1792-
int32_t listIndex = uprv_strtol(key, nullptr, 10);
1793-
if (listIndex + 1 > dataList.size()) {
1794-
dataList.ensureCapacity(listIndex + 1, errorCode); // needed only to minimize expansions
1795-
if (U_FAILURE(errorCode)) { return; }
1796-
// Fill in empty strings for all added slots (else they are undefined)
1797-
while (dataList.size() < listIndex + 1) {
1798-
LocalPointer<UnicodeString> emptyString(new UnicodeString(), errorCode);
1799-
dataList.adoptElement(emptyString.orphan(), errorCode);
1800-
}
1801-
}
1802-
// Now set the eraName that we just read
1803-
LocalPointer<UnicodeString> eraName((value.getType() == URES_STRING) ?
1804-
new UnicodeString(value.getUnicodeString(errorCode)) : new UnicodeString(), errorCode);
1805-
if (U_FAILURE(errorCode)) { return; }
1806-
dataList.setElementAt(eraName.orphan(), listIndex);
1807-
}
1808-
// Now convert to array running from era code 0 to the max era we have data for, fill
1809-
// in from the UVector
1810-
int32_t dataArraySize = dataList.size();
1811-
LocalArray<UnicodeString> dataArray(new UnicodeString[dataArraySize], errorCode);
1812-
// Fill out dataArray from dataList (dataList.toArray did not seem to work as expected)
1813-
for (int32_t dataArrayIndex = 0; dataArrayIndex < dataArraySize; dataArrayIndex++) {
1814-
dataArray[dataArrayIndex] = std::move(*(reinterpret_cast<UnicodeString *>(dataList.elementAt(dataArrayIndex))));
1815-
}
1816-
// Save array...
1817-
arrays.put(path, dataArray.orphan(), errorCode);
1818-
arraySizes.puti(path, dataArraySize, errorCode);
1819-
} else {
1820-
// We are not on a leaf, recursively process the subtable.
1821-
processResource(path, key, value, errorCode);
1822-
}
1780+
// We are not on a leaf, recursively process the subtable.
1781+
processResource(path, key, value, errorCode);
18231782
if (U_FAILURE(errorCode)) { return; }
18241783
}
18251784

@@ -1944,6 +1903,51 @@ initField(UnicodeString **field, int32_t& length, CalendarDataSink &sink, CharSt
19441903
}
19451904
}
19461905

1906+
static void
1907+
initEras(UnicodeString **field, int32_t& length, CalendarDataSink &sink, CharString &key, const UResourceBundle *ctebPtr, const char* eraWidth, int32_t maxEra, UErrorCode &status) {
1908+
if (U_SUCCESS(status)) {
1909+
length = 0;
1910+
UnicodeString keyUString(key.data(), -1, US_INV);
1911+
Hashtable *eraNamesTable = static_cast<Hashtable*>(sink.maps.get(keyUString));
1912+
1913+
if (eraNamesTable != nullptr) {
1914+
UErrorCode resStatus = U_ZERO_ERROR;
1915+
LocalUResourceBundlePointer ctewb(ures_getByKeyWithFallback(ctebPtr, eraWidth, nullptr, &resStatus));
1916+
const UResourceBundle *ctewbPtr = (U_SUCCESS(resStatus))? ctewb.getAlias() : nullptr;
1917+
*field = new UnicodeString[maxEra + 1];
1918+
if (*field == nullptr) {
1919+
status = U_MEMORY_ALLOCATION_ERROR;
1920+
return;
1921+
}
1922+
length = maxEra + 1;
1923+
for (int32_t eraCode = 0; eraCode <= maxEra; eraCode++) {
1924+
char eraCodeStr[12]; // T_CString_integerToString is documented to generate at most 12 bytes including nul terminator
1925+
int32_t eraCodeStrLen = T_CString_integerToString(eraCodeStr, eraCode, 10);
1926+
UnicodeString eraCodeKey = UnicodeString(eraCodeStr, eraCodeStrLen, US_INV);
1927+
UnicodeString *eraName = static_cast<UnicodeString*>(eraNamesTable->get(eraCodeKey));
1928+
(*field)[eraCode].remove();
1929+
if (eraName != nullptr) {
1930+
// Get eraName from map (created by CalendarSink)
1931+
(*field)[eraCode].fastCopyFrom(*eraName);
1932+
} else if (ctewbPtr != nullptr) {
1933+
// Try filling in missing items from parent locale(s)
1934+
resStatus = U_ZERO_ERROR;
1935+
LocalUResourceBundlePointer ctewkb(ures_getByKeyWithFallback(ctewbPtr, eraCodeStr, nullptr, &resStatus));
1936+
if (U_SUCCESS(resStatus)) {
1937+
int32_t eraNameLen;
1938+
const UChar* eraNamePtr = ures_getString(ctewkb.getAlias(), &eraNameLen, &resStatus);
1939+
if (U_SUCCESS(resStatus)) {
1940+
(*field)[eraCode].setTo(false, eraNamePtr, eraNameLen);
1941+
}
1942+
}
1943+
}
1944+
}
1945+
return;
1946+
}
1947+
status = U_MISSING_RESOURCE_ERROR;
1948+
}
1949+
}
1950+
19471951
static void
19481952
initLeapMonthPattern(UnicodeString *field, int32_t index, CalendarDataSink &sink, CharString &path, UErrorCode &status) {
19491953
field[index].remove();
@@ -2344,17 +2348,30 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError
23442348
validLocale = Locale(ures_getLocaleByType(cb.getAlias(), ULOC_VALID_LOCALE, &status));
23452349
actualLocale = Locale(ures_getLocaleByType(cb.getAlias(), ULOC_ACTUAL_LOCALE, &status));
23462350

2351+
// Era setup
2352+
if (type == nullptr) {
2353+
type = "gregorian";
2354+
}
2355+
LocalPointer<EraRules> eraRules(EraRules::createInstance(type, false, status));
2356+
int32_t maxEra = (U_SUCCESS(status))? eraRules->getMaxEraCode(): 0;
2357+
UErrorCode resStatus = U_ZERO_ERROR;
2358+
LocalUResourceBundlePointer ctpb(ures_getByKeyWithFallback(cb.getAlias(), type, nullptr, &resStatus));
2359+
LocalUResourceBundlePointer cteb(ures_getByKeyWithFallback(ctpb.getAlias(), gErasTag, nullptr, &resStatus));
2360+
const UResourceBundle *ctebPtr = (U_SUCCESS(resStatus))? cteb.getAlias() : nullptr;
23472361
// Load eras
2348-
initField(&fEras, fErasCount, calendarSink, buildResourcePath(path, gErasTag, gNamesAbbrTag, status), status);
2362+
initEras(&fEras, fErasCount, calendarSink, buildResourcePath(path, gErasTag, gNamesAbbrTag, status),
2363+
ctebPtr, gNamesAbbrTag, maxEra, status);
23492364
UErrorCode oldStatus = status;
2350-
initField(&fEraNames, fEraNamesCount, calendarSink, buildResourcePath(path, gErasTag, gNamesWideTag, status), status);
2365+
initEras(&fEraNames, fEraNamesCount, calendarSink, buildResourcePath(path, gErasTag, gNamesWideTag, status),
2366+
ctebPtr, gNamesWideTag, maxEra, status);
23512367
if (status == U_MISSING_RESOURCE_ERROR) { // Workaround because eras/wide was omitted from CLDR 1.3
23522368
status = oldStatus;
23532369
assignArray(fEraNames, fEraNamesCount, fEras, fErasCount);
23542370
}
23552371
// current ICU4J falls back to abbreviated if narrow eras are missing, so we will too
23562372
oldStatus = status;
2357-
initField(&fNarrowEras, fNarrowErasCount, calendarSink, buildResourcePath(path, gErasTag, gNamesNarrowTag, status), status);
2373+
initEras(&fNarrowEras, fNarrowErasCount, calendarSink, buildResourcePath(path, gErasTag, gNamesNarrowTag, status),
2374+
ctebPtr, gNamesNarrowTag, maxEra, status);
23582375
if (status == U_MISSING_RESOURCE_ERROR) { // Workaround because eras/wide was omitted from CLDR 1.3
23592376
status = oldStatus;
23602377
assignArray(fNarrowEras, fNarrowErasCount, fEras, fErasCount);

icu4c/source/test/intltest/dtifmtts.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,11 +1251,6 @@ void DateIntervalFormatTest::expect(const char** data, int32_t data_length) {
12511251
FieldPosition pos(FieldPosition::DONT_CARE);
12521252
dtitvfmt->format(&dtitv, str.remove(), pos, ec);
12531253
if (!assertSuccess("format in expect", ec)) return;
1254-
if (strcmp(locName, "ja-u-ca-japanese") == 0 &&
1255-
logKnownIssue("ICU-23182", "Japanese calendar formatting")) {
1256-
i++;
1257-
continue;
1258-
}
12591254

12601255
if (strcmp(locName, "de") == 0 &&
12611256
(oneSkeleton == UnicodeString(u"hv",-1) || oneSkeleton == UnicodeString(u"hz",-1)) &&
@@ -2335,10 +2330,6 @@ void DateIntervalFormatTest::testTicket21222ROCEraDiff() {
23352330
void DateIntervalFormatTest::testTicket21222JapaneseEraDiff() {
23362331
IcuTestErrorCode status(*this, "testTicket21222JapaneseEraDiff");
23372332

2338-
if (logKnownIssue("ICU-23182", "Japanese calendar formatting")) {
2339-
return;
2340-
}
2341-
23422333
LocalPointer<Calendar> cal(Calendar::createInstance(*TimeZone::getGMT(), status));
23432334
if (U_FAILURE(status)) {
23442335
errln("Failure encountered: %s", u_errorName(status));

icu4c/source/test/intltest/dtptngts.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -696,11 +696,6 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
696696
resultDate = sdf.format(testDate, resultDate);
697697
if ( resultDate != patternResults[localeIndex][resultIndex] ) {
698698
const auto* calendar = sdf.getCalendar();
699-
if (localeIndex == 6 && (dataIndex == 1 || dataIndex == 3) &&
700-
logKnownIssue("ICU-23182", "Japanese calendar formatting")) {
701-
resultIndex++;
702-
continue;
703-
}
704699
errln(UnicodeString("\nERROR: Test various skeletons[") + (dataIndex-1) + UnicodeString("], localeIndex ") + localeIndex +
705700
u". Got: \"" + resultDate +
706701
u"\" with calendar " + calendar->getType() +

icu4c/source/test/intltest/incaltst.cpp

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,11 @@ void IntlCalendarTest::quasiGregorianTest(Calendar& cal, const Locale& gcl, cons
238238
cal.get(UCAL_YEAR, status) + "/" +
239239
(cal.get(UCAL_MONTH, status) + 1) + "/" + cal.get(UCAL_DATE, status) + " (" + UnicodeString(cal.getType()) + ")");
240240
} else {
241-
if (!logKnownIssue("ICU-23186", "Japanese calendar round trip fails")) {
242241
errln(UnicodeString("Fail: (millis to fields)") + D + " => " + cal.get(UCAL_ERA, status) + ":" +
243242
cal.get(UCAL_YEAR, status) + "/" +
244243
(cal.get(UCAL_MONTH, status)+1) + "/" + cal.get(UCAL_DATE, status) +
245244
", expected " + era + ":" + year + "/" + (month+1) + "/" +
246245
dayOfMonth + " (" + UnicodeString(cal.getType()));
247-
}
248246
}
249247
}
250248
delete grego;
@@ -405,8 +403,8 @@ void IntlCalendarTest::TestJapanese() {
405403
// BE 2542 == 1999 CE
406404
int32_t data[] = {
407405
// Jera Jyr Gyear m d
408-
JapaneseCalendar_MEIJI, 1, 1868, UCAL_SEPTEMBER, 8,
409-
JapaneseCalendar_MEIJI, 1, 1868, UCAL_SEPTEMBER, 9,
406+
JapaneseCalendar_MEIJI, 1, 1868, UCAL_OCTOBER, 23,
407+
JapaneseCalendar_MEIJI, 1, 1868, UCAL_OCTOBER, 24,
410408
JapaneseCalendar_MEIJI, 2, 1869, UCAL_JUNE, 4,
411409
JapaneseCalendar_MEIJI, 45, 1912, UCAL_JULY, 29,
412410
JapaneseCalendar_TAISHO, 1, 1912, UCAL_JULY, 30,
@@ -553,7 +551,7 @@ void IntlCalendarTest::TestJapaneseFormat() {
553551

554552
// Test parse with incomplete information
555553
SimpleDateFormat fmti(UnicodeString("G y"), Locale("en_US@calendar=japanese"), status);
556-
aDate = -3197117222000.0;
554+
aDate = -3193229222000.0;
557555
CHECK(status, "creating date format instance");
558556
str.remove();
559557
fmt2.format(aDate, str);
@@ -562,21 +560,17 @@ void IntlCalendarTest::TestJapaneseFormat() {
562560
fmti.format(aDate, str);
563561
logln(UnicodeString() + "as Japanese Calendar: " + str);
564562
expected = u"Meiji 1";
565-
if(str != expected) {
566-
if (!logKnownIssue("ICU-23186", "Japanese calendar round trip fails")) {
567-
errln("Expected " + expected + " but got " + str);
568-
}
563+
if (str != expected) {
564+
errln("Expected " + expected + " but got " + str);
569565
}
570566
otherDate = fmti.parse(expected, status);
571-
if(otherDate != aDate) {
567+
if (otherDate != aDate) {
572568
UnicodeString str3;
573569
ParsePosition pp;
574570
fmti.parse(expected, *cal2, pp);
575571
fmti.format(otherDate, str3);
576-
if (!logKnownIssue("ICU-23186", "Japanese calendar round trip fails")) {
577-
errln("Parse incorrect of " + expected + " - wanted " + aDate + " but got " + " = " +
578-
otherDate + ", " + str3 + " = " + CalendarTest::calToStr(*cal2) );
579-
}
572+
errln("Parse incorrect of " + expected + " - wanted " + aDate + " but got " + " = " +
573+
otherDate + ", " + str3 + " = " + CalendarTest::calToStr(*cal2) );
580574
} else {
581575
logln("Parsed OK: " + expected);
582576
}
@@ -745,18 +739,14 @@ void IntlCalendarTest::TestForceGannenNumbering()
745739
}
746740
testString2 = testFmt2->format(refDate, testString2);
747741
if (testString2.length() < 2 || testString2.charAt(1) != 0x0031) {
748-
if (!logKnownIssue("ICU-23182", "Japanese calendar formatting")) {
749-
errln(UnicodeString("Formatting year 1 in created numeric style, got " + testString2 + " but expected 2nd char to be 1"));
750-
}
742+
errln(UnicodeString("Formatting year 1 in created numeric style, got " + testString2 + " but expected 2nd char to be 1"));
751743
}
752744
// Now switch the patterns and verify that Gannen use follows the pattern
753745
testFmt1->applyPattern(patNumr);
754746
testString1.remove();
755747
testString1 = testFmt1->format(refDate, testString1);
756748
if (testString1.length() < 2 || testString1.charAt(1) != 0x0031) {
757-
if (!logKnownIssue("ICU-23182", "Japanese calendar formatting")) {
758-
errln(UnicodeString("Formatting year 1 in applied numeric style, got " + testString1 + " but expected 2nd char to be 1"));
759-
}
749+
errln(UnicodeString("Formatting year 1 in applied numeric style, got " + testString1 + " but expected 2nd char to be 1"));
760750
}
761751
testFmt2->applyPattern(patText);
762752
testString2.remove();

0 commit comments

Comments
 (0)