+// © 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 *
#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"
#include <float.h>
#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 <stdio.h>
* 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
/*
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);
// 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);
}
// 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
{
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.
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
{
status = U_MISSING_RESOURCE_ERROR;
}
-
- parsePattern();
}
/* Initialize the fields we use to disambiguate ambiguous years. Separate
} 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);
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);
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);
// 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) {
// 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;
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;
}
}
+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,
{
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
+ }
+ }
+ }
+ }
}
//----------------------------------------------------------------------
{
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)
}
void SimpleDateFormat::parsePattern() {
fHasMinute = FALSE;
fHasSecond = FALSE;
+ fHasHanYearChar = FALSE;
int len = fPattern.length();
UBool inQuote = FALSE;
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;