]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/dtptngen.cpp
ICU-59180.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / dtptngen.cpp
index 3e10f4277aa2854129df9d4ca80f0aac772e5d9a..5fccfc07f6978434114bd7642b54b7fb226d4c07 100644 (file)
@@ -1,6 +1,8 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 *******************************************************************************
-* Copyright (C) 2007-2014, International Business Machines Corporation and
+* Copyright (C) 2007-2016, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 *
@@ -16,7 +18,7 @@
 #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"
+#include "uassert.h"
 
 #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
 /**
@@ -127,7 +130,6 @@ static const UChar *ures_a_getNextString(UResourceBundleAIterator *aiter, int32_
 
 U_NAMESPACE_BEGIN
 
-
 // *****************************************************************************
 // class DateTimePatternGenerator
 // *****************************************************************************
@@ -206,6 +208,8 @@ static const dtTypeElem dtTypes[] = {
     {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[]
  };
 
@@ -219,10 +223,6 @@ static const char* const CLDR_FIELD_NAME[] = {
     "hour", "minute", "second", "*", "zone"
 };
 
-static const char* const Resource_Fields[] = {
-    "day", "dayperiod", "era", "hour", "minute", "month", "second", "week",
-    "weekday", "year", "zone", "quarter" };
-
 // For appendItems
 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
@@ -248,15 +248,12 @@ DateTimePatternGenerator::createInstance(UErrorCode& status) {
 
 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
@@ -315,6 +312,10 @@ DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerato
 
 DateTimePatternGenerator&
 DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) {
+    // reflexive case
+    if (&other == this) {
+        return *this;
+    }
     pLocale = other.pLocale;
     fDefaultHourFormatChar = other.fDefaultHourFormatChar;
     *fp = *(other.fp);
@@ -383,72 +384,249 @@ DateTimePatternGenerator::~DateTimePatternGenerator() {
     if (skipMatcher != NULL) delete skipMatcher;
 }
 
+namespace {
+
+UInitOnce initOnce = U_INITONCE_INITIALIZER;
+UHashtable *localeToAllowedHourFormatsMap = NULL;
+
+// Value deleter for hashmap.
+U_CFUNC void U_CALLCONV deleteAllowedHourFormats(void *ptr) {
+    uprv_free(ptr);
+}
+
+// Close hashmap at cleanup.
+U_CFUNC UBool U_CALLCONV 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
 
     skipMatcher = NULL;
     fAvailableFormatKeyHash=NULL;
-    addCanonicalItems();
+    addCanonicalItems(status);
     addICUPatterns(locale, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
     addCLDRData(locale, status);
     setDateTimeFromCalendar(locale, status);
     setDecimalSymbols(locale, status);
+    umtx_initOnce(initOnce, loadAllowedHourFormatsData, status);
+    getAllowedHourFormats(locale, status);
 } // DateTimePatternGenerator::initData
 
+namespace {
+
+struct AllowedHourFormatsSink : public ResourceSink {
+    // Initialize sub-sinks.
+    AllowedHourFormatsSink() {}
+    virtual ~AllowedHourFormatsSink();
+
+    virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
+                     UErrorCode &errorCode) {
+        ResourceTable timeData = value.getTable(errorCode);
+        if (U_FAILURE(errorCode)) { return; }
+        for (int32_t i = 0; timeData.getKeyAndValue(i, key, value); ++i) {
+            const char *regionOrLocale = key;
+            ResourceTable formatList = value.getTable(errorCode);
+            if (U_FAILURE(errorCode)) { return; }
+            for (int32_t j = 0; formatList.getKeyAndValue(j, key, value); ++j) {
+                if (uprv_strcmp(key, "allowed") == 0) {  // Ignore "preferred" list.
+                    LocalMemory<int32_t> list;
+                    int32_t length;
+                    if (value.getType() == URES_STRING) {
+                        if (list.allocateInsteadAndReset(2) == NULL) {
+                            errorCode = U_MEMORY_ALLOCATION_ERROR;
+                            return;
+                        }
+                        list[0] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
+                        length = 1;
+                    }
+                    else {
+                        ResourceArray allowedFormats = value.getArray(errorCode);
+                        length = allowedFormats.getSize();
+                        if (list.allocateInsteadAndReset(length + 1) == NULL) {
+                            errorCode = U_MEMORY_ALLOCATION_ERROR;
+                            return;
+                        }
+                        for (int32_t k = 0; k < length; ++k) {
+                            allowedFormats.getValue(k, value);
+                            list[k] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
+                        }
+                    }
+                    list[length] = ALLOWED_HOUR_FORMAT_UNKNOWN;
+                    uhash_put(localeToAllowedHourFormatsMap,
+                              const_cast<char *>(regionOrLocale), list.orphan(), &errorCode);
+                    if (U_FAILURE(errorCode)) { return; }
+                }
+            }
+        }
+    }
+
+    AllowedHourFormat getHourFormatFromUnicodeString(const 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() {}
+
+U_CFUNC void U_CALLCONV 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_getAllItemsWithFallback(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
 DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& status) {
+    if (U_FAILURE(status)) { return; }
     UnicodeString dfPattern;
     UnicodeString conflictingString;
     DateFormat* df;
 
-    if (U_FAILURE(status)) {
-        return;
-    }
-
     // Load with ICU patterns
     for (int32_t i=DateFormat::kFull; i<=DateFormat::kShort; i++) {
         DateFormat::EStyle style = (DateFormat::EStyle)i;
         df = DateFormat::createDateInstance(style, locale);
         SimpleDateFormat* sdf;
         if (df != NULL && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != NULL) {
-            addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status);
+            sdf->toPattern(dfPattern);
+            addPattern(dfPattern, FALSE, conflictingString, status);
         }
         // TODO Maybe we should return an error when the date format isn't simple.
         delete df;
-        if (U_FAILURE(status)) {
-            return;
-        }
+        if (U_FAILURE(status)) { return; }
 
         df = DateFormat::createTimeInstance(style, locale);
         if (df != NULL && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != NULL) {
-            addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status);
-            // HACK for hh:ss
-            if ( i==DateFormat::kMedium ) {
-                hackPattern = dfPattern;
+            sdf->toPattern(dfPattern);
+            addPattern(dfPattern, FALSE, conflictingString, status);
+
+            // TODO: C++ and Java are inconsistent (see #12568).
+            // C++ uses MEDIUM, but Java uses SHORT.
+            if ( i==DateFormat::kShort && !dfPattern.isEmpty() ) {
+                consumeShortTimePattern(dfPattern, status);
             }
         }
         // TODO Maybe we should return an error when the date format isn't simple.
         delete df;
-        if (U_FAILURE(status)) {
-            return;
-        }
+        if (U_FAILURE(status)) { return; }
     }
 }
 
@@ -503,221 +681,223 @@ DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode
 static const UChar hourFormatChars[] = { CAP_H, LOW_H, CAP_K, LOW_K, 0 }; // HhKk, the hour format characters
 
 void
-DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& err) {
-    UResourceBundle *rb, *calTypeBundle, *calBundle;
-    UResourceBundle *patBundle, *fieldBundle, *fBundle;
-    UnicodeString rbPattern, value, field;
-    UnicodeString conflictingPattern;
-    const char *key=NULL;
-    int32_t i;
-
-    UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, 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;
-        if (i<10) {
-            appendItemNames[i]+=(UChar)(i+0x30);
-        }
-        else {
-            appendItemNames[i]+=(UChar)0x31;
-            appendItemNames[i]+=(UChar)(i-10 + 0x30);
-        }
-        // NUL-terminate for the C API.
-        appendItemNames[i].getTerminatedBuffer();
-    }
-
-    rb = ures_open(NULL, locale.getName(), &err);
-    if (rb == NULL || U_FAILURE(err)) {
-        return;
-    }
-    const char *curLocaleName=ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &err);
-    const char * calendarTypeToUse = DT_DateTimeGregorianTag; // initial default
-    char         calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well
+DateTimePatternGenerator::getCalendarTypeToUse(const Locale& locale, CharString& destination, UErrorCode& err) {
+    destination.clear().append(DT_DateTimeGregorianTag, -1, err); // initial default
     if ( U_SUCCESS(err) ) {
-        char    localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY];
+        char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY];
         // obtain a locale that always has the calendar key value that should be used
-        (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, NULL,
-                                            "calendar", "calendar", locale.getName(), NULL, FALSE, &err);
+        ures_getFunctionalEquivalent(
+            localeWithCalendarKey,
+            ULOC_LOCALE_IDENTIFIER_CAPACITY,
+            NULL,
+            "calendar",
+            "calendar",
+            locale.getName(),
+            NULL,
+            FALSE,
+            &err);
         localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination
         // now get the calendar key value from that locale
-        int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType, ULOC_KEYWORDS_CAPACITY, &err);
+        char calendarType[ULOC_KEYWORDS_CAPACITY];
+        int32_t calendarTypeLen = uloc_getKeywordValue(
+            localeWithCalendarKey,
+            "calendar",
+            calendarType,
+            ULOC_KEYWORDS_CAPACITY,
+            &err);
         if (U_SUCCESS(err) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) {
-            calendarTypeToUse = calendarType;
+            destination.clear().append(calendarType, -1, err);
+            if (U_FAILURE(err)) { return; }
         }
         err = U_ZERO_ERROR;
     }
-    calBundle = ures_getByKeyWithFallback(rb, DT_DateTimeCalendarTag, NULL, &err);
-    calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &err);
+}
 
-    key=NULL;
-    int32_t dtCount=0;
-    patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimePatternsTag, NULL, &err);
-    while (U_SUCCESS(err)) {
-        rbPattern = ures_getNextUnicodeString(patBundle, &key, &err);
-        dtCount++;
-        if (rbPattern.length()==0 ) {
-            break;  // no more pattern
-        }
-        else {
-            if (dtCount==9) {
-                setDateTimeFormat(rbPattern);
-            } else if (dtCount==4) { // short time format
-                // set fDefaultHourFormatChar to the hour format character from this pattern
-                int32_t tfIdx, tfLen = rbPattern.length();
-                UBool ignoreChars = FALSE;
-                for (tfIdx = 0; tfIdx < tfLen; tfIdx++) {
-                    UChar tfChar = rbPattern.charAt(tfIdx);
-                    if ( tfChar == SINGLE_QUOTE ) {
-                        ignoreChars = !ignoreChars; // toggle (handle quoted literals & '' for single quote)
-                    } else if ( !ignoreChars && u_strchr(hourFormatChars, tfChar) != NULL ) {
-                        fDefaultHourFormatChar = tfChar;
-                        break;
-                    }
-                }
-            }
+void
+DateTimePatternGenerator::consumeShortTimePattern(const UnicodeString& shortTimePattern,
+        UErrorCode& status) {
+
+    // set fDefaultHourFormatChar to the hour format character from this pattern
+    int32_t tfIdx, tfLen = shortTimePattern.length();
+    UBool ignoreChars = FALSE;
+    for (tfIdx = 0; tfIdx < tfLen; tfIdx++) {
+        UChar tfChar = shortTimePattern.charAt(tfIdx);
+        if ( tfChar == SINGLE_QUOTE ) {
+            ignoreChars = !ignoreChars; // toggle (handle quoted literals & '' for single quote)
+        } else if ( !ignoreChars && u_strchr(hourFormatChars, tfChar) != NULL ) {
+            fDefaultHourFormatChar = tfChar;
+            break;
         }
     }
-    ures_close(patBundle);
 
-    err = U_ZERO_ERROR;
-    patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimeAppendItemsTag, NULL, &err);
-    key=NULL;
-    UnicodeString itemKey;
-    while (U_SUCCESS(err)) {
-        rbPattern = ures_getNextUnicodeString(patBundle, &key, &err);
-        if (rbPattern.length()==0 ) {
-            break;  // no more pattern
-        }
-        else {
-            setAppendItemFormat(getAppendFormatNumber(key), rbPattern);
+    // HACK for hh:ss
+    hackTimes(shortTimePattern, status);
+}
+
+struct DateTimePatternGenerator::AppendItemFormatsSink : public ResourceSink {
+
+    // Destination for data, modified via setters.
+    DateTimePatternGenerator& dtpg;
+
+    AppendItemFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {}
+    virtual ~AppendItemFormatsSink();
+
+    virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
+            UErrorCode &errorCode) {
+        ResourceTable itemsTable = value.getTable(errorCode);
+        if (U_FAILURE(errorCode)) { return; }
+        for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
+            UDateTimePatternField field = dtpg.getAppendFormatNumber(key);
+            if (field == UDATPG_FIELD_COUNT) { continue; }
+            const UnicodeString& valueStr = value.getUnicodeString(errorCode);
+            if (dtpg.getAppendItemFormat(field).isEmpty() && !valueStr.isEmpty()) {
+                dtpg.setAppendItemFormat(field, valueStr);
+            }
         }
     }
-    ures_close(patBundle);
 
-    key=NULL;
-    err = U_ZERO_ERROR;
-    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);
-        fieldBundle = ures_getByKeyWithFallback(patBundle, "dn", NULL, &err);
-        rbPattern = ures_getNextUnicodeString(fieldBundle, &key, &err);
-        ures_close(fieldBundle);
-        ures_close(patBundle);
-        if (rbPattern.length()==0 ) {
-            continue;
-        }
-        else {
-            setAppendItemName(getAppendNameNumber(Resource_Fields[i]), rbPattern);
+    void fillInMissing() {
+        UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, UPRV_LENGTHOF(UDATPG_ItemFormat)-1);  // Read-only alias.
+        for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) {
+            UDateTimePatternField field = (UDateTimePatternField)i;
+            if (dtpg.getAppendItemFormat(field).isEmpty()) {
+                dtpg.setAppendItemFormat(field, defaultItemFormat);
+            }
         }
     }
-    ures_close(fBundle);
+};
 
-    // add available formats
-    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,
-        //   whose actual name is in curLocaleName.
-        // - if U_SUCCESS(err), then calBundle and calTypeBundle are open;
-        //   process contents of calTypeBundle, then close calBundle and calTypeBundle.
-        if (U_SUCCESS(err)) {
-            // process contents of calTypeBundle
-            patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimeAvailableFormatsTag, NULL, &err);
-            if (U_SUCCESS(err)) {
-                int32_t numberKeys = ures_getSize(patBundle);
-                int32_t len;
-                const UChar *retPattern;
-                key=NULL;
-#if defined(U_USE_ASCII_BUNDLE_ITERATOR)
-                UResourceBundleAIterator aiter;
-                ures_a_open(&aiter, patBundle, &err);
-#endif
-                for(i=0; i<numberKeys; ++i) {
-#if defined(U_USE_ASCII_BUNDLE_ITERATOR)
-                    retPattern=ures_a_getNextString(&aiter, &len, &key, &err);
-#else
-                    retPattern=ures_getNextString(patBundle, &len, &key, &err);
-#endif
-                    UnicodeString format=UnicodeString(retPattern);
-                    UnicodeString retKey=UnicodeString(key, -1, US_INV);
-                    if ( firstTimeThrough || !isAvailableFormatSet(retKey) ) {
-                        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, override, conflictingPattern, err);
-                    }
+struct DateTimePatternGenerator::AppendItemNamesSink : public ResourceSink {
+
+    // Destination for data, modified via setters.
+    DateTimePatternGenerator& dtpg;
+
+    AppendItemNamesSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {}
+    virtual ~AppendItemNamesSink();
+
+    virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
+            UErrorCode &errorCode) {
+        ResourceTable itemsTable = value.getTable(errorCode);
+        if (U_FAILURE(errorCode)) { return; }
+        for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
+            UDateTimePatternField field = dtpg.getAppendNameNumber(key);
+            if (field == UDATPG_FIELD_COUNT) { continue; }
+            ResourceTable detailsTable = value.getTable(errorCode);
+            if (U_FAILURE(errorCode)) { return; }
+            for (int32_t j = 0; detailsTable.getKeyAndValue(j, key, value); ++j) {
+                if (uprv_strcmp(key, "dn") != 0) { continue; }
+                const UnicodeString& valueStr = value.getUnicodeString(errorCode);
+                if (dtpg.getAppendItemName(field).isEmpty() && !valueStr.isEmpty()) {
+                    dtpg.setAppendItemName(field, valueStr);
                 }
-#if defined(U_USE_ASCII_BUNDLE_ITERATOR)
-                ures_a_close(&aiter);
-#endif
-                ures_close(patBundle);
+                break;
             }
-            firstTimeThrough = FALSE;
-            // close calBundle and calTypeBundle
-            ures_close(calTypeBundle);
-            ures_close(calBundle);
-        }
-        if (uprv_strcmp(curLocaleName,"root")==0 || uprv_strlen(curLocaleName)==0) {
-            // we just finished handling root, nothing more to check
-            ures_close(rb);
-            break;
         }
-        // Find the name of the appropriate parent locale (from %%Parent if present, else
-        // uloc_getParent on the actual locale name)
-        // (It would be nice to have a ures function that did this...)
-        err = U_ZERO_ERROR;
-        char parentLocale[ULOC_FULLNAME_CAPACITY];
-        int32_t locNameLen;
-        const UChar * parentUName = ures_getStringByKey(rb, "%%Parent", &locNameLen, &err);
-        if (U_SUCCESS(err) && err != U_USING_FALLBACK_WARNING && locNameLen < ULOC_FULLNAME_CAPACITY) {
-            u_UCharsToChars(parentUName, parentLocale, locNameLen + 1);
-        } else {
-            err = U_ZERO_ERROR;
-            uloc_getParent(curLocaleName, parentLocale, ULOC_FULLNAME_CAPACITY, &err);
-            if (U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) {
-                // just fallback to root, since we are not already there
-                parentLocale[0] = 0;
-                err = U_ZERO_ERROR;
+    }
+
+    void fillInMissing() {
+        for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) {
+            UDateTimePatternField field = (UDateTimePatternField)i;
+            UnicodeString& valueStr = dtpg.getMutableAppendItemName(field);
+            if (valueStr.isEmpty()) {
+                valueStr = CAP_F;
+                U_ASSERT(i < 20);
+                if (i < 10) {
+                    // F0, F1, ..., F9
+                    valueStr += (UChar)(i+0x30);
+                } else {
+                    // F10, F11, ...
+                    valueStr += (UChar)0x31;
+                    valueStr += (UChar)(i-10 + 0x30);
+                }
+                // NUL-terminate for the C API.
+                valueStr.getTerminatedBuffer();
             }
         }
-        // Close current locale bundle
-        ures_close(rb);
-        // And open its parent, which becomes the new current locale being processed
-        rb = ures_open(NULL, parentLocale, &err);
-        if ( U_FAILURE(err) ) {
-            err = U_ZERO_ERROR;
-            break;
-        }
-        // Get the name of the parent / new current locale
-        curLocaleName=ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &err);
-        if ( U_FAILURE(err) ) {
-            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)) {
-            calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &err);
-            if ( U_FAILURE(err) ) {
-                ures_close(calBundle);
+    }
+};
+
+struct DateTimePatternGenerator::AvailableFormatsSink : public ResourceSink {
+
+    // Destination for data, modified via setters.
+    DateTimePatternGenerator& dtpg;
+
+    // Temporary variable, required for calling addPatternWithSkeleton.
+    UnicodeString conflictingPattern;
+
+    AvailableFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {}
+    virtual ~AvailableFormatsSink();
+
+    virtual void put(const char *key, ResourceValue &value, UBool isRoot,
+            UErrorCode &errorCode) {
+        ResourceTable itemsTable = value.getTable(errorCode);
+        if (U_FAILURE(errorCode)) { return; }
+        for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
+            const UnicodeString formatKey(key, -1, US_INV);
+            if (!dtpg.isAvailableFormatSet(formatKey) ) {
+                dtpg.setAvailableFormat(formatKey, errorCode);
+                // Add pattern with its associated skeleton. Override any duplicate
+                // derived from std patterns, but not a previous availableFormats entry:
+                const UnicodeString& formatValue = value.getUnicodeString(errorCode);
+                conflictingPattern.remove();
+                dtpg.addPatternWithSkeleton(formatValue, &formatKey, !isRoot, conflictingPattern, errorCode);
             }
         }
-        // Go to the top of the loop to process contents of calTypeBundle
-    }    
-
-    if (hackPattern.length()>0) {
-        hackTimes(hackPattern, err);
     }
+};
+
+// Virtual destructors must be defined out of line.
+DateTimePatternGenerator::AppendItemFormatsSink::~AppendItemFormatsSink() {}
+DateTimePatternGenerator::AppendItemNamesSink::~AppendItemNamesSink() {}
+DateTimePatternGenerator::AvailableFormatsSink::~AvailableFormatsSink() {}
+
+void
+DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& errorCode) {
+    if (U_FAILURE(errorCode)) { return; }
+    UnicodeString rbPattern, value, field;
+    CharString path;
+
+    LocalUResourceBundlePointer rb(ures_open(NULL, locale.getName(), &errorCode));
+    if (U_FAILURE(errorCode)) { return; }
+
+    CharString calendarTypeToUse; // to be filled in with the type to use, if all goes well
+    getCalendarTypeToUse(locale, calendarTypeToUse, errorCode);
+    if (U_FAILURE(errorCode)) { return; }
+
+    // Local err to ignore resource not found exceptions
+    UErrorCode err = U_ZERO_ERROR;
+
+    // Load append item formats.
+    AppendItemFormatsSink appendItemFormatsSink(*this);
+    path.clear()
+        .append(DT_DateTimeCalendarTag, errorCode)
+        .append('/', errorCode)
+        .append(calendarTypeToUse, errorCode)
+        .append('/', errorCode)
+        .append(DT_DateTimeAppendItemsTag, errorCode); // i.e., calendar/xxx/appendItems
+    if (U_FAILURE(errorCode)) { return; }
+    ures_getAllItemsWithFallback(rb.getAlias(), path.data(), appendItemFormatsSink, err);
+    appendItemFormatsSink.fillInMissing();
+
+    // Load CLDR item names.
+    err = U_ZERO_ERROR;
+    AppendItemNamesSink appendItemNamesSink(*this);
+    ures_getAllItemsWithFallback(rb.getAlias(), DT_DateTimeFieldsTag, appendItemNamesSink, err);
+    appendItemNamesSink.fillInMissing();
+
+    // Load the available formats from CLDR.
+    err = U_ZERO_ERROR;
+    initHashtable(errorCode);
+    if (U_FAILURE(errorCode)) { return; }
+    AvailableFormatsSink availableFormatsSink(*this);
+    path.clear()
+        .append(DT_DateTimeCalendarTag, errorCode)
+        .append('/', errorCode)
+        .append(calendarTypeToUse, errorCode)
+        .append('/', errorCode)
+        .append(DT_DateTimeAvailableFormatsTag, errorCode); // i.e., calendar/xxx/availableFormats
+    if (U_FAILURE(errorCode)) { return; }
+    ures_getAllItemsWithFallback(rb.getAlias(), path.data(), availableFormatsSink, err);
 }
 
 void
@@ -731,7 +911,6 @@ DateTimePatternGenerator::initHashtable(UErrorCode& err) {
     }
 }
 
-
 void
 DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field, const UnicodeString& value) {
     appendItemFormats[field] = value;
@@ -752,7 +931,12 @@ DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field, const U
 }
 
 const UnicodeString&
-DateTimePatternGenerator:: getAppendItemName(UDateTimePatternField field) const {
+DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field) const {
+    return appendItemNames[field];
+}
+
+UnicodeString&
+DateTimePatternGenerator::getMutableAppendItemName(UDateTimePatternField field) {
     return appendItemNames[field];
 }
 
@@ -778,7 +962,7 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate
     int32_t dateMask=(1<<UDATPG_DAYPERIOD_FIELD) - 1;
     int32_t timeMask=(1<<UDATPG_FIELD_COUNT) - 1 - dateMask;
 
-    // Replace hour metacharacters 'j' and 'J', set flags as necessary
+    // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
     UnicodeString patternFormCopy = UnicodeString(patternForm);
     UChar hourFormatSkeletonCharForLowJ = fDefaultHourFormatChar;
     switch (options & UADATPG_FORCE_HOUR_CYCLE_MASK) {
@@ -795,6 +979,26 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate
         } 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.
@@ -830,8 +1034,7 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate
     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;
 }
 
@@ -865,12 +1068,15 @@ DateTimePatternGenerator::getDecimal() const {
 }
 
 void
-DateTimePatternGenerator::addCanonicalItems() {
+DateTimePatternGenerator::addCanonicalItems(UErrorCode& status) {
+    if (U_FAILURE(status)) { return; }
     UnicodeString  conflictingPattern;
-    UErrorCode status = U_ZERO_ERROR;
 
     for (int32_t i=0; i<UDATPG_FIELD_COUNT; i++) {
-        addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status);
+        if (Canonical_Items[i] > 0) {
+            addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status);
+        }
+        if (U_FAILURE(status)) { return; }
     }
 }
 
@@ -892,16 +1098,35 @@ DateTimePatternGenerator::setDateTimeFromCalendar(const Locale& locale, UErrorCo
     int32_t resStrLen = 0;
 
     Calendar* fCalendar = Calendar::createInstance(locale, status);
-    CalendarData calData(locale, fCalendar?fCalendar->getType():NULL, status);
-    UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, status);
-    if (U_FAILURE(status)) return;
+    if (U_FAILURE(status)) { return; }
+
+    LocalUResourceBundlePointer calData(ures_open(NULL, locale.getBaseName(), &status));
+    ures_getByKey(calData.getAlias(), DT_DateTimeCalendarTag, calData.getAlias(), &status);
+
+    LocalUResourceBundlePointer dateTimePatterns;
+    if (fCalendar != NULL && fCalendar->getType() != NULL && *fCalendar->getType() != '\0'
+            && uprv_strcmp(fCalendar->getType(), DT_DateTimeGregorianTag) != 0) {
+        dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), fCalendar->getType(),
+                                                                NULL, &status));
+        ures_getByKeyWithFallback(dateTimePatterns.getAlias(), DT_DateTimePatternsTag,
+                                  dateTimePatterns.getAlias(), &status);
+    }
+
+    if (dateTimePatterns.isNull() || status == U_MISSING_RESOURCE_ERROR) {
+        status = U_ZERO_ERROR;
+        dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), DT_DateTimeGregorianTag,
+                                                                dateTimePatterns.orphan(), &status));
+        ures_getByKeyWithFallback(dateTimePatterns.getAlias(), DT_DateTimePatternsTag,
+                                  dateTimePatterns.getAlias(), &status);
+    }
+    if (U_FAILURE(status)) { return; }
 
-    if (ures_getSize(dateTimePatterns) <= DateFormat::kDateTime)
+    if (ures_getSize(dateTimePatterns.getAlias()) <= DateFormat::kDateTime)
     {
         status = U_INVALID_FORMAT_ERROR;
         return;
     }
-    resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)DateFormat::kDateTime, &resStrLen, &status);
+    resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), (int32_t)DateFormat::kDateTime, &resStrLen, &status);
     setDateTimeFormat(UnicodeString(TRUE, resStr, resStrLen));
 
     delete fCalendar;
@@ -1079,9 +1304,22 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
             }
             const dtTypeElem *row = &dtTypes[canonicalIndex];
             int32_t typeValue = row->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;
+                field += decimal;
+                dtMatcher->skeleton.original.appendFieldTo(UDATPG_FRACTIONAL_SECOND_FIELD, field);
             } else if (dtMatcher->skeleton.type[typeValue]!=0) {
                     // Here:
                     // - "reqField" is the field from the originally requested skeleton, with length
@@ -1105,9 +1343,9 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
                     //    a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen.
                     //    b) The pattern field is numeric and the skeleton field is not, or vice versa.
 
-                    UnicodeString reqField = dtMatcher->skeleton.original[typeValue];
-                    int32_t reqFieldLen = reqField.length();
-                    if (reqField.charAt(0) == CAP_E && reqFieldLen < 3)
+                    UChar reqFieldChar = dtMatcher->skeleton.original.getFieldChar(typeValue);
+                    int32_t reqFieldLen = dtMatcher->skeleton.original.getFieldLength(typeValue);
+                    if (reqFieldChar == CAP_E && reqFieldLen < 3)
                         reqFieldLen = 3; // 1-3 for E are equivalent to 3 for c,e
                     int32_t adjFieldLen = reqFieldLen;
                     if ( (typeValue==UDATPG_HOUR_FIELD && (options & UDATPG_MATCH_HOUR_FIELD_LENGTH)==0) ||
@@ -1115,8 +1353,7 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
                          (typeValue==UDATPG_SECOND_FIELD && (options & UDATPG_MATCH_SECOND_FIELD_LENGTH)==0) ) {
                          adjFieldLen = field.length();
                     } else if (specifiedSkeleton) {
-                        UnicodeString skelField = specifiedSkeleton->original[typeValue];
-                        int32_t skelFieldLen = skelField.length();
+                        int32_t skelFieldLen = specifiedSkeleton->original.getFieldLength(typeValue);
                         UBool patFieldIsNumeric = (row->type > 0);
                         UBool skelFieldIsNumeric = (specifiedSkeleton->type[typeValue] > 0);
                         if (skelFieldLen == reqFieldLen || (patFieldIsNumeric && !skelFieldIsNumeric) || (skelFieldIsNumeric && !patFieldIsNumeric)) {
@@ -1124,9 +1361,12 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
                             adjFieldLen = field.length();
                         }
                     }
-                    UChar c = (typeValue!= UDATPG_HOUR_FIELD && typeValue!= UDATPG_MONTH_FIELD &&
-                               typeValue!= UDATPG_WEEKDAY_FIELD && (typeValue!= UDATPG_YEAR_FIELD || reqField.charAt(0)==CAP_Y))?
-                        reqField.charAt(0): field.charAt(0);
+                    UChar c = (typeValue!= UDATPG_HOUR_FIELD
+                            && typeValue!= UDATPG_MONTH_FIELD
+                            && typeValue!= UDATPG_WEEKDAY_FIELD
+                            && (typeValue!= UDATPG_YEAR_FIELD || reqFieldChar==CAP_Y))
+                            ? reqFieldChar
+                            : field.charAt(0);
                     if (typeValue == UDATPG_HOUR_FIELD && (flags & kDTPGSkeletonUsesCapJ) != 0) {
                         c = fDefaultHourFormatChar;
                         switch (options & UADATPG_FORCE_HOUR_CYCLE_MASK) {
@@ -1189,13 +1429,13 @@ DateTimePatternGenerator::getBestAppending(int32_t missingFields, int32_t flags,
             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;
         }
     }
@@ -1244,7 +1484,7 @@ DateTimePatternGenerator::copyHashtable(Hashtable *other, UErrorCode &status) {
     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){
@@ -1360,7 +1600,7 @@ PatternMap::copyFrom(const PatternMap& other, UErrorCode& status) {
                 status = U_MEMORY_ALLOCATION_ERROR;
                 return;
             }
-
+            curElem->skeletonWasSpecified = otherElem->skeletonWasSpecified;
             if (prevElem!=NULL) {
                 prevElem->next=curElem;
             }
@@ -1507,36 +1747,19 @@ PatternMap::getPatternFromSkeleton(PtnSkeleton& skeleton, const PtnSkeleton** sp
    }
 
    // find boot entry
-   UChar baseChar='\0';
-   for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) {
-       if (skeleton.baseOriginal[i].length() !=0 ) {
-           baseChar = skeleton.baseOriginal[i].charAt(0);
-           break;
-       }
-   }
-
+   UChar baseChar = skeleton.getFirstChar();
    if ((curElem=getHeader(baseChar))==NULL) {
        return NULL;  // no match
    }
 
    do  {
-       int32_t i=0;
+       UBool equal;
        if (specifiedSkeletonPtr != NULL) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original
-           for (i=0; i<UDATPG_FIELD_COUNT; ++i) {
-               if (curElem->skeleton->original[i].compare(skeleton.original[i]) != 0 )
-               {
-                   break;
-               }
-           }
+           equal = curElem->skeleton->original == skeleton.original;
        } else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal
-           for (i=0; i<UDATPG_FIELD_COUNT; ++i) {
-               if (curElem->skeleton->baseOriginal[i].compare(skeleton.baseOriginal[i]) != 0 )
-               {
-                   break;
-               }
-           }
+           equal = curElem->skeleton->baseOriginal == skeleton.baseOriginal;
        }
-       if (i == UDATPG_FIELD_COUNT) {
+       if (equal) {
            if (specifiedSkeletonPtr && curElem->skeletonWasSpecified) {
                *specifiedSkeletonPtr = curElem->skeleton;
            }
@@ -1640,37 +1863,35 @@ void
 DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton& skeletonResult) {
     int32_t i;
     for (i=0; i<UDATPG_FIELD_COUNT; ++i) {
-        skeletonResult.type[i]=NONE;
+        skeletonResult.type[i] = NONE;
     }
     fp->set(pattern);
     for (i=0; i < fp->itemNumber; i++) {
-        UnicodeString field = fp->items[i];
-        if ( field.charAt(0) == LOW_A ) {
+        const UnicodeString& value = fp->items[i];
+        if ( value.charAt(0) == LOW_A ) {
             continue;  // skip 'a'
         }
 
-        if ( fp->isQuoteLiteral(field) ) {
+        if ( fp->isQuoteLiteral(value) ) {
             UnicodeString quoteLiteral;
             fp->getQuoteLiteral(quoteLiteral, &i);
             continue;
         }
-        int32_t canonicalIndex = fp->getCanonicalIndex(field);
+        int32_t canonicalIndex = fp->getCanonicalIndex(value);
         if (canonicalIndex < 0 ) {
             continue;
         }
         const dtTypeElem *row = &dtTypes[canonicalIndex];
-        int32_t typeValue = row->field;
-        skeletonResult.original[typeValue]=field;
+        int32_t field = row->field;
+        skeletonResult.original.populate(field, value);
         UChar repeatChar = row->patternChar;
         int32_t repeatCount = row->minLen; // #7930 removes cap at 3
-        while (repeatCount-- > 0) {
-            skeletonResult.baseOriginal[typeValue] += repeatChar;
-        }
-        int16_t subTypeValue = row->type;
+        skeletonResult.baseOriginal.populate(field, repeatChar, repeatCount);
+        int16_t subField = row->type;
         if ( row->type > 0) {
-            subTypeValue += field.length();
+            subField += value.length();
         }
-        skeletonResult.type[typeValue] = subTypeValue;
+        skeletonResult.type[field] = subField;
     }
     copyFrom(skeletonResult);
 }
@@ -1678,23 +1899,13 @@ DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton
 void
 DateTimeMatcher::getBasePattern(UnicodeString &result ) {
     result.remove(); // Reset the result first.
-    for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) {
-        if (skeleton.baseOriginal[i].length()!=0) {
-            result += skeleton.baseOriginal[i];
-        }
-    }
+    skeleton.baseOriginal.appendTo(result);
 }
 
 UnicodeString
 DateTimeMatcher::getPattern() {
     UnicodeString result;
-
-    for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) {
-        if (skeleton.original[i].length()!=0) {
-            result += skeleton.original[i];
-        }
-    }
-    return result;
+    return skeleton.original.appendTo(result);
 }
 
 int32_t
@@ -1727,34 +1938,19 @@ DateTimeMatcher::getDistance(const DateTimeMatcher& other, int32_t includeMask,
 
 void
 DateTimeMatcher::copyFrom(const PtnSkeleton& newSkeleton) {
-    for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) {
-        this->skeleton.type[i]=newSkeleton.type[i];
-        this->skeleton.original[i]=newSkeleton.original[i];
-        this->skeleton.baseOriginal[i]=newSkeleton.baseOriginal[i];
-    }
+    skeleton.copyFrom(newSkeleton);
 }
 
 void
 DateTimeMatcher::copyFrom() {
     // same as clear
-    for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) {
-        this->skeleton.type[i]=0;
-        this->skeleton.original[i].remove();
-        this->skeleton.baseOriginal[i].remove();
-    }
+    skeleton.clear();
 }
 
 UBool
 DateTimeMatcher::equals(const DateTimeMatcher* other) const {
-    if (other==NULL) {
-        return FALSE;
-    }
-    for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) {
-        if (this->skeleton.original[i]!=other->skeleton.original[i] ) {
-            return FALSE;
-        }
-    }
-    return TRUE;
+    if (other==NULL) { return FALSE; }
+    return skeleton.original == other->skeleton.original;
 }
 
 int32_t
@@ -1848,7 +2044,7 @@ FormatParser::getCanonicalIndex(const UnicodeString& s, UBool strict) {
     }
     int32_t i = 0;
     int32_t bestRow = -1;
-    while (dtTypes[i].patternChar != '\0') {
+    while (dtTypes[i].patternChar != 0x0000) {
         if ( dtTypes[i].patternChar != ch ) {
             ++i;
             continue;
@@ -1867,7 +2063,7 @@ FormatParser::getCanonicalIndex(const UnicodeString& s, UBool strict) {
 }
 
 UBool
-FormatParser::isQuoteLiteral(const UnicodeString& s) const {
+FormatParser::isQuoteLiteral(const UnicodeString& s) {
     return (UBool)(s.charAt(0)==SINGLE_QUOTE);
 }
 
@@ -2020,52 +2216,116 @@ PatternMapIterator::next() {
     return *matcher;
 }
 
-PtnSkeleton::PtnSkeleton() {
+
+SkeletonFields::SkeletonFields() {
+    // Set initial values to zero
+    clear();
 }
 
+void SkeletonFields::clear() {
+    uprv_memset(chars, 0, sizeof(chars));
+    uprv_memset(lengths, 0, sizeof(lengths));
+}
 
-PtnSkeleton::PtnSkeleton(const PtnSkeleton& other) {
-    for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) {
-        this->type[i]=other.type[i];
-        this->original[i]=other.original[i];
-        this->baseOriginal[i]=other.baseOriginal[i];
+void SkeletonFields::copyFrom(const SkeletonFields& other) {
+    uprv_memcpy(chars, other.chars, sizeof(chars));
+    uprv_memcpy(lengths, other.lengths, sizeof(lengths));
+}
+
+void SkeletonFields::clearField(int32_t field) {
+    chars[field] = 0;
+    lengths[field] = 0;
+}
+
+UChar SkeletonFields::getFieldChar(int32_t field) const {
+    return chars[field];
+}
+
+int32_t SkeletonFields::getFieldLength(int32_t field) const {
+    return lengths[field];
+}
+
+void SkeletonFields::populate(int32_t field, const UnicodeString& value) {
+    populate(field, value.charAt(0), value.length());
+}
+
+void SkeletonFields::populate(int32_t field, UChar ch, int32_t length) {
+    chars[field] = (int8_t) ch;
+    lengths[field] = (int8_t) length;
+}
+
+UBool SkeletonFields::isFieldEmpty(int32_t field) const {
+    return lengths[field] == 0;
+}
+
+UnicodeString& SkeletonFields::appendTo(UnicodeString& string) const {
+    for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) {
+        appendFieldTo(i, string);
     }
+    return string;
 }
 
-UBool
-PtnSkeleton::equals(const PtnSkeleton& other)  {
-    for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) {
-        if ( (type[i]!= other.type[i]) ||
-             (original[i]!=other.original[i]) ||
-             (baseOriginal[i]!=other.baseOriginal[i]) ) {
-            return FALSE;
+UnicodeString& SkeletonFields::appendFieldTo(int32_t field, UnicodeString& string) const {
+    UChar ch(chars[field]);
+    int32_t length = (int32_t) lengths[field];
+
+    for (int32_t i=0; i<length; i++) {
+        string += ch;
+    }
+    return string;
+}
+
+UChar SkeletonFields::getFirstChar() const {
+    for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) {
+        if (lengths[i] != 0) {
+            return chars[i];
         }
     }
-    return TRUE;
+    return '\0';
+}
+
+
+PtnSkeleton::PtnSkeleton() {
+}
+
+PtnSkeleton::PtnSkeleton(const PtnSkeleton& other) {
+    copyFrom(other);
+}
+
+void PtnSkeleton::copyFrom(const PtnSkeleton& other) {
+    uprv_memcpy(type, other.type, sizeof(type));
+    original.copyFrom(other.original);
+    baseOriginal.copyFrom(other.baseOriginal);
+}
+
+void PtnSkeleton::clear() {
+    uprv_memset(type, 0, sizeof(type));
+    original.clear();
+    baseOriginal.clear();
+}
+
+UBool
+PtnSkeleton::equals(const PtnSkeleton& other) const  {
+    return (original == other.original)
+        && (baseOriginal == other.baseOriginal)
+        && (uprv_memcmp(type, other.type, sizeof(type)) == 0);
 }
 
 UnicodeString
-PtnSkeleton::getSkeleton() {
+PtnSkeleton::getSkeleton() const {
     UnicodeString result;
-
-    for(int32_t i=0; i< UDATPG_FIELD_COUNT; ++i) {
-        if (original[i].length()!=0) {
-            result += original[i];
-        }
-    }
-    return result;
+    return original.appendTo(result);
 }
 
 UnicodeString
-PtnSkeleton::getBaseSkeleton() {
+PtnSkeleton::getBaseSkeleton() const {
     UnicodeString result;
+    return baseOriginal.appendTo(result);
+}
 
-    for(int32_t i=0; i< UDATPG_FIELD_COUNT; ++i) {
-        if (baseOriginal[i].length()!=0) {
-            result += baseOriginal[i];
-        }
-    }
-    return result;
+UChar
+PtnSkeleton::getFirstChar() const {
+    return baseOriginal.getFirstChar();
 }
 
 PtnSkeleton::~PtnSkeleton() {