X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/2ca993e82fb37b597a3c73ecd1586a139a6579c5..249c4c5ea9376c24572daf9c2effa7484a282f14:/icuSources/i18n/smpdtfmt.cpp diff --git a/icuSources/i18n/smpdtfmt.cpp b/icuSources/i18n/smpdtfmt.cpp index 2bcb5920..9feee023 100644 --- a/icuSources/i18n/smpdtfmt.cpp +++ b/icuSources/i18n/smpdtfmt.cpp @@ -1,3 +1,5 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* * Copyright (C) 1997-2016, International Business Machines Corporation and * @@ -46,14 +48,15 @@ #include "unicode/simpletz.h" #include "unicode/rbtz.h" #include "unicode/tzfmt.h" +#include "unicode/ucasemap.h" #include "unicode/utf16.h" #include "unicode/vtzone.h" #include "unicode/udisplaycontext.h" #include "unicode/brkiter.h" +#include "uresimp.h" #include "olsontz.h" #include "patternprops.h" #include "fphdlimp.h" -#include "gregoimp.h" #include "hebrwcal.h" #include "cstring.h" #include "uassert.h" @@ -62,11 +65,13 @@ #include #include "smpdtfst.h" #include "sharednumberformat.h" +#include "ucasemap_imp.h" #include "ustr_imp.h" #include "charstr.h" #include "uvector.h" #include "cstr.h" #include "dayperiodrules.h" +#include "tznames_impl.h" // ZONE_NAME_U16_MAX #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) #include @@ -164,9 +169,6 @@ static const UChar SUPPRESS_NEGATIVE_PREFIX[] = {0xAB00, 0}; * These are the tags we expect to see in normal resource bundle files associated * with a locale. */ -static const char gDateTimePatternsTag[]="DateTimePatterns"; - -//static const UChar gEtcUTC[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC" static const UChar QUOTE = 0x27; // Single quote /* @@ -755,20 +757,42 @@ void SimpleDateFormat::construct(EStyle timeStyle, initializeCalendar(NULL, locale, status); if (U_FAILURE(status)) return; - CalendarData calData(locale, fCalendar?fCalendar->getType():NULL, status); - UResourceBundle *dateTimePatterns = calData.getByKey(gDateTimePatternsTag, status); - UResourceBundle *currentBundle; + // Load date time patterns directly from resources. + const char* cType = fCalendar ? fCalendar->getType() : NULL; + LocalUResourceBundlePointer bundle(ures_open(NULL, locale.getBaseName(), &status)); + if (U_FAILURE(status)) return; + + UBool cTypeIsGregorian = TRUE; + LocalUResourceBundlePointer dateTimePatterns; + if (cType != NULL && uprv_strcmp(cType, "gregorian") != 0) { + CharString resourcePath("calendar/", status); + resourcePath.append(cType, status).append("/DateTimePatterns", status); + dateTimePatterns.adoptInstead( + ures_getByKeyWithFallback(bundle.getAlias(), resourcePath.data(), + (UResourceBundle*)NULL, &status)); + cTypeIsGregorian = FALSE; + } + // Check for "gregorian" fallback. + if (cTypeIsGregorian || status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + dateTimePatterns.adoptInstead( + ures_getByKeyWithFallback(bundle.getAlias(), + "calendar/gregorian/DateTimePatterns", + (UResourceBundle*)NULL, &status)); + } if (U_FAILURE(status)) return; - if (ures_getSize(dateTimePatterns) <= kDateTime) + LocalUResourceBundlePointer currentBundle; + + if (ures_getSize(dateTimePatterns.getAlias()) <= kDateTime) { status = U_INVALID_FORMAT_ERROR; return; } - setLocaleIDs(ures_getLocaleByType(dateTimePatterns, ULOC_VALID_LOCALE, &status), - ures_getLocaleByType(dateTimePatterns, ULOC_ACTUAL_LOCALE, &status)); + setLocaleIDs(ures_getLocaleByType(dateTimePatterns.getAlias(), ULOC_VALID_LOCALE, &status), + ures_getLocaleByType(dateTimePatterns.getAlias(), ULOC_ACTUAL_LOCALE, &status)); // create a symbols object from the locale fSymbols = DateFormatSymbols::createForLocale(locale, status); @@ -789,66 +813,64 @@ void SimpleDateFormat::construct(EStyle timeStyle, // and time pattern strings. if ((timeStyle != kNone) && (dateStyle != kNone)) { - currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)timeStyle, NULL, &status); + currentBundle.adoptInstead( + ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)timeStyle, NULL, &status)); if (U_FAILURE(status)) { status = U_INVALID_FORMAT_ERROR; return; } - switch (ures_getType(currentBundle)) { + switch (ures_getType(currentBundle.getAlias())) { case URES_STRING: { - resStr = ures_getString(currentBundle, &resStrLen, &status); + resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status); break; } case URES_ARRAY: { - resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); - ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); + resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status); + ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status); fTimeOverride.setTo(TRUE, ovrStr, ovrStrLen); break; } default: { status = U_INVALID_FORMAT_ERROR; - ures_close(currentBundle); return; } } - ures_close(currentBundle); UnicodeString tempus1(TRUE, resStr, resStrLen); - currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)dateStyle, NULL, &status); + currentBundle.adoptInstead( + ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)dateStyle, NULL, &status)); if (U_FAILURE(status)) { status = U_INVALID_FORMAT_ERROR; return; } - switch (ures_getType(currentBundle)) { + switch (ures_getType(currentBundle.getAlias())) { case URES_STRING: { - resStr = ures_getString(currentBundle, &resStrLen, &status); + resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status); break; } case URES_ARRAY: { - resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); - ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); + resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status); + ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status); fDateOverride.setTo(TRUE, ovrStr, ovrStrLen); break; } default: { status = U_INVALID_FORMAT_ERROR; - ures_close(currentBundle); return; } } - ures_close(currentBundle); UnicodeString tempus2(TRUE, resStr, resStrLen); int32_t glueIndex = kDateTime; - int32_t patternsSize = ures_getSize(dateTimePatterns); + int32_t patternsSize = ures_getSize(dateTimePatterns.getAlias()); if (patternsSize >= (kDateTimeOffset + kShort + 1)) { // Get proper date time format glueIndex = (int32_t)(kDateTimeOffset + (dateStyle - kDateOffset)); } - resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &status); + resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), glueIndex, &resStrLen, &status); SimpleFormatter(UnicodeString(TRUE, resStr, resStrLen), 2, 2, status). format(tempus1, tempus2, fPattern, status); } @@ -856,56 +878,54 @@ void SimpleDateFormat::construct(EStyle timeStyle, // pattern string from the resources // setTo() - see DateFormatSymbols::assignArray comments else if (timeStyle != kNone) { - currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)timeStyle, NULL, &status); + currentBundle.adoptInstead( + ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)timeStyle, NULL, &status)); if (U_FAILURE(status)) { status = U_INVALID_FORMAT_ERROR; return; } - switch (ures_getType(currentBundle)) { + switch (ures_getType(currentBundle.getAlias())) { case URES_STRING: { - resStr = ures_getString(currentBundle, &resStrLen, &status); + resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status); break; } case URES_ARRAY: { - resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); - ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); + resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status); + ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status); fDateOverride.setTo(TRUE, ovrStr, ovrStrLen); break; } default: { status = U_INVALID_FORMAT_ERROR; - ures_close(currentBundle); return; } } fPattern.setTo(TRUE, resStr, resStrLen); - ures_close(currentBundle); } else if (dateStyle != kNone) { - currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)dateStyle, NULL, &status); + currentBundle.adoptInstead( + ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)dateStyle, NULL, &status)); if (U_FAILURE(status)) { status = U_INVALID_FORMAT_ERROR; return; } - switch (ures_getType(currentBundle)) { + switch (ures_getType(currentBundle.getAlias())) { case URES_STRING: { - resStr = ures_getString(currentBundle, &resStrLen, &status); + resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status); break; } case URES_ARRAY: { - resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); - ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); + resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status); + ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status); fDateOverride.setTo(TRUE, ovrStr, ovrStrLen); break; } default: { status = U_INVALID_FORMAT_ERROR; - ures_close(currentBundle); return; } } fPattern.setTo(TRUE, resStr, resStrLen); - ures_close(currentBundle); } // and if it includes _neither_, that's an error @@ -933,6 +953,8 @@ SimpleDateFormat::initialize(const Locale& locale, { if (U_FAILURE(status)) return; + parsePattern(); // Need this before initNumberFormatters(), to set fHasHanYearChar + // If the locale has @[....]numbers=hanidays we want to *delete* that (so it // it is not used for every field) and then set fDateOverride to "d=hanidays" // (as with std formats for zh@calendar=chinese) to use hanidays for d field. @@ -947,6 +969,14 @@ SimpleDateFormat::initialize(const Locale& locale, fDateOverride.setTo(hanidaysOverride,-1); } } + // Simple-minded hack to force Gannen year numbering for ja@calendar=japanese + // if format is non-numeric (includes 年) and fDateOverride is not already specified. + // 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) { + 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 @@ -963,8 +993,6 @@ SimpleDateFormat::initialize(const Locale& locale, { status = U_MISSING_RESOURCE_ERROR; } - - parsePattern(); } /* Initialize the fields we use to disambiguate ambiguous years. Separate @@ -1621,7 +1649,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, } else if (count == 2) { value /= 10; } - FieldPosition p(0); + FieldPosition p(FieldPosition::DONT_CARE); currentNumberFormat->format(value, appendTo, p); if (count > 3) { currentNumberFormat->setMinimumIntegerDigits(count - 3); @@ -1739,7 +1767,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, case UDAT_TIMEZONE_ISO_FIELD: // 'X' case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x' { - UChar zsbuf[64]; + UChar zsbuf[ZONE_NAME_U16_MAX]; UnicodeString zoneString(zsbuf, 0, UPRV_LENGTHOF(zsbuf)); const TimeZone& tz = cal.getTimeZone(); UDate date = cal.getTime(status); @@ -2108,7 +2136,7 @@ SimpleDateFormat::zeroPaddingNumber( int32_t value, int32_t minDigits, int32_t maxDigits) const { if (currentNumberFormat!=NULL) { - FieldPosition pos(0); + FieldPosition pos(FieldPosition::DONT_CARE); currentNumberFormat->setMinimumIntegerDigits(minDigits); currentNumberFormat->setMaximumIntegerDigits(maxDigits); @@ -3137,9 +3165,9 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC // is treated literally: "2250", "-1", "1", "002". if (fDateOverride.compare(hebr)==0 && value < 1000) { value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR; - } else if ((pos.getIndex() - start) == 2 && !isChineseCalendar - && u_isdigit(text.charAt(start)) - && u_isdigit(text.charAt(start+1))) + } else if (text.moveIndex32(start, 2) == pos.getIndex() && !isChineseCalendar + && u_isdigit(text.char32At(start)) + && u_isdigit(text.char32At(text.moveIndex32(start, 1)))) { // only adjust year for patterns less than 3. if(count < 3) { @@ -3177,9 +3205,9 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC // Comment is the same as for UDAT_Year_FIELDs - look above if (fDateOverride.compare(hebr)==0 && value < 1000) { value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR; - } else if ((pos.getIndex() - start) == 2 - && u_isdigit(text.charAt(start)) - && u_isdigit(text.charAt(start+1)) + } else if (text.moveIndex32(start, 2) == pos.getIndex() + && u_isdigit(text.char32At(start)) + && u_isdigit(text.char32At(text.moveIndex32(start, 1))) && fHaveDefaultCentury ) { int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100; @@ -3285,7 +3313,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC case UDAT_FRACTIONAL_SECOND_FIELD: // Fractional seconds left-justify - i = pos.getIndex() - start; + i = countDigits(text, start, pos.getIndex()); if (i < 3) { while (i < 3) { value *= 10; @@ -3816,6 +3844,19 @@ void SimpleDateFormat::parseInt(const UnicodeString& text, } } +int32_t SimpleDateFormat::countDigits(const UnicodeString& text, int32_t start, int32_t end) const { + int32_t numDigits = 0; + int32_t idx = start; + while (idx < end) { + UChar32 cp = text.char32At(idx); + if (u_isdigit(cp)) { + numDigits++; + } + idx += U16_LENGTH(cp); + } + return numDigits; +} + //---------------------------------------------------------------------- void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern, @@ -3890,6 +3931,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 = createSharedNumberFormat(ovrLoc, status); + 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]); + snf->deleteIfZeroRefCount(); + fDateOverride.setTo(u"y=jpanyear", -1); // record status + } + } + } + } } //---------------------------------------------------------------------- @@ -3957,15 +4034,19 @@ void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt) { UErrorCode status = U_ZERO_ERROR; Locale calLocale(fLocale); - calLocale.setKeywordValue("calendar", calendarToAdopt->getType(), status); - DateFormatSymbols *newSymbols = - DateFormatSymbols::createForLocale(calLocale, status); - if (U_FAILURE(status)) { - return; + DateFormatSymbols *newSymbols = fSymbols; + if (!newSymbols || fCalendar->getType() != calendarToAdopt->getType()) { + calLocale.setKeywordValue("calendar", calendarToAdopt->getType(), status); + newSymbols = DateFormatSymbols::createForLocale(calLocale, status); + if (U_FAILURE(status)) { + return; + } } DateFormat::adoptCalendar(calendarToAdopt); - delete fSymbols; - fSymbols = newSymbols; + if (fSymbols != newSymbols) { + delete fSymbols; + fSymbols = newSymbols; + } initializeDefaultCentury(); // we need a new century (possibly) } @@ -4220,6 +4301,7 @@ SimpleDateFormat::tzFormat() const { void SimpleDateFormat::parsePattern() { fHasMinute = FALSE; fHasSecond = FALSE; + fHasHanYearChar = FALSE; int len = fPattern.length(); UBool inQuote = FALSE; @@ -4228,6 +4310,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;