/*
*******************************************************************************
-* Copyright (C) 2007-2012, International Business Machines Corporation and
+* Copyright (C) 2007-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
#include "unicode/decimfmt.h"
#include "unicode/dtfmtsym.h"
#include "unicode/dtptngen.h"
-#include "unicode/msgfmt.h"
+#include "unicode/simpleformatter.h"
#include "unicode/smpdtfmt.h"
#include "unicode/udat.h"
#include "unicode/udatpg.h"
#include "unicode/ustring.h"
#include "unicode/rep.h"
#include "cpputils.h"
-#include "ucln_in.h"
#include "mutex.h"
+#include "umutex.h"
#include "cmemory.h"
#include "cstring.h"
#include "locbased.h"
#include "gregoimp.h"
#include "hash.h"
+#include "uhash.h"
#include "uresimp.h"
#include "dtptngen_impl.h"
-
-#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
+#include "ucln_in.h"
+#include "charstr.h"
#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
/**
U_NAMESPACE_BEGIN
-
// *****************************************************************************
// class DateTimePatternGenerator
// *****************************************************************************
{LOW_Y, UDATPG_YEAR_FIELD, DT_NUMERIC, 1, 20},
{CAP_Y, UDATPG_YEAR_FIELD, DT_NUMERIC + DT_DELTA, 1, 20},
{LOW_U, UDATPG_YEAR_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 20},
+ {LOW_R, UDATPG_YEAR_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20},
{CAP_U, UDATPG_YEAR_FIELD, DT_SHORT, 1, 3},
{CAP_U, UDATPG_YEAR_FIELD, DT_LONG, 4, 0},
{CAP_U, UDATPG_YEAR_FIELD, DT_NARROW, 5, 0},
{LOW_G, UDATPG_DAY_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20}, // really internal use, so we don't care
{LOW_A, UDATPG_DAYPERIOD_FIELD, DT_SHORT, 1, 0},
{CAP_H, UDATPG_HOUR_FIELD, DT_NUMERIC + 10*DT_DELTA, 1, 2}, // 24 hour
- {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2},
+ {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2}, // 24 hour
{LOW_H, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12 hour
- {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2},
+ {CAP_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // 12 hour
{LOW_M, UDATPG_MINUTE_FIELD, DT_NUMERIC, 1, 2},
{LOW_S, UDATPG_SECOND_FIELD, DT_NUMERIC, 1, 2},
{CAP_S, UDATPG_FRACTIONAL_SECOND_FIELD, DT_NUMERIC + DT_DELTA, 1, 1000},
{LOW_V, UDATPG_ZONE_FIELD, DT_LONG - 2*DT_DELTA, 4, 0},
{LOW_Z, UDATPG_ZONE_FIELD, DT_SHORT, 1, 3},
{LOW_Z, UDATPG_ZONE_FIELD, DT_LONG, 4, 0},
- {CAP_Z, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 3},
+ {CAP_Z, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 3},
{CAP_Z, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
- {CAP_V, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 3},
- {CAP_V, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
+ {CAP_Z, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 5, 0},
+ {CAP_O, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0},
+ {CAP_O, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
+ {CAP_V, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0},
+ {CAP_V, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 2, 0},
+ {CAP_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0},
+ {CAP_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0},
+ {CAP_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
+ {LOW_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0},
+ {LOW_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0},
+ {LOW_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
+ {LOW_J, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12/24 hour
+ {CAP_J, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12/24 hour no AM/PM
{0, UDATPG_FIELD_COUNT, 0, 0, 0} , // last row of dtTypes[]
};
};
static const char* const CLDR_FIELD_NAME[] = {
- "era", "year", "quarter", "month", "week", "*", "weekday", "day", "*", "*", "dayperiod",
+ "era", "year", "quarter", "month", "week", "*", "weekday", "*", "*", "day", "dayperiod",
"hour", "minute", "second", "*", "zone"
};
static const UChar UDATPG_ItemFormat[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A,
0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524
-static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ"
+//static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ"
static const char DT_DateTimePatternsTag[]="DateTimePatterns";
static const char DT_DateTimeCalendarTag[]="calendar";
DateTimePatternGenerator* U_EXPORT2
DateTimePatternGenerator::createInstance(const Locale& locale, UErrorCode& status) {
- DateTimePatternGenerator *result = new DateTimePatternGenerator(locale, status);
- if (result == NULL) {
- status = U_MEMORY_ALLOCATION_ERROR;
- }
if (U_FAILURE(status)) {
- delete result;
- result = NULL;
+ return NULL;
}
- return result;
+ LocalPointer<DateTimePatternGenerator> result(
+ new DateTimePatternGenerator(locale, status), status);
+ return U_SUCCESS(status) ? result.orphan() : NULL;
}
DateTimePatternGenerator* U_EXPORT2
DateTimePatternGenerator&
DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) {
+ // reflexive case
+ if (&other == this) {
+ return *this;
+ }
pLocale = other.pLocale;
fDefaultHourFormatChar = other.fDefaultHourFormatChar;
*fp = *(other.fp);
if (skipMatcher != NULL) delete skipMatcher;
}
+namespace {
+
+UInitOnce initOnce = U_INITONCE_INITIALIZER;
+UHashtable *localeToAllowedHourFormatsMap = NULL;
+
+// Value deleter for hashmap.
+void deleteAllowedHourFormats(void *ptr) {
+ uprv_free(ptr);
+}
+
+// Close hashmap at cleanup.
+UBool allowedHourFormatsCleanup() {
+ uhash_close(localeToAllowedHourFormatsMap);
+ return TRUE;
+}
+
+enum AllowedHourFormat{
+ ALLOWED_HOUR_FORMAT_UNKNOWN = -1,
+ ALLOWED_HOUR_FORMAT_h,
+ ALLOWED_HOUR_FORMAT_H,
+ ALLOWED_HOUR_FORMAT_hb,
+ ALLOWED_HOUR_FORMAT_Hb,
+ ALLOWED_HOUR_FORMAT_hB,
+ ALLOWED_HOUR_FORMAT_HB
+};
+
+} // namespace
+
void
DateTimePatternGenerator::initData(const Locale& locale, UErrorCode &status) {
//const char *baseLangName = locale.getBaseName(); // unused
addCLDRData(locale, status);
setDateTimeFromCalendar(locale, status);
setDecimalSymbols(locale, status);
+ umtx_initOnce(initOnce, loadAllowedHourFormatsData, status);
+ getAllowedHourFormats(locale, status);
} // DateTimePatternGenerator::initData
+namespace {
+
+struct AllowedHourFormatsSink : public ResourceTableSink {
+ // Initialize sub-sinks.
+ AllowedHourFormatsSink() : localeSink(*this), allowedListSink(*this) {}
+ virtual ~AllowedHourFormatsSink();
+
+ // Entry point.
+ virtual ResourceTableSink *getOrCreateTableSink(const char *key, int32_t, UErrorCode &status) {
+ if (U_FAILURE(status)) { return NULL; }
+
+ locale = key;
+ return &localeSink;
+ }
+
+ struct LocaleSink : public ResourceTableSink {
+ AllowedHourFormatsSink &outer;
+ LocaleSink(AllowedHourFormatsSink &outer) : outer(outer) {}
+ virtual ~LocaleSink();
+
+ virtual void put(const char *key, const ResourceValue &value, UErrorCode &status) {
+ if (U_FAILURE(status)) { return; }
+
+ if (uprv_strcmp(key, "allowed") == 0) {
+ outer.allowedFormats = static_cast<int32_t *>(uprv_malloc(2 * sizeof(int32_t)));
+ outer.allowedFormatsLength = 1;
+ if (outer.allowedFormats == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ outer.allowedFormats[0] = outer.getHourFormatFromUnicodeString(
+ value.getUnicodeString(status));
+ }
+ }
+
+ virtual ResourceArraySink *getOrCreateArraySink(const char *key, int32_t size, UErrorCode &status) {
+ if (U_FAILURE(status)) { return NULL; }
+
+ if (uprv_strcmp(key, "allowed") == 0) {
+ outer.allowedFormats = static_cast<int32_t *>(uprv_malloc((size + 1) * sizeof(int32_t)));
+ outer.allowedFormatsLength = size;
+ if (outer.allowedFormats == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ } else {
+ return &outer.allowedListSink;
+ }
+ }
+ return NULL;
+ }
+
+ virtual void leave(UErrorCode &status) {
+ if (U_FAILURE(status) || outer.allowedFormats == NULL) { return; }
+
+ outer.allowedFormats[outer.allowedFormatsLength] = ALLOWED_HOUR_FORMAT_UNKNOWN;
+ uhash_put(localeToAllowedHourFormatsMap, const_cast<char *>(outer.locale), outer.allowedFormats, &status);
+ outer.allowedFormats = NULL;
+ }
+ } localeSink;
+
+ struct AllowedListSink : public ResourceArraySink {
+ AllowedHourFormatsSink &outer;
+ AllowedListSink(AllowedHourFormatsSink &outer) : outer(outer) {}
+ virtual ~AllowedListSink();
+
+ virtual void put(int32_t index, const ResourceValue &value, UErrorCode &status) {
+ if (U_FAILURE(status)) { return; }
+
+ outer.allowedFormats[index] = outer.getHourFormatFromUnicodeString(
+ value.getUnicodeString(status));
+ }
+ } allowedListSink;
+
+ const char *locale;
+ int32_t *allowedFormats;
+ int32_t allowedFormatsLength;
+
+ AllowedHourFormat getHourFormatFromUnicodeString(UnicodeString s) {
+ if (s.length() == 1) {
+ if (s[0] == LOW_H) { return ALLOWED_HOUR_FORMAT_h; }
+ if (s[0] == CAP_H) { return ALLOWED_HOUR_FORMAT_H; }
+ } else if (s.length() == 2) {
+ if (s[0] == LOW_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_hb; }
+ if (s[0] == CAP_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Hb; }
+ if (s[0] == LOW_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_hB; }
+ if (s[0] == CAP_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_HB; }
+ }
+
+ return ALLOWED_HOUR_FORMAT_UNKNOWN;
+ }
+};
+
+} // namespace
+
+AllowedHourFormatsSink::~AllowedHourFormatsSink() {}
+AllowedHourFormatsSink::LocaleSink::~LocaleSink() {}
+AllowedHourFormatsSink::AllowedListSink::~AllowedListSink() {}
+
+void DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode &status) {
+ if (U_FAILURE(status)) { return; }
+ localeToAllowedHourFormatsMap = uhash_open(
+ uhash_hashChars, uhash_compareChars, NULL, &status);
+ uhash_setValueDeleter(localeToAllowedHourFormatsMap, deleteAllowedHourFormats);
+ LocalUResourceBundlePointer rb(ures_openDirect(NULL, "supplementalData", &status));
+
+ AllowedHourFormatsSink sink;
+ // TODO: Currently in the enumeration each table allocates a new array.
+ // Try to reduce the number of memory allocations. Consider storing a
+ // UVector32 with the concatenation of all of the sub-arrays, put the start index
+ // into the hashmap, store 6 single-value sub-arrays right at the beginning of the
+ // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime
+ // object. Remember to clean up the vector, too.
+ ures_getAllTableItemsWithFallback(rb.getAlias(), "timeData", sink, status);
+
+ ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS, allowedHourFormatsCleanup);
+}
+
+void DateTimePatternGenerator::getAllowedHourFormats(const Locale &locale, UErrorCode &status) {
+ if (U_FAILURE(status)) { return; }
+
+ const char *localeID = locale.getName();
+ char maxLocaleID[ULOC_FULLNAME_CAPACITY];
+ int32_t length = uloc_addLikelySubtags(localeID, maxLocaleID, ULOC_FULLNAME_CAPACITY, &status);
+ if (U_FAILURE(status)) {
+ return;
+ } else if (length == ULOC_FULLNAME_CAPACITY) { // no room for NUL
+ status = U_BUFFER_OVERFLOW_ERROR;
+ return;
+ }
+ Locale maxLocale = Locale(maxLocaleID);
+
+ const char *country = maxLocale.getCountry();
+ if (*country == '\0') { country = "001"; }
+ const char *language = maxLocale.getLanguage();
+
+ CharString langCountry;
+ langCountry.append(language, uprv_strlen(language), status);
+ langCountry.append('_', status);
+ langCountry.append(country, uprv_strlen(country), status);
+
+ int32_t *allowedFormats;
+ allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, langCountry.data());
+ if (allowedFormats == NULL) {
+ allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, const_cast<char *>(country));
+ }
+
+ if (allowedFormats != NULL) { // Lookup is successful
+ for (int32_t i = 0; i < UPRV_LENGTHOF(fAllowedHourFormats); ++i) {
+ fAllowedHourFormats[i] = allowedFormats[i];
+ if (allowedFormats[i] == ALLOWED_HOUR_FORMAT_UNKNOWN) {
+ break;
+ }
+ }
+ } else { // Lookup failed, twice
+ fAllowedHourFormats[0] = ALLOWED_HOUR_FORMAT_H;
+ fAllowedHourFormats[1] = ALLOWED_HOUR_FORMAT_UNKNOWN;
+ }
+}
+
UnicodeString
DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode&
/*status*/) {
- dtMatcher->set(pattern, fp);
- return dtMatcher->getSkeletonPtr()->getSkeleton();
+ FormatParser fp;
+ DateTimeMatcher matcher;
+ PtnSkeleton localSkeleton;
+ matcher.set(pattern, &fp, localSkeleton);
+ return localSkeleton.getSkeleton();
+}
+
+UnicodeString
+DateTimePatternGenerator::staticGetSkeleton(
+ const UnicodeString& pattern, UErrorCode& /*status*/) {
+ FormatParser fp;
+ DateTimeMatcher matcher;
+ PtnSkeleton localSkeleton;
+ matcher.set(pattern, &fp, localSkeleton);
+ return localSkeleton.getSkeleton();
}
UnicodeString
DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) {
- dtMatcher->set(pattern, fp);
- return dtMatcher->getSkeletonPtr()->getBaseSkeleton();
+ FormatParser fp;
+ DateTimeMatcher matcher;
+ PtnSkeleton localSkeleton;
+ matcher.set(pattern, &fp, localSkeleton);
+ return localSkeleton.getBaseSkeleton();
+}
+
+UnicodeString
+DateTimePatternGenerator::staticGetBaseSkeleton(
+ const UnicodeString& pattern, UErrorCode& /*status*/) {
+ FormatParser fp;
+ DateTimeMatcher matcher;
+ PtnSkeleton localSkeleton;
+ matcher.set(pattern, &fp, localSkeleton);
+ return localSkeleton.getBaseSkeleton();
}
void
const char *key=NULL;
int32_t i;
- UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, LENGTHOF(UDATPG_ItemFormat)-1); // Read-only alias.
+ UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, UPRV_LENGTHOF(UDATPG_ItemFormat)-1); // Read-only alias.
err = U_ZERO_ERROR;
-
+
fDefaultHourFormatChar = 0;
for (i=0; i<UDATPG_FIELD_COUNT; ++i ) {
appendItemNames[i]=CAP_F;
key=NULL;
err = U_ZERO_ERROR;
- fBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimeFieldsTag, NULL, &err);
+ fBundle = ures_getByKeyWithFallback(rb, DT_DateTimeFieldsTag, NULL, &err);
for (i=0; i<MAX_RESOURCE_FIELD; ++i) {
err = U_ZERO_ERROR;
patBundle = ures_getByKeyWithFallback(fBundle, Resource_Fields[i], NULL, &err);
UBool firstTimeThrough = TRUE;
err = U_ZERO_ERROR;
initHashtable(err);
+ UBool override = TRUE;
while (TRUE) {
// At the start of the loop:
// - rb is the open resource bundle for the current locale being processed,
setAvailableFormat(retKey, err);
// Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
// but not a previous availableFormats entry:
- addPatternWithSkeleton(format, &retKey, TRUE, conflictingPattern, err);
+ addPatternWithSkeleton(format, &retKey, override, conflictingPattern, err);
}
}
#if defined(U_USE_ASCII_BUNDLE_ITERATOR)
curLocaleName = parentLocale;
err = U_ZERO_ERROR;
}
+ if (uprv_strcmp(curLocaleName,"root")==0 || uprv_strlen(curLocaleName)==0) {
+ override = FALSE;
+ }
// Open calBundle and calTypeBundle
calBundle = ures_getByKeyWithFallback(rb, DT_DateTimeCalendarTag, NULL, &err);
if (U_SUCCESS(err)) {
}
}
// Go to the top of the loop to process contents of calTypeBundle
- }
+ }
if (hackPattern.length()>0) {
hackTimes(hackPattern, err);
const UnicodeString *bestPattern=NULL;
UnicodeString dtFormat;
UnicodeString resultPattern;
+ int32_t flags = kDTPGNoFlags;
int32_t dateMask=(1<<UDATPG_DAYPERIOD_FIELD) - 1;
int32_t timeMask=(1<<UDATPG_FIELD_COUNT) - 1 - dateMask;
+ // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
UnicodeString patternFormCopy = UnicodeString(patternForm);
- patternFormCopy.findAndReplace(UnicodeString(LOW_J), UnicodeString(fDefaultHourFormatChar));
+ UChar hourFormatSkeletonCharForLowJ = fDefaultHourFormatChar;
+ switch (options & UADATPG_FORCE_HOUR_CYCLE_MASK) {
+ case UADATPG_FORCE_12_HOUR_CYCLE: hourFormatSkeletonCharForLowJ = LOW_H; break;
+ case UADATPG_FORCE_24_HOUR_CYCLE: hourFormatSkeletonCharForLowJ = CAP_H; break;
+ default: break;
+ }
+ int32_t patPos, patLen = patternFormCopy.length();
+ UBool inQuoted = FALSE;
+ for (patPos = 0; patPos < patLen; patPos++) {
+ UChar patChr = patternFormCopy.charAt(patPos);
+ if (patChr == SINGLE_QUOTE) {
+ inQuoted = !inQuoted;
+ } else if (!inQuoted) {
+ if (patChr == LOW_J) {
+ patternFormCopy.setCharAt(patPos, hourFormatSkeletonCharForLowJ);
+ } else if (patChr == CAP_C) {
+ AllowedHourFormat preferred;
+ if (fAllowedHourFormats[0] != ALLOWED_HOUR_FORMAT_UNKNOWN) {
+ preferred = (AllowedHourFormat)fAllowedHourFormats[0];
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ return UnicodeString();
+ }
+
+ if (preferred == ALLOWED_HOUR_FORMAT_H || preferred == ALLOWED_HOUR_FORMAT_HB || preferred == ALLOWED_HOUR_FORMAT_Hb) {
+ patternFormCopy.setCharAt(patPos, CAP_H);
+ } else {
+ patternFormCopy.setCharAt(patPos, LOW_H);
+ }
+
+ if (preferred == ALLOWED_HOUR_FORMAT_HB || preferred == ALLOWED_HOUR_FORMAT_hB) {
+ flags |= kDTPGSkeletonUsesCapB;
+ } else if (preferred == ALLOWED_HOUR_FORMAT_Hb || preferred == ALLOWED_HOUR_FORMAT_hb) {
+ flags |= kDTPGSkeletonUsesLowB;
+ }
+ } else if (patChr == CAP_J) {
+ // Get pattern for skeleton with H, then in adjustFieldTypes
+ // replace hour pattern characters as necessary.
+ patternFormCopy.setCharAt(patPos, CAP_H);
+ flags |= kDTPGSkeletonUsesCapJ;
+ }
+ }
+ }
resultPattern.remove();
dtMatcher->set(patternFormCopy, fp);
const PtnSkeleton* specifiedSkeleton=NULL;
bestPattern=getBestRaw(*dtMatcher, -1, distanceInfo, &specifiedSkeleton);
if ( distanceInfo->missingFieldMask==0 && distanceInfo->extraFieldMask==0 ) {
- resultPattern = adjustFieldTypes(*bestPattern, specifiedSkeleton, FALSE, options);
+ resultPattern = adjustFieldTypes(*bestPattern, specifiedSkeleton, flags, options);
return resultPattern;
}
int32_t neededFields = dtMatcher->getFieldMask();
- UnicodeString datePattern=getBestAppending(neededFields & dateMask, options);
- UnicodeString timePattern=getBestAppending(neededFields & timeMask, options);
+ UnicodeString datePattern=getBestAppending(neededFields & dateMask, flags, options);
+ UnicodeString timePattern=getBestAppending(neededFields & timeMask, flags, options);
if (datePattern.length()==0) {
if (timePattern.length()==0) {
resultPattern.remove();
resultPattern.remove();
status = U_ZERO_ERROR;
dtFormat=getDateTimeFormat();
- Formattable dateTimeObject[] = { timePattern, datePattern };
- resultPattern = MessageFormat::format(dtFormat, dateTimeObject, 2, resultPattern, status );
+ SimpleFormatter(dtFormat, 2, 2, status).format(timePattern, datePattern, resultPattern, status);
return resultPattern;
}
UDateTimePatternMatchOptions options,
UErrorCode& /*status*/) {
dtMatcher->set(skeleton, fp);
- UnicodeString result = adjustFieldTypes(pattern, NULL, FALSE, options);
+ UnicodeString result = adjustFieldTypes(pattern, NULL, kDTPGNoFlags, options);
return result;
}
matcher.set(*skeletonToUse, fp, skeleton); // no longer trims skeleton fields to max len 3, per #7930
matcher.getBasePattern(basePattern); // or perhaps instead: basePattern = *skeletonToUse;
}
+ // We only care about base conflicts - and replacing the pattern associated with a base - if:
+ // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous
+ // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or
+ // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen
+ // if we are getting here from a subsequent call to addPattern).
+ // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking
+ // availableFormats items from root, which should not override any previous entry with the same base.
UBool entryHadSpecifiedSkeleton;
const UnicodeString *duplicatePattern = patternMap->getPatternFromBasePattern(basePattern, entryHadSpecifiedSkeleton);
- if (duplicatePattern != NULL ) {
+ if (duplicatePattern != NULL && (!entryHadSpecifiedSkeleton || (skeletonToUse != NULL && !override))) {
conflictingStatus = UDATPG_BASE_CONFLICT;
conflictingPattern = *duplicatePattern;
- if (!override || (skeletonToUse != NULL && entryHadSpecifiedSkeleton)) {
+ if (!override) {
return conflictingStatus;
}
}
+ // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats
+ // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with
+ // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for
+ // the previously-specified conflicting item.
const PtnSkeleton* entrySpecifiedSkeleton = NULL;
duplicatePattern = patternMap->getPatternFromSkeleton(skeleton, &entrySpecifiedSkeleton);
if (duplicatePattern != NULL ) {
UnicodeString
DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
const PtnSkeleton* specifiedSkeleton,
- UBool fixFractionalSeconds,
+ int32_t flags,
UDateTimePatternMatchOptions options) {
UnicodeString newPattern;
fp->set(pattern);
}
const dtTypeElem *row = &dtTypes[canonicalIndex];
int32_t typeValue = row->field;
- if (fixFractionalSeconds && typeValue == UDATPG_SECOND_FIELD) {
+
+ // Handle special day periods.
+ if (typeValue == UDATPG_DAYPERIOD_FIELD && flags != 0) {
+ UChar c = NONE; // '0'
+ if (flags & kDTPGSkeletonUsesCapB) { c = CAP_B; }
+ if (flags & kDTPGSkeletonUsesLowB) { c = LOW_B; }
+
+ if (c != NONE) {
+ for (int32_t i = 0; i < field.length(); ++i)
+ field.setCharAt(i, c);
+ }
+ }
+
+ if ((flags & kDTPGFixFractionalSeconds) != 0 && typeValue == UDATPG_SECOND_FIELD) {
UnicodeString newField=dtMatcher->skeleton.original[UDATPG_FRACTIONAL_SECOND_FIELD];
field = field + decimal + newField;
} else if (dtMatcher->skeleton.type[typeValue]!=0) {
}
}
UChar c = (typeValue!= UDATPG_HOUR_FIELD && typeValue!= UDATPG_MONTH_FIELD &&
- typeValue!= UDATPG_WEEKDAY_FIELD && typeValue!= UDATPG_YEAR_FIELD)?
+ typeValue!= UDATPG_WEEKDAY_FIELD && (typeValue!= UDATPG_YEAR_FIELD || reqField.charAt(0)==CAP_Y))?
reqField.charAt(0): field.charAt(0);
+ if (typeValue == UDATPG_HOUR_FIELD && (flags & kDTPGSkeletonUsesCapJ) != 0) {
+ c = fDefaultHourFormatChar;
+ switch (options & UADATPG_FORCE_HOUR_CYCLE_MASK) {
+ case UADATPG_FORCE_12_HOUR_CYCLE:
+ if (c == CAP_H || c == LOW_K) {
+ // Have 24-hour cycle, change to 12-hour cycle.
+ // Should have better way to pick 'h' or 'K'.
+ c = LOW_H;
+ };
+ break;
+ case UADATPG_FORCE_24_HOUR_CYCLE:
+ if (c == LOW_H || c == CAP_K) {
+ // Have 12-hour cycle, change to 24-hour cycle.
+ // Should have better way to pick 'H' or 'k'.
+ c = CAP_H;
+ };
+ break;
+ default:
+ break;
+ }
+ }
field.remove();
for (int32_t i=adjFieldLen; i>0; --i) {
field+=c;
}
UnicodeString
-DateTimePatternGenerator::getBestAppending(int32_t missingFields, UDateTimePatternMatchOptions options) {
+DateTimePatternGenerator::getBestAppending(int32_t missingFields, int32_t flags, UDateTimePatternMatchOptions options) {
UnicodeString resultPattern, tempPattern;
UErrorCode err=U_ZERO_ERROR;
int32_t lastMissingFieldMask=0;
resultPattern=UnicodeString();
const PtnSkeleton* specifiedSkeleton=NULL;
tempPattern = *getBestRaw(*dtMatcher, missingFields, distanceInfo, &specifiedSkeleton);
- resultPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, FALSE, options);
+ resultPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options);
if ( distanceInfo->missingFieldMask==0 ) {
return resultPattern;
}
}
if (((distanceInfo->missingFieldMask & UDATPG_SECOND_AND_FRACTIONAL_MASK)==UDATPG_FRACTIONAL_MASK) &&
((missingFields & UDATPG_SECOND_AND_FRACTIONAL_MASK) == UDATPG_SECOND_AND_FRACTIONAL_MASK)) {
- resultPattern = adjustFieldTypes(resultPattern, specifiedSkeleton, TRUE, options);
+ resultPattern = adjustFieldTypes(resultPattern, specifiedSkeleton, flags | kDTPGFixFractionalSeconds, options);
distanceInfo->missingFieldMask &= ~UDATPG_FRACTIONAL_MASK;
continue;
}
int32_t startingMask = distanceInfo->missingFieldMask;
tempPattern = *getBestRaw(*dtMatcher, distanceInfo->missingFieldMask, distanceInfo, &specifiedSkeleton);
- tempPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, FALSE, options);
+ tempPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options);
int32_t foundMask=startingMask& ~distanceInfo->missingFieldMask;
int32_t topField=getTopBitNumber(foundMask);
UnicodeString appendName;
getAppendName((UDateTimePatternField)topField, appendName);
- const Formattable formatPattern[] = {
- resultPattern,
- tempPattern,
- appendName
+ const UnicodeString *values[3] = {
+ &resultPattern,
+ &tempPattern,
+ &appendName
};
- UnicodeString emptyStr;
- resultPattern = MessageFormat::format(appendItemFormats[topField], formatPattern, 3, emptyStr, err);
+ SimpleFormatter(appendItemFormats[topField], 2, 3, err).
+ formatAndReplace(values, 3, resultPattern, NULL, 0, err);
lastMissingFieldMask = distanceInfo->missingFieldMask;
}
}
if(U_FAILURE(status)){
return;
}
- int32_t pos = -1;
+ int32_t pos = UHASH_FIRST;
const UHashElement* elem = NULL;
// walk through the hash table and create a deep clone
while((elem = other->nextElement(pos))!= NULL){
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
-
+ curElem->skeletonWasSpecified = otherElem->skeletonWasSpecified;
if (prevElem!=NULL) {
prevElem->next=curElem;
}
}
// Overwrite the value.
curElem->pattern = value;
+ // It was a bug that we were not doing the following previously,
+ // though that bug hid other problems by making things partly work.
+ curElem->skeletonWasSpecified = skeletonWasSpecified;
}
}
} // PatternMap::add
}
UBool
-FormatParser::isQuoteLiteral(const UnicodeString& s) const {
+FormatParser::isQuoteLiteral(const UnicodeString& s) {
return (UBool)(s.charAt(0)==SINGLE_QUOTE);
}