]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/dtptngen.cpp
ICU-57131.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / dtptngen.cpp
index 42fb8f4a3e8381231c8fb303719634d897ca80a8..c0cf9079529708e9f9a1777c215a2873b99bf264 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-* Copyright (C) 2007-2011, International Business Machines Corporation and
+* Copyright (C) 2007-2016, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 *
@@ -16,7 +16,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"
 
 #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
 /**
@@ -66,9 +67,8 @@ struct UResourceBundleAIterator {
 
 /* Must be C linkage to pass function pointer to the sort function */
 
-#if !defined (OS390) && !defined (OS400)
-extern "C"
-#endif
+U_CDECL_BEGIN
+
 static int32_t U_CALLCONV
 ures_a_codepointSort(const void *context, const void *left, const void *right) {
     //CompareContext *cmp=(CompareContext *)context;
@@ -76,6 +76,7 @@ ures_a_codepointSort(const void *context, const void *left, const void *right) {
                     ((const UResAEntry *)right)->key);
 }
 
+U_CDECL_END
 
 static void ures_a_open(UResourceBundleAIterator *aiter, UResourceBundle *bund, UErrorCode *status) {
     if(U_FAILURE(*status)) {
@@ -127,7 +128,6 @@ static const UChar *ures_a_getNextString(UResourceBundleAIterator *aiter, int32_
 
 U_NAMESPACE_BEGIN
 
-
 // *****************************************************************************
 // class DateTimePatternGenerator
 // *****************************************************************************
@@ -144,6 +144,10 @@ static const dtTypeElem dtTypes[] = {
     {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},
     {CAP_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC, 1, 2},
     {CAP_Q, UDATPG_QUARTER_FIELD, DT_SHORT, 3, 0},
     {CAP_Q, UDATPG_QUARTER_FIELD, DT_LONG, 4, 0},
@@ -158,6 +162,7 @@ static const dtTypeElem dtTypes[] = {
     {CAP_L, UDATPG_MONTH_FIELD, DT_SHORT - DT_DELTA, 3, 0},
     {CAP_L, UDATPG_MONTH_FIELD, DT_LONG - DT_DELTA, 4, 0},
     {CAP_L, UDATPG_MONTH_FIELD, DT_NARROW - DT_DELTA, 5, 0},
+    {LOW_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 1},
     {LOW_W, UDATPG_WEEK_OF_YEAR_FIELD, DT_NUMERIC, 1, 2},
     {CAP_W, UDATPG_WEEK_OF_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 0},
     {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORT, 1, 3},
@@ -177,9 +182,9 @@ static const dtTypeElem dtTypes[] = {
     {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},
@@ -188,10 +193,21 @@ static const dtTypeElem dtTypes[] = {
     {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[]
  };
 
@@ -201,7 +217,7 @@ static const char* const CLDR_FIELD_APPEND[] = {
 };
 
 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"
 };
 
@@ -213,7 +229,7 @@ static const char* const Resource_Fields[] = {
 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";
@@ -234,15 +250,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
@@ -301,6 +314,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);
@@ -369,6 +386,34 @@ DateTimePatternGenerator::~DateTimePatternGenerator() {
     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
@@ -383,26 +428,212 @@ DateTimePatternGenerator::initData(const Locale& locale, UErrorCode &status) {
     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
 DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& status) {
     UnicodeString dfPattern;
     UnicodeString conflictingString;
-    UDateTimePatternConflict conflictingStatus;
     DateFormat* df;
 
     if (U_FAILURE(status)) {
@@ -415,7 +646,7 @@ DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& statu
         df = DateFormat::createDateInstance(style, locale);
         SimpleDateFormat* sdf;
         if (df != NULL && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != NULL) {
-            conflictingStatus = addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status);
+            addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status);
         }
         // TODO Maybe we should return an error when the date format isn't simple.
         delete df;
@@ -425,7 +656,7 @@ DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& statu
 
         df = DateFormat::createTimeInstance(style, locale);
         if (df != NULL && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != NULL) {
-            conflictingStatus = addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status);
+            addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status);
             // HACK for hh:ss
             if ( i==DateFormat::kMedium ) {
                 hackPattern = dfPattern;
@@ -441,7 +672,6 @@ DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& statu
 
 void
 DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode& status)  {
-    UDateTimePatternConflict conflictingStatus;
     UnicodeString conflictingString;
 
     fp->set(hackPattern);
@@ -472,7 +702,7 @@ DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode
                             break;
                         }
                         mmss+= field;
-                        conflictingStatus = addPattern(mmss, FALSE, conflictingString, status);
+                        addPattern(mmss, FALSE, conflictingString, status);
                         break;
                     }
                     else {
@@ -496,14 +726,13 @@ DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& err) {
     UResourceBundle *patBundle, *fieldBundle, *fBundle;
     UnicodeString rbPattern, value, field;
     UnicodeString conflictingPattern;
-    UDateTimePatternConflict conflictingStatus;
     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;
@@ -588,7 +817,7 @@ DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& err) {
 
     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);
@@ -606,87 +835,102 @@ DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& err) {
     ures_close(fBundle);
 
     // add available formats
+    UBool firstTimeThrough = TRUE;
     err = U_ZERO_ERROR;
     initHashtable(err);
-    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);
-            setAvailableFormat(retKey, err);
-            // Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
-            // but not a previous availableFormats entry:
-            conflictingStatus = addPatternWithSkeleton(format, &retKey, TRUE, conflictingPattern, err);
-        }
-#if defined(U_USE_ASCII_BUNDLE_ITERATOR)
-        ures_a_close(&aiter);
-#endif
-    }
-    ures_close(patBundle);
-    ures_close(calTypeBundle);
-    ures_close(calBundle);
-    ures_close(rb);
-
-    err = U_ZERO_ERROR;
-    char parentLocale[50];
-    int32_t localeNameLen=0;
-    uprv_strcpy(parentLocale, curLocaleName);
-    while((localeNameLen=uloc_getParent(parentLocale, parentLocale, 50, &err))>=0 ) {
-        rb = ures_open(NULL, parentLocale, &err);
-        curLocaleName=ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &err);
-        uprv_strcpy(parentLocale, curLocaleName);
-        calBundle = ures_getByKey(rb, DT_DateTimeCalendarTag, NULL, &err);
-        calTypeBundle = ures_getByKey(calBundle, calendarTypeToUse, NULL, &err);
-        patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimeAvailableFormatsTag, NULL, &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)) {
-            int32_t numberKeys = ures_getSize(patBundle);
-            int32_t len;
-            const UChar *retPattern;
-            key=NULL;
+            // 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);
+                UResourceBundleAIterator aiter;
+                ures_a_open(&aiter, patBundle, &err);
 #endif
-            for(i=0; i<numberKeys; ++i) {
+                for(i=0; i<numberKeys; ++i) {
 #if defined(U_USE_ASCII_BUNDLE_ITERATOR)
-                retPattern=ures_a_getNextString(&aiter, &len, &key, &err);
+                    retPattern=ures_a_getNextString(&aiter, &len, &key, &err);
 #else
-                retPattern=ures_getNextString(patBundle, &len, &key, &err);
+                    retPattern=ures_getNextString(patBundle, &len, &key, &err);
 #endif
-                UnicodeString format=UnicodeString(retPattern);
-                UnicodeString retKey=UnicodeString(key, -1, US_INV);
-                if ( !isAvailableFormatSet(retKey) ) {
-                    setAvailableFormat(retKey, err);
-                    // Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
-                    // but not a previous availableFormats entry:
-                    conflictingStatus = addPatternWithSkeleton(format, &retKey, TRUE, conflictingPattern, err);
+                    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);
+                    }
                 }
-            }
 #if defined(U_USE_ASCII_BUNDLE_ITERATOR)
-            ures_a_close(&aiter);
+                ures_a_close(&aiter);
 #endif
+                ures_close(patBundle);
+            }
+            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;
         }
-        err = U_ZERO_ERROR; // reset; if this locale lacks the necessary data, need to keep checking up to root.
-        ures_close(patBundle);
-        ures_close(calTypeBundle);
-        ures_close(calBundle);
+        // 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;
+            }
+        }
+        // Close current locale bundle
         ures_close(rb);
-        if (localeNameLen==0) {
+        // 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);
+            }
+        }
+        // Go to the top of the loop to process contents of calTypeBundle
     }
 
     if (hackPattern.length()>0) {
@@ -747,25 +991,69 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate
     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();
@@ -780,8 +1068,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;
 }
 
@@ -798,7 +1085,7 @@ DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern,
                                             UDateTimePatternMatchOptions options,
                                             UErrorCode& /*status*/) {
     dtMatcher->set(skeleton, fp);
-    UnicodeString result = adjustFieldTypes(pattern, NULL, FALSE, options);
+    UnicodeString result = adjustFieldTypes(pattern, NULL, kDTPGNoFlags, options);
     return result;
 }
 
@@ -817,11 +1104,10 @@ DateTimePatternGenerator::getDecimal() const {
 void
 DateTimePatternGenerator::addCanonicalItems() {
     UnicodeString  conflictingPattern;
-    UDateTimePatternConflict conflictingStatus;
     UErrorCode status = U_ZERO_ERROR;
 
     for (int32_t i=0; i<UDATPG_FIELD_COUNT; i++) {
-        conflictingStatus = addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status);
+        addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status);
     }
 }
 
@@ -906,18 +1192,29 @@ DateTimePatternGenerator::addPatternWithSkeleton(
         matcher.set(pattern, fp, skeleton);
         matcher.getBasePattern(basePattern);
     } else {
-        matcher.set(*skeletonToUse, fp, skeleton); // this still trims skeleton fields to max len 3, may need to change it.
+        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 ) {
@@ -995,7 +1292,7 @@ DateTimePatternGenerator::getBestRaw(DateTimeMatcher& source,
 UnicodeString
 DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
                                            const PtnSkeleton* specifiedSkeleton,
-                                           UBool fixFractionalSeconds,
+                                           int32_t flags,
                                            UDateTimePatternMatchOptions options) {
     UnicodeString newPattern;
     fp->set(pattern);
@@ -1019,7 +1316,20 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& 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) {
@@ -1030,8 +1340,8 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
                     //
                     // The adjusted field should consist of characters from the originally requested
                     // skeleton, except in the case of UDATPG_HOUR_FIELD or UDATPG_MONTH_FIELD or
-                    // UDATPG_WEEKDAY_FIELD, in which case it should consist of characters from the
-                    // found pattern.
+                    // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist
+                    // of characters from the  found pattern.
                     //
                     // The length of the adjusted field (adjFieldLen) should match that in the originally
                     // requested skeleton, except that in the following cases the length of the adjusted field
@@ -1064,8 +1374,30 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
                             adjFieldLen = field.length();
                         }
                     }
-                    UChar c = (typeValue!= UDATPG_HOUR_FIELD && typeValue!= UDATPG_MONTH_FIELD && typeValue!= UDATPG_WEEKDAY_FIELD)?
+                    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);
+                    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;
@@ -1078,7 +1410,7 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
 }
 
 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;
@@ -1086,7 +1418,7 @@ DateTimePatternGenerator::getBestAppending(int32_t missingFields, UDateTimePatte
         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;
         }
@@ -1096,24 +1428,24 @@ DateTimePatternGenerator::getBestAppending(int32_t missingFields, UDateTimePatte
             }
             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;
         }
     }
@@ -1162,7 +1494,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){
@@ -1278,7 +1610,7 @@ PatternMap::copyFrom(const PatternMap& other, UErrorCode& status) {
                 status = U_MEMORY_ALLOCATION_ERROR;
                 return;
             }
-
+            curElem->skeletonWasSpecified = otherElem->skeletonWasSpecified;
             if (prevElem!=NULL) {
                 prevElem->next=curElem;
             }
@@ -1382,6 +1714,9 @@ PatternMap::add(const UnicodeString& basePattern,
             }
             // 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
@@ -1538,6 +1873,8 @@ PatternMap::getDuplicateElem(
 DateTimeMatcher::DateTimeMatcher(void) {
 }
 
+DateTimeMatcher::~DateTimeMatcher() {}
+
 DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher& other) {
     copyFrom(other.skeleton);
 }
@@ -1575,7 +1912,7 @@ DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton
         int32_t typeValue = row->field;
         skeletonResult.original[typeValue]=field;
         UChar repeatChar = row->patternChar;
-        int32_t repeatCount = row->minLen > 3 ? 3: row->minLen;
+        int32_t repeatCount = row->minLen; // #7930 removes cap at 3
         while (repeatCount-- > 0) {
             skeletonResult.baseOriginal[typeValue] += repeatChar;
         }
@@ -1780,7 +2117,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);
 }
 
@@ -1831,6 +2168,8 @@ FormatParser::isPatternSeparator(UnicodeString& field) {
     return TRUE;
 }
 
+DistanceInfo::~DistanceInfo() {}
+
 void
 DistanceInfo::setTo(DistanceInfo &other) {
     missingFieldMask = other.missingFieldMask;