ICU-21087 Merge maint/maint-67 to master

diff --git a/.ci-builds/data-filter.json b/.ci-builds/data-filter.json
index d2dd74d..ffde995 100644
--- a/.ci-builds/data-filter.json
+++ b/.ci-builds/data-filter.json
@@ -1,6 +1,6 @@
 {
   "localeFilter": {
-    "filterType": "language",
+    "filterType": "locale",
     "whitelist": [
       "en",
       "de",
diff --git a/docs/userguide/icu_data/buildtool.md b/docs/userguide/icu_data/buildtool.md
index aa34b8e..7c3eadc 100644
--- a/docs/userguide/icu_data/buildtool.md
+++ b/docs/userguide/icu_data/buildtool.md
@@ -202,7 +202,7 @@
 | Region Display <br/> Names | `"region_tree"` | region/\*.txt | **1.1 MiB** |
 | Rule-Based <br/> Number Formatting <br/> (Spellout, Ordinals) | `"rbnf_tree"` | rbnf/\*.txt | 538 KiB |
 | StringPrep | `"stringprep"` | sprep/\*.txt | 193 KiB |
-| Time Zones | `"misc"` <br/> `"zone_tree"` | misc/metaZones.txt <br/> misc/timezoneTypes.txt <br/> misc/windowsZones.txt <br/> misc/zoneinfo64.txt <br/> zone/\*.txt | 41 KiB <br/> 20 KiB <br/> 22 KiB <br/> 151 KiB <br/> **2.7 MiB** |
+| Time Zones | `"misc"` <br/> `"zone_tree"` <br/> `"zone_supplemental"` | misc/metaZones.txt <br/> misc/timezoneTypes.txt <br/> misc/windowsZones.txt <br/> misc/zoneinfo64.txt <br/> zone/\*.txt <br/> zone/tzdbNames.txt | 41 KiB <br/> 20 KiB <br/> 22 KiB <br/> 151 KiB <br/> **2.7 MiB** <br/> 4.8 KiB |
 | Transliteration | `"translit"` | translit/\*.txt | 685 KiB |
 | Unicode Character <br/> Names | `"unames"` | in/unames.icu | 269 KiB |
 | Unicode Text Layout | `"ulayout"` | in/ulayout.icu | 14 KiB |
diff --git a/icu4c/source/common/localematcher.cpp b/icu4c/source/common/localematcher.cpp
index 7f0dceb..85db8c8 100644
--- a/icu4c/source/common/localematcher.cpp
+++ b/icu4c/source/common/localematcher.cpp
@@ -466,6 +466,7 @@
         thresholdDistance(src.thresholdDistance),
         demotionPerDesiredLocale(src.demotionPerDesiredLocale),
         favorSubtag(src.favorSubtag),
+        direction(src.direction),
         supportedLocales(src.supportedLocales), lsrs(src.lsrs),
         supportedLocalesLength(src.supportedLocalesLength),
         supportedLsrToIndex(src.supportedLsrToIndex),
@@ -502,6 +503,7 @@
     thresholdDistance = src.thresholdDistance;
     demotionPerDesiredLocale = src.demotionPerDesiredLocale;
     favorSubtag = src.favorSubtag;
+    direction = src.direction;
     supportedLocales = src.supportedLocales;
     lsrs = src.lsrs;
     supportedLocalesLength = src.supportedLocalesLength;
diff --git a/icu4c/source/common/unicode/umutablecptrie.h b/icu4c/source/common/unicode/umutablecptrie.h
index 13e71ef..f2af364 100644
--- a/icu4c/source/common/unicode/umutablecptrie.h
+++ b/icu4c/source/common/unicode/umutablecptrie.h
@@ -83,25 +83,6 @@
 U_CAPI void U_EXPORT2
 umutablecptrie_close(UMutableCPTrie *trie);
 
-#if U_SHOW_CPLUSPLUS_API
-
-U_NAMESPACE_BEGIN
-
-/**
- * \class LocalUMutableCPTriePointer
- * "Smart pointer" class, closes a UMutableCPTrie via umutablecptrie_close().
- * For most methods see the LocalPointerBase base class.
- *
- * @see LocalPointerBase
- * @see LocalPointer
- * @stable ICU 63
- */
-U_DEFINE_LOCAL_OPEN_POINTER(LocalUMutableCPTriePointer, UMutableCPTrie, umutablecptrie_close);
-
-U_NAMESPACE_END
-
-#endif
-
 /**
  * Creates a mutable trie with the same contents as the UCPMap.
  * You must umutablecptrie_close() the mutable trie once you are done using it.
@@ -235,4 +216,23 @@
 
 U_CDECL_END
 
+#if U_SHOW_CPLUSPLUS_API
+
+U_NAMESPACE_BEGIN
+
+/**
+ * \class LocalUMutableCPTriePointer
+ * "Smart pointer" class, closes a UMutableCPTrie via umutablecptrie_close().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @stable ICU 63
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUMutableCPTriePointer, UMutableCPTrie, umutablecptrie_close);
+
+U_NAMESPACE_END
+
+#endif
+
 #endif
diff --git a/icu4c/source/common/unicode/utext.h b/icu4c/source/common/unicode/utext.h
index 196056b..37d71a3 100644
--- a/icu4c/source/common/unicode/utext.h
+++ b/icu4c/source/common/unicode/utext.h
@@ -183,25 +183,6 @@
 U_STABLE UText * U_EXPORT2
 utext_close(UText *ut);
 
-#if U_SHOW_CPLUSPLUS_API
-
-U_NAMESPACE_BEGIN
-
-/**
- * \class LocalUTextPointer
- * "Smart pointer" class, closes a UText via utext_close().
- * For most methods see the LocalPointerBase base class.
- *
- * @see LocalPointerBase
- * @see LocalPointer
- * @stable ICU 4.4
- */
-U_DEFINE_LOCAL_OPEN_POINTER(LocalUTextPointer, UText, utext_close);
-
-U_NAMESPACE_END
-
-#endif
-
 /**
  * Open a read-only UText implementation for UTF-8 strings.
  * 
@@ -1599,5 +1580,24 @@
 U_CDECL_END
 
 
+#if U_SHOW_CPLUSPLUS_API
+
+U_NAMESPACE_BEGIN
+
+/**
+ * \class LocalUTextPointer
+ * "Smart pointer" class, closes a UText via utext_close().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @stable ICU 4.4
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUTextPointer, UText, utext_close);
+
+U_NAMESPACE_END
+
+#endif
+
 
 #endif
diff --git a/icu4c/source/data/BUILDRULES.py b/icu4c/source/data/BUILDRULES.py
index 2338afd..e6ddea9 100644
--- a/icu4c/source/data/BUILDRULES.py
+++ b/icu4c/source/data/BUILDRULES.py
@@ -33,6 +33,7 @@
     requests += generate_unames(config, io, common_vars)
     requests += generate_misc(config, io, common_vars)
     requests += generate_curr_supplemental(config, io, common_vars)
+    requests += generate_zone_supplemental(config, io, common_vars)
     requests += generate_translit(config, io, common_vars)
 
     # Res Tree Files
@@ -399,6 +400,29 @@
     ]
 
 
+def generate_zone_supplemental(config, io, common_vars):
+    # tzdbNames Res File
+    input_file = InFile("zone/tzdbNames.txt")
+    input_basename = "tzdbNames.txt"
+    output_file = OutFile("zone/tzdbNames.res")
+    return [
+        SingleExecutionRequest(
+            name = "zone_supplemental_res",
+            category = "zone_supplemental",
+            dep_targets = [],
+            input_files = [input_file],
+            output_files = [output_file],
+            tool = IcuTool("genrb"),
+            args = "-s {IN_DIR}/zone -d {OUT_DIR}/zone -i {OUT_DIR} "
+                "-k "
+                "{INPUT_BASENAME}",
+            format_with = {
+                "INPUT_BASENAME": input_basename
+            }
+        )
+    ]
+
+
 def generate_translit(config, io, common_vars):
     input_files = [
         InFile("translit/root.txt"),
@@ -444,10 +468,11 @@
     requests = []
     category = "%s_tree" % sub_dir
     out_prefix = "%s/" % out_sub_dir if out_sub_dir else ""
-    # TODO: Clean this up for curr
     input_files = [InFile(filename) for filename in io.glob("%s/*.txt" % sub_dir)]
     if sub_dir == "curr":
         input_files.remove(InFile("curr/supplementalData.txt"))
+    if sub_dir == "zone":
+        input_files.remove(InFile("zone/tzdbNames.txt"))
     input_basenames = [v.filename[len(sub_dir)+1:] for v in input_files]
     output_files = [
         OutFile("%s%s.res" % (out_prefix, v[:-4]))
diff --git a/icu4c/source/i18n/double-conversion-utils.h b/icu4c/source/i18n/double-conversion-utils.h
index 10d8fdd..8c6a0e1 100644
--- a/icu4c/source/i18n/double-conversion-utils.h
+++ b/icu4c/source/i18n/double-conversion-utils.h
@@ -66,15 +66,23 @@
 #endif
 #endif
 
+// Not all compilers support __has_attribute and combining a check for both
+// ifdef and __has_attribute on the same preprocessor line isn't portable.
+#ifdef __has_attribute
+#   define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) __has_attribute(x)
+#else
+#   define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) 0
+#endif
+
 #ifndef DOUBLE_CONVERSION_UNUSED
-#ifdef __GNUC__
+#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(unused)
 #define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
 #else
 #define DOUBLE_CONVERSION_UNUSED
 #endif
 #endif
 
-#if defined(__clang__) && __has_attribute(uninitialized)
+#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(uninitialized)
 #define DOUBLE_CONVERSION_STACK_UNINITIALIZED __attribute__((uninitialized))
 #else
 #define DOUBLE_CONVERSION_STACK_UNINITIALIZED
diff --git a/icu4c/source/i18n/listformatter.cpp b/icu4c/source/i18n/listformatter.cpp
index b9065e8..da99c92 100644
--- a/icu4c/source/i18n/listformatter.cpp
+++ b/icu4c/source/i18n/listformatter.cpp
@@ -348,6 +348,7 @@
     return result;
 }
 
+#if !UCONFIG_NO_FORMATTING
 static const char* typeWidthToStyleString(UListFormatterType type, UListFormatterWidth width) {
     switch (type) {
         case ULISTFMT_TYPE_AND:
@@ -391,6 +392,7 @@
 
     return nullptr;
 }
+#endif
 
 static const UChar solidus = 0x2F;
 static const UChar aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/"
@@ -511,9 +513,14 @@
 }
 
 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
+#if !UCONFIG_NO_FORMATTING
     return createInstance(locale, ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, errorCode);
+#else
+    return createInstance(locale, "standard", errorCode);
+#endif
 }
 
+#if !UCONFIG_NO_FORMATTING
 ListFormatter* ListFormatter::createInstance(
         const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode) {
     const char* style = typeWidthToStyleString(type, width);
@@ -523,6 +530,7 @@
     }
     return createInstance(locale, style, errorCode);
 }
+#endif
 
 ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
     const ListFormatInternal* listFormatInternal = getListFormatInternal(locale, style, errorCode);
diff --git a/icu4c/source/i18n/measunit.cpp b/icu4c/source/i18n/measunit.cpp
index 344ba45..4edf130 100644
--- a/icu4c/source/i18n/measunit.cpp
+++ b/icu4c/source/i18n/measunit.cpp
@@ -537,9 +537,9 @@
     "solar-mass",
     "stone",
     "ton",
-    "one",
-    "percent",
-    "permille",
+    "", // TODO(ICU-21076): manual edit of what should have been generated by Java.
+    "percent", // TODO(ICU-21076): regenerate, deal with duplication.
+    "permille", // TODO(ICU-21076): regenerate, deal with duplication.
     "gigawatt",
     "horsepower",
     "kilowatt",
diff --git a/icu4c/source/i18n/measunit_extra.cpp b/icu4c/source/i18n/measunit_extra.cpp
index 8af9e41..aeb60017 100644
--- a/icu4c/source/i18n/measunit_extra.cpp
+++ b/icu4c/source/i18n/measunit_extra.cpp
@@ -12,6 +12,7 @@
 // Helpful in toString methods and elsewhere.
 #define UNISTR_FROM_STRING_EXPLICIT
 
+#include <cstdlib>
 #include "cstring.h"
 #include "measunit_impl.h"
 #include "uarrsort.h"
@@ -34,17 +35,32 @@
 // TODO: Propose a new error code for this?
 constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR;
 
-// This is to ensure we only insert positive integers into the trie
+// Trie value offset for SI Prefixes. This is big enough to ensure we only
+// insert positive integers into the trie.
 constexpr int32_t kSIPrefixOffset = 64;
 
+// Trie value offset for compound parts, e.g. "-per-", "-", "-and-".
 constexpr int32_t kCompoundPartOffset = 128;
 
 enum CompoundPart {
+    // Represents "-per-"
     COMPOUND_PART_PER = kCompoundPartOffset,
+    // Represents "-"
     COMPOUND_PART_TIMES,
-    COMPOUND_PART_PLUS,
+    // Represents "-and-"
+    COMPOUND_PART_AND,
 };
 
+// Trie value offset for "per-".
+constexpr int32_t kInitialCompoundPartOffset = 192;
+
+enum InitialCompoundPart {
+    // Represents "per-", the only compound part that can appear at the start of
+    // an identifier.
+    INITIAL_COMPOUND_PART_PER = kInitialCompoundPartOffset,
+};
+
+// Trie value offset for powers like "square-", "cubic-", "p2-" etc.
 constexpr int32_t kPowerPartOffset = 256;
 
 enum PowerPart {
@@ -64,6 +80,8 @@
     POWER_PART_P15,
 };
 
+// Trie value offset for simple units, e.g. "gram", "nautical-mile",
+// "fluid-ounce-imperial".
 constexpr int32_t kSimpleUnitOffset = 512;
 
 const struct SIPrefixStrings {
@@ -94,7 +112,6 @@
 
 // TODO(ICU-21059): Get this list from data
 const char16_t* const gSimpleUnits[] = {
-    u"one", // note: expected to be index 0
     u"candela",
     u"carat",
     u"gram",
@@ -226,7 +243,8 @@
     // Add syntax parts (compound, power prefixes)
     b.add(u"-per-", COMPOUND_PART_PER, status);
     b.add(u"-", COMPOUND_PART_TIMES, status);
-    b.add(u"-and-", COMPOUND_PART_PLUS, status);
+    b.add(u"-and-", COMPOUND_PART_AND, status);
+    b.add(u"per-", INITIAL_COMPOUND_PART_PER, status);
     b.add(u"square-", POWER_PART_P2, status);
     b.add(u"cubic-", POWER_PART_P3, status);
     b.add(u"p2-", POWER_PART_P2, status);
@@ -270,28 +288,30 @@
     enum Type {
         TYPE_UNDEFINED,
         TYPE_SI_PREFIX,
+        // Token type for "-per-", "-", and "-and-".
         TYPE_COMPOUND_PART,
+        // Token type for "per-".
+        TYPE_INITIAL_COMPOUND_PART,
         TYPE_POWER_PART,
-        TYPE_ONE,
         TYPE_SIMPLE_UNIT,
     };
 
+    // Calling getType() is invalid, resulting in an assertion failure, if Token
+    // value isn't positive.
     Type getType() const {
-        if (fMatch <= 0) {
-            UPRV_UNREACHABLE;
-        }
+        U_ASSERT(fMatch > 0);
         if (fMatch < kCompoundPartOffset) {
             return TYPE_SI_PREFIX;
         }
-        if (fMatch < kPowerPartOffset) {
+        if (fMatch < kInitialCompoundPartOffset) {
             return TYPE_COMPOUND_PART;
         }
+        if (fMatch < kPowerPartOffset) {
+            return TYPE_INITIAL_COMPOUND_PART;
+        }
         if (fMatch < kSimpleUnitOffset) {
             return TYPE_POWER_PART;
         }
-        if (fMatch == kSimpleUnitOffset) {
-            return TYPE_ONE;
-        }
         return TYPE_SIMPLE_UNIT;
     }
 
@@ -300,11 +320,22 @@
         return static_cast<UMeasureSIPrefix>(fMatch - kSIPrefixOffset);
     }
 
+    // Valid only for tokens with type TYPE_COMPOUND_PART.
     int32_t getMatch() const {
         U_ASSERT(getType() == TYPE_COMPOUND_PART);
         return fMatch;
     }
 
+    int32_t getInitialCompoundPart() const {
+        // Even if there is only one InitialCompoundPart value, we have this
+        // function for the simplicity of code consistency.
+        U_ASSERT(getType() == TYPE_INITIAL_COMPOUND_PART);
+        // Defensive: if this assert fails, code using this function also needs
+        // to change.
+        U_ASSERT(fMatch == INITIAL_COMPOUND_PART_PER);
+        return fMatch;
+    }
+
     int8_t getPower() const {
         U_ASSERT(getType() == TYPE_POWER_PART);
         return static_cast<int8_t>(fMatch - kPowerPartOffset);
@@ -321,6 +352,14 @@
 
 class Parser {
 public:
+    /**
+     * Factory function for parsing the given identifier.
+     *
+     * @param source The identifier to parse. This function does not make a copy
+     * of source: the underlying string that source points at, must outlive the
+     * parser.
+     * @param status ICU error code.
+     */
     static Parser from(StringPiece source, UErrorCode& status) {
         if (U_FAILURE(status)) {
             return Parser();
@@ -339,10 +378,18 @@
     }
 
 private:
+    // Tracks parser progress: the offset into fSource.
     int32_t fIndex = 0;
+
+    // Since we're not owning this memory, whatever is passed to the constructor
+    // should live longer than this Parser - and the parser shouldn't return any
+    // references to that string.
     StringPiece fSource;
     UCharsTrie fTrie;
 
+    // Set to true when we've seen a "-per-" or a "per-", after which all units
+    // are in the denominator. Until we find an "-and-", at which point the
+    // identifier is invalid pending TODO(CLDR-13700).
     bool fAfterPer = false;
 
     Parser() : fSource(""), fTrie(u"") {}
@@ -354,11 +401,17 @@
         return fIndex < fSource.length();
     }
 
+    // Returns the next Token parsed from fSource, advancing fIndex to the end
+    // of that token in fSource. In case of U_FAILURE(status), the token
+    // returned will cause an abort if getType() is called on it.
     Token nextToken(UErrorCode& status) {
         fTrie.reset();
         int32_t match = -1;
+        // Saves the position in the fSource string for the end of the most
+        // recent matching token.
         int32_t previ = -1;
-        do {
+        // Find the longest token that matches a value in the trie:
+        while (fIndex < fSource.length()) {
             auto result = fTrie.next(fSource.data()[fIndex++]);
             if (result == USTRINGTRIE_NO_MATCH) {
                 break;
@@ -373,7 +426,7 @@
             }
             U_ASSERT(result == USTRINGTRIE_INTERMEDIATE_VALUE);
             // continue;
-        } while (fIndex < fSource.length());
+        }
 
         if (match < 0) {
             status = kUnitIdentifierSyntaxError;
@@ -383,62 +436,88 @@
         return Token(match);
     }
 
-    void nextSingleUnit(SingleUnitImpl& result, bool& sawPlus, UErrorCode& status) {
-        sawPlus = false;
+    /**
+     * Returns the next "single unit" via result.
+     *
+     * If a "-per-" was parsed, the result will have appropriate negative
+     * dimensionality.
+     *
+     * Returns an error if we parse both compound units and "-and-", since mixed
+     * compound units are not yet supported - TODO(CLDR-13700).
+     *
+     * @param result Will be overwritten by the result, if status shows success.
+     * @param sawAnd If an "-and-" was parsed prior to finding the "single
+     * unit", sawAnd is set to true. If not, it is left as is.
+     * @param status ICU error code.
+     */
+    void nextSingleUnit(SingleUnitImpl& result, bool& sawAnd, UErrorCode& status) {
         if (U_FAILURE(status)) {
             return;
         }
 
-        if (!hasNext()) {
-            // probably "one"
-            return;
-        }
-
         // state:
         // 0 = no tokens seen yet (will accept power, SI prefix, or simple unit)
         // 1 = power token seen (will not accept another power token)
         // 2 = SI prefix token seen (will not accept a power or SI prefix token)
         int32_t state = 0;
-        int32_t previ = fIndex;
 
-        // Maybe read a compound part
-        if (fIndex != 0) {
-            Token token = nextToken(status);
-            if (U_FAILURE(status)) {
-                return;
+        bool atStart = fIndex == 0;
+        Token token = nextToken(status);
+        if (U_FAILURE(status)) { return; }
+
+        if (atStart) {
+            // Identifiers optionally start with "per-".
+            if (token.getType() == Token::TYPE_INITIAL_COMPOUND_PART) {
+                U_ASSERT(token.getInitialCompoundPart() == INITIAL_COMPOUND_PART_PER);
+                fAfterPer = true;
+                result.dimensionality = -1;
+
+                token = nextToken(status);
+                if (U_FAILURE(status)) { return; }
             }
+        } else {
+            // All other SingleUnit's are separated from previous SingleUnit's
+            // via a compound part:
             if (token.getType() != Token::TYPE_COMPOUND_PART) {
                 status = kUnitIdentifierSyntaxError;
                 return;
             }
+
             switch (token.getMatch()) {
-                case COMPOUND_PART_PER:
-                    if (fAfterPer) {
-                        status = kUnitIdentifierSyntaxError;
-                        return;
-                    }
-                    fAfterPer = true;
+            case COMPOUND_PART_PER:
+                if (sawAnd) {
+                    // Mixed compound units not yet supported,
+                    // TODO(CLDR-13700).
+                    status = kUnitIdentifierSyntaxError;
+                    return;
+                }
+                fAfterPer = true;
+                result.dimensionality = -1;
+                break;
+
+            case COMPOUND_PART_TIMES:
+                if (fAfterPer) {
                     result.dimensionality = -1;
-                    break;
+                }
+                break;
 
-                case COMPOUND_PART_TIMES:
-                    break;
-
-                case COMPOUND_PART_PLUS:
-                    sawPlus = true;
-                    fAfterPer = false;
-                    break;
+            case COMPOUND_PART_AND:
+                if (fAfterPer) {
+                    // Can't start with "-and-", and mixed compound units
+                    // not yet supported, TODO(CLDR-13700).
+                    status = kUnitIdentifierSyntaxError;
+                    return;
+                }
+                sawAnd = true;
+                break;
             }
-            previ = fIndex;
+
+            token = nextToken(status);
+            if (U_FAILURE(status)) { return; }
         }
 
-        // Read a unit
-        while (hasNext()) {
-            Token token = nextToken(status);
-            if (U_FAILURE(status)) {
-                return;
-            }
-
+        // Read tokens until we have a complete SingleUnit or we reach the end.
+        while (true) {
             switch (token.getType()) {
                 case Token::TYPE_POWER_PART:
                     if (state > 0) {
@@ -446,7 +525,6 @@
                         return;
                     }
                     result.dimensionality *= token.getPower();
-                    previ = fIndex;
                     state = 1;
                     break;
 
@@ -456,54 +534,62 @@
                         return;
                     }
                     result.siPrefix = token.getSIPrefix();
-                    previ = fIndex;
                     state = 2;
                     break;
 
-                case Token::TYPE_ONE:
-                    // Skip "one" and go to the next unit
-                    return nextSingleUnit(result, sawPlus, status);
-
                 case Token::TYPE_SIMPLE_UNIT:
                     result.index = token.getSimpleUnitIndex();
-                    result.identifier = fSource.substr(previ, fIndex - previ);
                     return;
 
                 default:
                     status = kUnitIdentifierSyntaxError;
                     return;
             }
-        }
 
-        // We ran out of tokens before finding a complete single unit.
-        status = kUnitIdentifierSyntaxError;
+            if (!hasNext()) {
+                // We ran out of tokens before finding a complete single unit.
+                status = kUnitIdentifierSyntaxError;
+                return;
+            }
+            token = nextToken(status);
+            if (U_FAILURE(status)) {
+                return;
+            }
+        }
     }
 
+    /// @param result is modified, not overridden. Caller must pass in a
+    /// default-constructed (empty) MeasureUnitImpl instance.
     void parseImpl(MeasureUnitImpl& result, UErrorCode& status) {
         if (U_FAILURE(status)) {
             return;
         }
+        if (fSource.empty()) {
+            // The dimenionless unit: nothing to parse. leave result as is.
+            return;
+        }
         int32_t unitNum = 0;
         while (hasNext()) {
-            bool sawPlus;
+            bool sawAnd = false;
             SingleUnitImpl singleUnit;
-            nextSingleUnit(singleUnit, sawPlus, status);
+            nextSingleUnit(singleUnit, sawAnd, status);
             if (U_FAILURE(status)) {
                 return;
             }
-            if (singleUnit.index == 0) {
-                continue;
-            }
+            U_ASSERT(!singleUnit.isDimensionless());
             bool added = result.append(singleUnit, status);
-            if (sawPlus && !added) {
+            if (sawAnd && !added) {
                 // Two similar units are not allowed in a mixed unit
                 status = kUnitIdentifierSyntaxError;
                 return;
             }
             if ((++unitNum) >= 2) {
-                UMeasureUnitComplexity complexity = sawPlus
-                    ? UMEASURE_UNIT_MIXED
-                    : UMEASURE_UNIT_COMPOUND;
+                // nextSingleUnit fails appropriately for "per" and "and" in the
+                // same identifier. It doesn't fail for other compound units
+                // (COMPOUND_PART_TIMES). Consequently we take care of that
+                // here.
+                UMeasureUnitComplexity complexity =
+                    sawAnd ? UMEASURE_UNIT_MIXED : UMEASURE_UNIT_COMPOUND;
                 if (unitNum == 2) {
                     U_ASSERT(result.complexity == UMEASURE_UNIT_SINGLE);
                     result.complexity = complexity;
@@ -526,15 +612,22 @@
 
 /**
  * Generate the identifier string for a single unit in place.
+ *
+ * Does not support the dimensionless SingleUnitImpl: calling serializeSingle
+ * with the dimensionless unit results in an U_INTERNAL_PROGRAM_ERROR.
+ *
+ * @param first If singleUnit is part of a compound unit, and not its first
+ * single unit, set this to false. Otherwise: set to true.
  */
 void serializeSingle(const SingleUnitImpl& singleUnit, bool first, CharString& output, UErrorCode& status) {
     if (first && singleUnit.dimensionality < 0) {
-        output.append("one-per-", status);
+        // Essentially the "unary per". For compound units with a numerator, the
+        // caller takes care of the "binary per".
+        output.append("per-", status);
     }
 
-    if (singleUnit.index == 0) {
-        // Don't propagate SI prefixes and powers on one
-        output.append("one", status);
+    if (singleUnit.isDimensionless()) {
+        status = U_INTERNAL_PROGRAM_ERROR;
         return;
     }
     int8_t posPower = std::abs(singleUnit.dimensionality);
@@ -573,7 +666,7 @@
         return;
     }
 
-    output.append(singleUnit.identifier, status);
+    output.appendInvariantChars(gSimpleUnits[singleUnit.index], status);
 }
 
 /**
@@ -585,7 +678,8 @@
     }
     U_ASSERT(impl.identifier.isEmpty());
     if (impl.units.length() == 0) {
-        impl.identifier.append("one", status);
+        // Dimensionless, constructed by the default constructor: no appending
+        // to impl.identifier, we wish it to contain the zero-length string.
         return;
     }
     if (impl.complexity == UMEASURE_UNIT_COMPOUND) {
@@ -624,8 +718,17 @@
 
 }
 
-/** @return true if a new item was added */
+/**
+ * Appends a SingleUnitImpl to a MeasureUnitImpl.
+ *
+ * @return true if a new item was added. If unit is the dimensionless unit, it
+ * is never added: the return value will always be false.
+ */
 bool appendImpl(MeasureUnitImpl& impl, const SingleUnitImpl& unit, UErrorCode& status) {
+    if (unit.isDimensionless()) {
+        // We don't append dimensionless units.
+        return false;
+    }
     // Find a similar unit that already exists, to attempt to coalesce
     SingleUnitImpl* oldUnit = nullptr;
     for (int32_t i = 0; i < impl.units.length(); i++) {
@@ -635,6 +738,8 @@
         }
     }
     if (oldUnit) {
+        // Both dimensionalities will be positive, or both will be negative, by
+        // virtue of isCompatibleWith().
         oldUnit->dimensionality += unit.dimensionality;
     } else {
         SingleUnitImpl* destination = impl.units.emplaceBack();
@@ -734,7 +839,12 @@
 }
 
 int32_t MeasureUnit::getDimensionality(UErrorCode& status) const {
-    return SingleUnitImpl::forMeasureUnit(*this, status).dimensionality;
+    SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
+    if (U_FAILURE(status)) { return 0; }
+    if (singleUnit.isDimensionless()) {
+        return 0;
+    }
+    return singleUnit.dimensionality;
 }
 
 MeasureUnit MeasureUnit::withDimensionality(int32_t dimensionality, UErrorCode& status) const {
diff --git a/icu4c/source/i18n/measunit_impl.h b/icu4c/source/i18n/measunit_impl.h
index cf0ea63..c69d243 100644
--- a/icu4c/source/i18n/measunit_impl.h
+++ b/icu4c/source/i18n/measunit_impl.h
@@ -25,14 +25,25 @@
 struct SingleUnitImpl : public UMemory {
     /**
      * Gets a single unit from the MeasureUnit. If there are multiple single units, sets an error
-     * code and return the base dimensionless unit. Parses if necessary.
+     * code and returns the base dimensionless unit. Parses if necessary.
      */
     static SingleUnitImpl forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status);
 
     /** Transform this SingleUnitImpl into a MeasureUnit, simplifying if possible. */
     MeasureUnit build(UErrorCode& status) const;
 
-    /** Compare this SingleUnitImpl to another SingleUnitImpl. */
+    /**
+     * Compare this SingleUnitImpl to another SingleUnitImpl for the sake of
+     * sorting and coalescing.
+     *
+     * Takes the sign of dimensionality into account, but not the absolute
+     * value: per-meter is not considered the same as meter, but meter is
+     * considered the same as square-meter.
+     *
+     * The dimensionless unit generally does not get compared, but if it did, it
+     * would sort before other units by virtue of index being < 0 and
+     * dimensionality not being negative.
+     */
     int32_t compareTo(const SingleUnitImpl& other) const {
         if (dimensionality < 0 && other.dimensionality > 0) {
             // Positive dimensions first
@@ -66,16 +77,36 @@
         return (compareTo(other) == 0);
     }
 
-    /** Simple unit index, unique for every simple unit. */
-    int32_t index = 0;
+    /**
+     * Returns true if this unit is the "dimensionless base unit", as produced
+     * by the MeasureUnit() default constructor. (This does not include the
+     * likes of concentrations or angles.)
+     */
+    bool isDimensionless() const {
+        return index == -1;
+    }
 
-    /** Simple unit identifier; memory not owned by the SimpleUnit. */
-    StringPiece identifier;
+    /**
+     * Simple unit index, unique for every simple unit, -1 for the dimensionless
+     * unit. This is an index into a string list in measunit_extra.cpp.
+     *
+     * The default value is -1, meaning the dimensionless unit:
+     * isDimensionless() will return true, until index is changed.
+     */
+    int32_t index = -1;
 
-    /** SI prefix. **/
+    /**
+     * SI prefix.
+     *
+     * This is ignored for the dimensionless unit.
+     */
     UMeasureSIPrefix siPrefix = UMEASURE_SI_PREFIX_ONE;
-    
-    /** Dimensionality. **/
+
+    /**
+     * Dimensionality.
+     *
+     * This is meaningless for the dimensionless unit.
+     */
     int32_t dimensionality = 1;
 };
 
@@ -95,7 +126,8 @@
      *
      * @param identifier The unit identifier string.
      * @param status Set if the identifier string is not valid.
-     * @return A newly parsed value object.
+     * @return A newly parsed value object. Behaviour of this unit is
+     * unspecified if an error is returned via status.
      */
     static MeasureUnitImpl forIdentifier(StringPiece identifier, UErrorCode& status);
 
@@ -148,15 +180,23 @@
     /** Mutates this MeasureUnitImpl to take the reciprocal. */
     void takeReciprocal(UErrorCode& status);
 
-    /** Mutates this MeasureUnitImpl to append a single unit. */
+    /**
+     * Mutates this MeasureUnitImpl to append a single unit.
+     *
+     * @return true if a new item was added. If unit is the dimensionless unit,
+     * it is never added: the return value will always be false.
+     */
     bool append(const SingleUnitImpl& singleUnit, UErrorCode& status);
 
     /** The complexity, either SINGLE, COMPOUND, or MIXED. */
     UMeasureUnitComplexity complexity = UMEASURE_UNIT_SINGLE;
 
     /**
-     * The list of simple units. These may be summed or multiplied, based on the value of the
-     * complexity field.
+     * The list of simple units. These may be summed or multiplied, based on the
+     * value of the complexity field.
+     *
+     * The "dimensionless" unit (SingleUnitImpl default constructor) must not be
+     * added to this list.
      */
     MaybeStackVector<SingleUnitImpl> units;
 
diff --git a/icu4c/source/i18n/nounit.cpp b/icu4c/source/i18n/nounit.cpp
index b993cb5..1d4aa05 100644
--- a/icu4c/source/i18n/nounit.cpp
+++ b/icu4c/source/i18n/nounit.cpp
@@ -11,7 +11,7 @@
 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NoUnit)
 
 NoUnit U_EXPORT2 NoUnit::base() {
-    return NoUnit("one");
+    return NoUnit("");
 }
 
 NoUnit U_EXPORT2 NoUnit::percent() {
diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp
index 8042979..5bba09c 100644
--- a/icu4c/source/i18n/number_formatimpl.cpp
+++ b/icu4c/source/i18n/number_formatimpl.cpp
@@ -203,6 +203,9 @@
             patternStyle = CLDR_PATTERN_STYLE_CURRENCY;
         }
         pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status);
+        if (U_FAILURE(status)) {
+            return nullptr;
+        }
     }
     auto patternInfo = new ParsedPatternInfo();
     if (patternInfo == nullptr) {
@@ -211,6 +214,9 @@
     }
     fPatternInfo.adoptInstead(patternInfo);
     PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status);
+    if (U_FAILURE(status)) {
+        return nullptr;
+    }
 
     /////////////////////////////////////////////////////////////////////////////////////
     /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
@@ -241,6 +247,9 @@
         roundingMode = precision.fRoundingMode;
     }
     fMicros.rounder = {precision, roundingMode, currency, status};
+    if (U_FAILURE(status)) {
+        return nullptr;
+    }
 
     // Grouping strategy
     if (!macros.grouper.isBogus()) {
@@ -323,6 +332,9 @@
     if (safe) {
         fImmutablePatternModifier.adoptInstead(patternModifier->createImmutable(status));
     }
+    if (U_FAILURE(status)) {
+        return nullptr;
+    }
 
     // Outer modifier (CLDR units and currency long names)
     if (isCldrUnit) {
@@ -349,6 +361,9 @@
         // No outer modifier required
         fMicros.modOuter = &fMicros.helpers.emptyWeakModifier;
     }
+    if (U_FAILURE(status)) {
+        return nullptr;
+    }
 
     // Compact notation
     if (macros.notation.fType == Notation::NTN_COMPACT) {
@@ -371,6 +386,9 @@
         fCompactHandler.adoptInstead(newCompactHandler);
         chain = fCompactHandler.getAlias();
     }
+    if (U_FAILURE(status)) {
+        return nullptr;
+    }
 
     // Always add the pattern modifier as the last element of the chain.
     if (safe) {
diff --git a/icu4c/source/i18n/number_longnames.cpp b/icu4c/source/i18n/number_longnames.cpp
index 74ee0ef..bb32d03 100644
--- a/icu4c/source/i18n/number_longnames.cpp
+++ b/icu4c/source/i18n/number_longnames.cpp
@@ -246,7 +246,8 @@
         if (U_FAILURE(status)) { return result; }
         UnicodeString secondaryFormat = getWithPlural(secondaryData, StandardPlural::Form::ONE, status);
         if (U_FAILURE(status)) { return result; }
-        SimpleFormatter secondaryCompiled(secondaryFormat, 1, 1, status);
+        // Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale.
+        SimpleFormatter secondaryCompiled(secondaryFormat, 0, 1, status);
         if (U_FAILURE(status)) { return result; }
         UnicodeString secondaryString = secondaryCompiled.getTextWithNoArguments().trim();
         // TODO: Why does UnicodeString need to be explicit in the following line?
diff --git a/icu4c/source/i18n/unicode/dtptngen.h b/icu4c/source/i18n/unicode/dtptngen.h
index 35736a0..dd99d58 100644
--- a/icu4c/source/i18n/unicode/dtptngen.h
+++ b/icu4c/source/i18n/unicode/dtptngen.h
@@ -483,6 +483,8 @@
      */
     const UnicodeString& getDecimal() const;
 
+#if !UCONFIG_NO_FORMATTING
+
 #ifndef U_HIDE_DRAFT_API
     /**
      * Get the default hour cycle for a locale. Uses the locale that the
@@ -499,6 +501,8 @@
     UDateFormatHourCycle getDefaultHourCycle(UErrorCode& status) const;
 #endif  /* U_HIDE_DRAFT_API */
 
+#endif /* #if !UCONFIG_NO_FORMATTING */
+    
     /**
      * ICU "poor man's RTTI", returns a UClassID for the actual class.
      *
diff --git a/icu4c/source/i18n/unicode/listformatter.h b/icu4c/source/i18n/unicode/listformatter.h
index 26b42c2..211055d 100644
--- a/icu4c/source/i18n/unicode/listformatter.h
+++ b/icu4c/source/i18n/unicode/listformatter.h
@@ -186,6 +186,7 @@
     static ListFormatter* createInstance(const Locale& locale, UErrorCode& errorCode);
 
 #ifndef U_HIDE_DRAFT_API
+#if !UCONFIG_NO_FORMATTING
     /**
      * Creates a ListFormatter for the given locale, list type, and style.
      *
@@ -198,8 +199,9 @@
      */
     static ListFormatter* createInstance(
       const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode);
+#endif  /* !UCONFIG_NO_FORMATTING */
 #endif  /* U_HIDE_DRAFT_API */
-
+  
 #ifndef U_HIDE_INTERNAL_API
     /**
      * Creates a ListFormatter appropriate for a locale and style.
diff --git a/icu4c/source/i18n/unicode/measunit.h b/icu4c/source/i18n/unicode/measunit.h
index d221fd8..e240092 100644
--- a/icu4c/source/i18n/unicode/measunit.h
+++ b/icu4c/source/i18n/unicode/measunit.h
@@ -37,7 +37,7 @@
  * Enumeration for unit complexity. There are three levels:
  * 
  * - SINGLE: A single unit, optionally with a power and/or SI prefix. Examples: hectare,
- *           square-kilometer, kilojoule, one-per-second.
+ *           square-kilometer, kilojoule, per-second.
  * - COMPOUND: A unit composed of the product of multiple single units. Examples:
  *             meter-per-second, kilowatt-hour, kilogram-meter-per-square-second.
  * - MIXED: A unit composed of the sum of multiple single units. Examples: foot+inch,
@@ -387,6 +387,8 @@
      * NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
      * occur. For more information, see UMeasureUnitComplexity.
      *
+     * For the base dimensionless unit, withDimensionality does nothing.
+     *
      * @param dimensionality The dimensionality (power).
      * @param status Set if this is not a SINGLE unit or if another error occurs.
      * @return A new SINGLE unit.
@@ -401,6 +403,8 @@
      * NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
      * occur. For more information, see UMeasureUnitComplexity.
      *
+     * For the base dimensionless unit, getDimensionality returns 0.
+     *
      * @param status Set if this is not a SINGLE unit or if another error occurs.
      * @return The dimensionality (power) of this simple unit.
      * @draft ICU 67
@@ -447,7 +451,7 @@
      *
      * Examples:
      * - Given "meter-kilogram-per-second", three units will be returned: "meter",
-     *   "kilogram", and "one-per-second".
+     *   "kilogram", and "per-second".
      * - Given "hour+minute+second", three units will be returned: "hour", "minute",
      *   and "second".
      *
@@ -3375,11 +3379,15 @@
 
 private:
 
-    // If non-null, fImpl is owned by the MeasureUnit.
+    // Used by new draft APIs in ICU 67. If non-null, fImpl is owned by the
+    // MeasureUnit.
     MeasureUnitImpl* fImpl;
 
-    // These two ints are indices into static string lists in measunit.cpp
+    // An index into a static string list in measunit.cpp. If set to -1, fImpl
+    // is in use instead of fTypeId and fSubTypeId.
     int16_t fSubTypeId;
+    // An index into a static string list in measunit.cpp. If set to -1, fImpl
+    // is in use instead of fTypeId and fSubTypeId.
     int8_t fTypeId;
 
     MeasureUnit(int32_t typeId, int32_t subTypeId);
@@ -3389,7 +3397,11 @@
     static MeasureUnit *create(int typeId, int subTypeId, UErrorCode &status);
 
     /**
-     * @return Whether subType is known to ICU.
+     * Sets output's typeId and subTypeId according to subType, if subType is a
+     * valid/known identifier.
+     *
+     * @return Whether subType is known to ICU. If false, output was not
+     * modified.
      */
     static bool findBySubType(StringPiece subType, MeasureUnit* output);
 
diff --git a/icu4c/source/i18n/unicode/udatpg.h b/icu4c/source/i18n/unicode/udatpg.h
index 74c812f..5abe147 100644
--- a/icu4c/source/i18n/unicode/udatpg.h
+++ b/icu4c/source/i18n/unicode/udatpg.h
@@ -652,6 +652,8 @@
                              const UChar *skeleton, int32_t skeletonLength,
                              int32_t *pLength);
 
+#if !UCONFIG_NO_FORMATTING
+
 #ifndef U_HIDE_DRAFT_API
 /**
  * Return the default hour cycle for a locale. Uses the locale that the
@@ -670,4 +672,6 @@
 udatpg_getDefaultHourCycle(const UDateTimePatternGenerator *dtpg, UErrorCode* pErrorCode);
 #endif  /* U_HIDE_DRAFT_API */
 
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
 #endif
diff --git a/icu4c/source/python/icutools/databuilder/filtration.py b/icu4c/source/python/icutools/databuilder/filtration.py
index acdba0e..554013a 100644
--- a/icu4c/source/python/icutools/databuilder/filtration.py
+++ b/icu4c/source/python/icutools/databuilder/filtration.py
@@ -217,7 +217,7 @@
             return "root"
         i = locale.rfind("_")
         if i < 0:
-            assert locale == "root"
+            assert locale == "root", "Invalid locale: %s/%s" % (tree, locale)
             return None
         return locale[:i]
 
diff --git a/icu4c/source/test/cintltst/unumberformattertst.c b/icu4c/source/test/cintltst/unumberformattertst.c
index 2e296f0..8919c78 100644
--- a/icu4c/source/test/cintltst/unumberformattertst.c
+++ b/icu4c/source/test/cintltst/unumberformattertst.c
@@ -9,9 +9,11 @@
 // Helpful in toString methods and elsewhere.
 #define UNISTR_FROM_STRING_EXPLICIT
 
+#include <stdio.h>
 #include "unicode/unumberformatter.h"
 #include "unicode/umisc.h"
 #include "unicode/unum.h"
+#include "unicode/ustring.h"
 #include "cformtst.h"
 #include "cintltst.h"
 #include "cmemory.h"
@@ -26,6 +28,8 @@
 
 static void TestSkeletonParseError(void);
 
+static void TestPerUnitInArabic(void);
+
 void addUNumberFormatterTest(TestNode** root);
 
 #define TESTCASE(x) addTest(root, &x, "tsformat/unumberformatter/" #x)
@@ -36,6 +40,7 @@
     TESTCASE(TestExampleCode);
     TESTCASE(TestFormattedValue);
     TESTCASE(TestSkeletonParseError);
+    TESTCASE(TestPerUnitInArabic);
 }
 
 
@@ -254,5 +259,88 @@
     unumf_close(uformatter);
 }
 
-
+static void TestPerUnitInArabic() {
+    const char* simpleMeasureUnits[] = {
+        "area-acre",
+        "digital-bit",
+        "digital-byte",
+        "temperature-celsius",
+        "length-centimeter",
+        "duration-day",
+        "angle-degree",
+        "temperature-fahrenheit",
+        "volume-fluid-ounce",
+        "length-foot",
+        "volume-gallon",
+        "digital-gigabit",
+        "digital-gigabyte",
+        "mass-gram",
+        "area-hectare",
+        "duration-hour",
+        "length-inch",
+        "digital-kilobit",
+        "digital-kilobyte",
+        "mass-kilogram",
+        "length-kilometer",
+        "volume-liter",
+        "digital-megabit",
+        "digital-megabyte",
+        "length-meter",
+        "length-mile",
+        "length-mile-scandinavian",
+        "volume-milliliter",
+        "length-millimeter",
+        "duration-millisecond",
+        "duration-minute",
+        "duration-month",
+        "mass-ounce",
+        "concentr-percent",
+        "digital-petabyte",
+        "mass-pound",
+        "duration-second",
+        "mass-stone",
+        "digital-terabit",
+        "digital-terabyte",
+        "duration-week",
+        "length-yard",
+        "duration-year"
+    };
+#define BUFFER_LEN 256
+    char buffer[BUFFER_LEN];
+    UChar ubuffer[BUFFER_LEN];
+    const char* locale = "ar";
+    UErrorCode status = U_ZERO_ERROR;
+    UFormattedNumber* formatted = unumf_openResult(&status);
+    if (U_FAILURE(status)) {
+        log_err("FAIL: unumf_openResult failed");
+        return;
+    }
+    for(int32_t i=0; i < UPRV_LENGTHOF(simpleMeasureUnits); ++i) {
+        for(int32_t j=0; j < UPRV_LENGTHOF(simpleMeasureUnits); ++j) {
+            status = U_ZERO_ERROR;
+            sprintf(buffer, "measure-unit/%s per-measure-unit/%s",
+                    simpleMeasureUnits[i], simpleMeasureUnits[j]);
+            int32_t outputlen = 0;
+            u_strFromUTF8(ubuffer, BUFFER_LEN, &outputlen, buffer, strlen(buffer), &status);
+            if (U_FAILURE(status)) {
+                log_err("FAIL u_strFromUTF8: %s = %s ( %s )\n", locale, buffer,
+                        u_errorName(status));
+            }
+            UNumberFormatter* nf = unumf_openForSkeletonAndLocale(
+                ubuffer, outputlen, locale, &status);
+            if (U_FAILURE(status)) {
+                log_err("FAIL unumf_openForSkeletonAndLocale: %s = %s ( %s )\n",
+                        locale, buffer, u_errorName(status));
+            } else {
+                unumf_formatDouble(nf, 1, formatted, &status);
+                if (U_FAILURE(status)) {
+                    log_err("FAIL unumf_formatDouble: %s = %s ( %s )\n",
+                            locale, buffer, u_errorName(status));
+                }
+            }
+            unumf_close(nf);
+        }
+    }
+    unumf_closeResult(formatted);
+}
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/intltest/dtifmtts.cpp b/icu4c/source/test/intltest/dtifmtts.cpp
index 2f59c19..9713dee 100644
--- a/icu4c/source/test/intltest/dtifmtts.cpp
+++ b/icu4c/source/test/intltest/dtifmtts.cpp
@@ -1943,6 +1943,9 @@
         int32_t j = 0;
         for (const UnicodeString skeleton : {u"hh", u"HH", u"kk", u"KK", u"jj", u"JJs", u"CC"}) {
             LocalPointer<DateIntervalFormat> dtifmt(DateIntervalFormat::createInstance(skeleton, locale, status));
+            if (status.errDataIfFailureAndReset()) {
+                continue;
+            }
             FieldPosition fposition;
             UnicodeString result;
             LocalPointer<Calendar> calendar(Calendar::createInstance(TimeZone::createTimeZone(timeZone), status));
diff --git a/icu4c/source/test/intltest/localematchertest.cpp b/icu4c/source/test/intltest/localematchertest.cpp
index 6d7f48d..683466b 100644
--- a/icu4c/source/test/intltest/localematchertest.cpp
+++ b/icu4c/source/test/intltest/localematchertest.cpp
@@ -6,6 +6,7 @@
 
 #include <string>
 #include <vector>
+#include <utility>
 
 #include "unicode/utypes.h"
 #include "unicode/localematcher.h"
@@ -333,7 +334,9 @@
     {
         // arz is a close one-way match to ar, and the region matches.
         // (Egyptian Arabic vs. Arabic)
-        LocaleMatcher withOneWay = builder.build(errorCode);
+        // Also explicitly exercise the move copy constructor.
+        LocaleMatcher built = builder.build(errorCode);
+        LocaleMatcher withOneWay(std::move(built));
         Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
         assertEquals("with one-way", "ar",
                      locString(withOneWay.getBestMatch(desiredIter, errorCode)));
@@ -341,8 +344,11 @@
     {
         // nb is a less close two-way match to nn, and the regions differ.
         // (Norwegian Bokmal vs. Nynorsk)
-        LocaleMatcher onlyTwoWay =
+        // Also explicitly exercise the move assignment operator.
+        LocaleMatcher onlyTwoWay = builder.build(errorCode);
+        LocaleMatcher built =
             builder.setDirection(ULOCMATCH_DIRECTION_ONLY_TWO_WAY).build(errorCode);
+        onlyTwoWay = std::move(built);
         Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
         assertEquals("only two-way", "nn",
                      locString(onlyTwoWay.getBestMatch(desiredIter, errorCode)));
diff --git a/icu4c/source/test/intltest/measfmttest.cpp b/icu4c/source/test/intltest/measfmttest.cpp
index 6f6c14c..51a85fe 100644
--- a/icu4c/source/test/intltest/measfmttest.cpp
+++ b/icu4c/source/test/intltest/measfmttest.cpp
@@ -79,9 +79,11 @@
     void Test20332_PersonUnits();
     void TestNumericTime();
     void TestNumericTimeSomeSpecialFormats();
+    void TestIdentifiers();
     void TestInvalidIdentifiers();
     void TestCompoundUnitOperations();
-    void TestIdentifiers();
+    void TestDimensionlessBehaviour();
+    void Test21060_AddressSanitizerProblem();
 
     void verifyFormat(
         const char *description,
@@ -201,9 +203,11 @@
     TESTCASE_AUTO(Test20332_PersonUnits);
     TESTCASE_AUTO(TestNumericTime);
     TESTCASE_AUTO(TestNumericTimeSomeSpecialFormats);
+    TESTCASE_AUTO(TestIdentifiers);
     TESTCASE_AUTO(TestInvalidIdentifiers);
     TESTCASE_AUTO(TestCompoundUnitOperations);
-    TESTCASE_AUTO(TestIdentifiers);
+    TESTCASE_AUTO(TestDimensionlessBehaviour);
+    TESTCASE_AUTO(Test21060_AddressSanitizerProblem);
     TESTCASE_AUTO_END;
 }
 
@@ -3237,10 +3241,43 @@
     verifyFormat("Danish fhoursFminutes", fmtDa, fhoursFminutes, 2, "2.03,877");
 }
 
+void MeasureFormatTest::TestIdentifiers() {
+    IcuTestErrorCode status(*this, "TestIdentifiers");
+    struct TestCase {
+        const char* id;
+        const char* normalized;
+    } cases[] = {
+        // Correctly normalized identifiers should not change
+        {"", ""},
+        {"square-meter-per-square-meter", "square-meter-per-square-meter"},
+        {"kilogram-meter-per-square-meter-square-second",
+         "kilogram-meter-per-square-meter-square-second"},
+        {"square-mile-and-square-foot", "square-mile-and-square-foot"},
+        {"square-foot-and-square-mile", "square-foot-and-square-mile"},
+        {"per-cubic-centimeter", "per-cubic-centimeter"},
+        {"per-kilometer", "per-kilometer"},
+
+        // Normalization of power and per
+        {"p2-foot-and-p2-mile", "square-foot-and-square-mile"},
+        {"gram-square-gram-per-dekagram", "cubic-gram-per-dekagram"},
+        {"kilogram-per-meter-per-second", "kilogram-per-meter-second"},
+
+        // TODO(ICU-20920): Add more test cases once the proper ranking is available.
+    };
+    for (const auto &cas : cases) {
+        status.setScope(cas.id);
+        MeasureUnit unit = MeasureUnit::forIdentifier(cas.id, status);
+        status.errIfFailureAndReset();
+        const char* actual = unit.getIdentifier();
+        assertEquals(cas.id, cas.normalized, actual);
+        status.errIfFailureAndReset();
+    }
+}
+
 void MeasureFormatTest::TestInvalidIdentifiers() {
     IcuTestErrorCode status(*this, "TestInvalidIdentifiers");
 
-    const char* const inputs[] = {
+    const char *const inputs[] = {
         "kilo",
         "kilokilo",
         "onekilo",
@@ -3256,7 +3293,23 @@
         "-p2-meter",
         "+p2-meter",
         "+",
-        "-"
+        "-",
+        "-mile",
+        "-and-mile",
+        "-per-mile",
+        "one",
+        "one-one",
+        "one-per-mile",
+        "one-per-cubic-centimeter",
+        "square--per-meter",
+        "metersecond", // Must have compound part in between single units
+
+        // Negative powers not supported in mixed units yet. TODO(CLDR-13701).
+        "per-hour-and-hertz",
+        "hertz-and-per-hour",
+
+        // Compound units not supported in mixed units yet. TODO(CLDR-13700).
+        "kilonewton-meter-and-newton-meter",
     };
 
     for (const auto& input : inputs) {
@@ -3293,9 +3346,9 @@
     MeasureUnit overQuarticKilometer1 = kilometer.withDimensionality(-4, status);
 
     verifySingleUnit(squareMeter, UMEASURE_SI_PREFIX_ONE, 2, "square-meter");
-    verifySingleUnit(overCubicCentimeter, UMEASURE_SI_PREFIX_CENTI, -3, "one-per-cubic-centimeter");
+    verifySingleUnit(overCubicCentimeter, UMEASURE_SI_PREFIX_CENTI, -3, "per-cubic-centimeter");
     verifySingleUnit(quarticKilometer, UMEASURE_SI_PREFIX_KILO, 4, "p4-kilometer");
-    verifySingleUnit(overQuarticKilometer1, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
+    verifySingleUnit(overQuarticKilometer1, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
 
     assertTrue("power inequality", quarticKilometer != overQuarticKilometer1);
 
@@ -3308,9 +3361,9 @@
         .reciprocal(status)
         .withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
 
-    verifySingleUnit(overQuarticKilometer2, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
-    verifySingleUnit(overQuarticKilometer3, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
-    verifySingleUnit(overQuarticKilometer4, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
+    verifySingleUnit(overQuarticKilometer2, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
+    verifySingleUnit(overQuarticKilometer3, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
+    verifySingleUnit(overQuarticKilometer4, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
 
     assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer2);
     assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer3);
@@ -3341,7 +3394,7 @@
     const char* secondCentimeterSub[] = {"centimeter", "square-kilosecond"};
     verifyCompoundUnit(secondCentimeter, "centimeter-square-kilosecond",
         secondCentimeterSub, UPRV_LENGTHOF(secondCentimeterSub));
-    const char* secondCentimeterPerKilometerSub[] = {"centimeter", "square-kilosecond", "one-per-kilometer"};
+    const char* secondCentimeterPerKilometerSub[] = {"centimeter", "square-kilosecond", "per-kilometer"};
     verifyCompoundUnit(secondCentimeterPerKilometer, "centimeter-square-kilosecond-per-kilometer",
         secondCentimeterPerKilometerSub, UPRV_LENGTHOF(secondCentimeterPerKilometerSub));
 
@@ -3376,31 +3429,16 @@
 
     assertTrue("order matters inequality", footInch != inchFoot);
 
-    MeasureUnit one1;
-    MeasureUnit one2 = MeasureUnit::forIdentifier("one", status);
-    MeasureUnit one3 = MeasureUnit::forIdentifier("", status);
-    MeasureUnit squareOne = one2.withDimensionality(2, status);
-    MeasureUnit onePerOne = one2.reciprocal(status);
-    MeasureUnit squareKiloOne = squareOne.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
-    MeasureUnit onePerSquareKiloOne = squareKiloOne.reciprocal(status);
-    MeasureUnit oneOne = MeasureUnit::forIdentifier("one-one", status);
-    MeasureUnit onePlusOne = MeasureUnit::forIdentifier("one-and-one", status);
-    MeasureUnit kilometer2 = one2.product(kilometer, status);
+    MeasureUnit dimensionless;
+    MeasureUnit dimensionless2 = MeasureUnit::forIdentifier("", status);
+    status.errIfFailureAndReset("Dimensionless MeasureUnit.");
+    assertTrue("dimensionless equality", dimensionless == dimensionless2);
 
-    verifySingleUnit(one1, UMEASURE_SI_PREFIX_ONE, 1, "one");
-    verifySingleUnit(one2, UMEASURE_SI_PREFIX_ONE, 1, "one");
-    verifySingleUnit(one3, UMEASURE_SI_PREFIX_ONE, 1, "one");
-    verifySingleUnit(squareOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
-    verifySingleUnit(onePerOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
-    verifySingleUnit(squareKiloOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
-    verifySingleUnit(onePerSquareKiloOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
-    verifySingleUnit(oneOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
-    verifySingleUnit(onePlusOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
+    // We support starting from an "identity" MeasureUnit and then combining it
+    // with others via product:
+    MeasureUnit kilometer2 = dimensionless.product(kilometer, status);
+    status.errIfFailureAndReset("dimensionless.product(kilometer, status)");
     verifySingleUnit(kilometer2, UMEASURE_SI_PREFIX_KILO, 1, "kilometer");
-
-    assertTrue("one equality", one1 == one2);
-    assertTrue("one equality", one2 == one3);
-    assertTrue("one-per-one equality", onePerOne == onePerSquareKiloOne);
     assertTrue("kilometer equality", kilometer == kilometer2);
 
     // Test out-of-range powers
@@ -3411,36 +3449,102 @@
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
     MeasureUnit power16b = power15.product(kilometer, status);
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
-    MeasureUnit powerN15 = MeasureUnit::forIdentifier("one-per-p15-kilometer", status);
-    verifySingleUnit(powerN15, UMEASURE_SI_PREFIX_KILO, -15, "one-per-p15-kilometer");
+    MeasureUnit powerN15 = MeasureUnit::forIdentifier("per-p15-kilometer", status);
+    verifySingleUnit(powerN15, UMEASURE_SI_PREFIX_KILO, -15, "per-p15-kilometer");
     status.errIfFailureAndReset();
-    MeasureUnit powerN16a = MeasureUnit::forIdentifier("one-per-p16-kilometer", status);
+    MeasureUnit powerN16a = MeasureUnit::forIdentifier("per-p16-kilometer", status);
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
     MeasureUnit powerN16b = powerN15.product(overQuarticKilometer1, status);
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
 }
 
-void MeasureFormatTest::TestIdentifiers() {
-    IcuTestErrorCode status(*this, "TestIdentifiers");
-    struct TestCase {
-        bool valid;
-        const char* id;
-        const char* normalized;
-    } cases[] = {
-        { true, "square-meter-per-square-meter", "square-meter-per-square-meter" },
-        // TODO(ICU-20920): Add more test cases once the proper ranking is available.
-    };
-    for (const auto& cas : cases) {
-        status.setScope(cas.id);
-        MeasureUnit unit = MeasureUnit::forIdentifier(cas.id, status);
-        if (!cas.valid) {
-            status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
-            continue;
-        }
-        const char* actual = unit.getIdentifier();
-        assertEquals(cas.id, cas.normalized, actual);
-        status.errIfFailureAndReset();
-    }
+void MeasureFormatTest::TestDimensionlessBehaviour() {
+    IcuTestErrorCode status(*this, "TestDimensionlessBehaviour");
+    MeasureUnit dimensionless;
+    MeasureUnit modified;
+
+    // At the time of writing, each of the seven groups below caused
+    // Parser::from("") to be called:
+
+    // splitToSingleUnits
+    int32_t count;
+    LocalArray<MeasureUnit> singles = dimensionless.splitToSingleUnits(count, status);
+    status.errIfFailureAndReset("dimensionless.splitToSingleUnits(...)");
+    assertEquals("no singles in dimensionless", 0, count);
+
+    // product(dimensionless)
+    MeasureUnit mile = MeasureUnit::getMile();
+    mile = mile.product(dimensionless, status);
+    status.errIfFailureAndReset("mile.product(dimensionless, ...)");
+    verifySingleUnit(mile, UMEASURE_SI_PREFIX_ONE, 1, "mile");
+
+    // dimensionless.getSIPrefix()
+    UMeasureSIPrefix siPrefix = dimensionless.getSIPrefix(status);
+    status.errIfFailureAndReset("dimensionless.getSIPrefix(...)");
+    assertEquals("dimensionless SIPrefix", UMEASURE_SI_PREFIX_ONE, siPrefix);
+
+    // dimensionless.withSIPrefix()
+    modified = dimensionless.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
+    status.errIfFailureAndReset("dimensionless.withSIPrefix(...)");
+    singles = modified.splitToSingleUnits(count, status);
+    assertEquals("no singles in modified", 0, count);
+    siPrefix = modified.getSIPrefix(status);
+    status.errIfFailureAndReset("modified.getSIPrefix(...)");
+    assertEquals("modified SIPrefix", UMEASURE_SI_PREFIX_ONE, siPrefix);
+
+    // dimensionless.getComplexity()
+    UMeasureUnitComplexity complexity = dimensionless.getComplexity(status);
+    status.errIfFailureAndReset("dimensionless.getComplexity(...)");
+    assertEquals("dimensionless complexity", UMEASURE_UNIT_SINGLE, complexity);
+
+    // Dimensionality is mostly meaningless for dimensionless units, but it's
+    // still considered a SINGLE unit, so this code doesn't throw errors:
+
+    // dimensionless.getDimensionality()
+    int32_t dimensionality = dimensionless.getDimensionality(status);
+    status.errIfFailureAndReset("dimensionless.getDimensionality(...)");
+    assertEquals("dimensionless dimensionality", 0, dimensionality);
+
+    // dimensionless.withDimensionality()
+    dimensionless.withDimensionality(-1, status);
+    status.errIfFailureAndReset("dimensionless.withDimensionality(...)");
+    dimensionality = dimensionless.getDimensionality(status);
+    status.errIfFailureAndReset("dimensionless.getDimensionality(...)");
+    assertEquals("dimensionless dimensionality", 0, dimensionality);
+}
+
+// ICU-21060
+void MeasureFormatTest::Test21060_AddressSanitizerProblem() {
+    IcuTestErrorCode status(*this, "Test21060_AddressSanitizerProblem");
+
+    MeasureUnit first = MeasureUnit::forIdentifier("", status);
+    status.errIfFailureAndReset();
+
+    // Experimentally, a compound unit like "kilogram-meter" failed. A single
+    // unit like "kilogram" or "meter" did not fail, did not trigger the
+    // problem.
+    MeasureUnit crux = MeasureUnit::forIdentifier("per-meter", status);
+
+    // Heap allocation of a new CharString for first.identifier happens here:
+    first = first.product(crux, status);
+
+    // Constructing second from first's identifier resulted in a failure later,
+    // as second held a reference to a substring of first's identifier:
+    MeasureUnit second = MeasureUnit::forIdentifier(first.getIdentifier(), status);
+
+    // Heap is freed here, as an old first.identifier CharString is deallocated
+    // and a new CharString is allocated:
+    first = first.product(crux, status);
+
+    // Proving we've had no failure yet:
+    status.errIfFailureAndReset();
+
+    // heap-use-after-free failure happened here, since a SingleUnitImpl had
+    // held onto a StringPiece pointing at a substring of an identifier that was
+    // freed above:
+    second = second.product(crux, status);
+
+    status.errIfFailureAndReset();
 }
 
 
diff --git a/icu4c/source/test/intltest/numbertest.h b/icu4c/source/test/intltest/numbertest.h
index 2597aa8..cb0be28 100644
--- a/icu4c/source/test/intltest/numbertest.h
+++ b/icu4c/source/test/intltest/numbertest.h
@@ -258,6 +258,7 @@
     void defaultTokens();
     void flexibleSeparators();
     void wildcardCharacters();
+    void perUnitInArabic();
 
     void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
 
diff --git a/icu4c/source/test/intltest/numbertest_skeletons.cpp b/icu4c/source/test/intltest/numbertest_skeletons.cpp
index 3aad601..d5e4bcf 100644
--- a/icu4c/source/test/intltest/numbertest_skeletons.cpp
+++ b/icu4c/source/test/intltest/numbertest_skeletons.cpp
@@ -30,6 +30,7 @@
         TESTCASE_AUTO(defaultTokens);
         TESTCASE_AUTO(flexibleSeparators);
         TESTCASE_AUTO(wildcardCharacters);
+        TESTCASE_AUTO(perUnitInArabic);
     TESTCASE_AUTO_END;
 }
 
@@ -362,5 +363,77 @@
     }
 }
 
+void NumberSkeletonTest::perUnitInArabic() {
+    IcuTestErrorCode status(*this, "perUnitInArabic");
+
+    struct TestCase {
+        const char16_t* type;
+        const char16_t* subtype;
+    } cases[] = {
+        {u"area", u"acre"},
+        {u"digital", u"bit"},
+        {u"digital", u"byte"},
+        {u"temperature", u"celsius"},
+        {u"length", u"centimeter"},
+        {u"duration", u"day"},
+        {u"angle", u"degree"},
+        {u"temperature", u"fahrenheit"},
+        {u"volume", u"fluid-ounce"},
+        {u"length", u"foot"},
+        {u"volume", u"gallon"},
+        {u"digital", u"gigabit"},
+        {u"digital", u"gigabyte"},
+        {u"mass", u"gram"},
+        {u"area", u"hectare"},
+        {u"duration", u"hour"},
+        {u"length", u"inch"},
+        {u"digital", u"kilobit"},
+        {u"digital", u"kilobyte"},
+        {u"mass", u"kilogram"},
+        {u"length", u"kilometer"},
+        {u"volume", u"liter"},
+        {u"digital", u"megabit"},
+        {u"digital", u"megabyte"},
+        {u"length", u"meter"},
+        {u"length", u"mile"},
+        {u"length", u"mile-scandinavian"},
+        {u"volume", u"milliliter"},
+        {u"length", u"millimeter"},
+        {u"duration", u"millisecond"},
+        {u"duration", u"minute"},
+        {u"duration", u"month"},
+        {u"mass", u"ounce"},
+        {u"concentr", u"percent"},
+        {u"digital", u"petabyte"},
+        {u"mass", u"pound"},
+        {u"duration", u"second"},
+        {u"mass", u"stone"},
+        {u"digital", u"terabit"},
+        {u"digital", u"terabyte"},
+        {u"duration", u"week"},
+        {u"length", u"yard"},
+        {u"duration", u"year"},
+    };
+
+    for (const auto& cas1 : cases) {
+        for (const auto& cas2 : cases) {
+            UnicodeString skeleton(u"measure-unit/");
+            skeleton += cas1.type;
+            skeleton += u"-";
+            skeleton += cas1.subtype;
+            skeleton += u" ";
+            skeleton += u"per-measure-unit/";
+            skeleton += cas2.type;
+            skeleton += u"-";
+            skeleton += cas2.subtype;
+
+            status.setScope(skeleton);
+            UnicodeString actual = NumberFormatter::forSkeleton(skeleton, status).locale("ar")
+                                   .formatDouble(5142.3, status)
+                                   .toString(status);
+            status.errIfFailureAndReset();
+        }
+    }
+}
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp
index 62e161f..5a26a7a 100644
--- a/icu4c/source/test/intltest/numfmtst.cpp
+++ b/icu4c/source/test/intltest/numfmtst.cpp
@@ -9679,6 +9679,9 @@
     IcuTestErrorCode status(*this, "Test20956_MonetarySymbolGetters");
     LocalPointer<DecimalFormat> decimalFormat(static_cast<DecimalFormat*>(
         NumberFormat::createCurrencyInstance("et", status)));
+    if (status.errDataIfFailureAndReset()) {
+        return;
+    }
 
     decimalFormat->setCurrency(u"EEK");
 
@@ -9823,6 +9826,9 @@
     {
         LocalPointer<DecimalFormat> decimalFormat(static_cast<DecimalFormat*>(
             NumberFormat::createInstance("en-US", UNUM_CURRENCY_PLURAL, status)));
+        if (status.errDataIfFailureAndReset()) {
+            return;
+        }
         UnicodeString result;
         decimalFormat->toPattern(result);
         assertEquals("Currency pattern", u"#,##0.00 ¤¤¤", result);
diff --git a/icu4c/source/test/intltest/restsnew.cpp b/icu4c/source/test/intltest/restsnew.cpp
index b2d72d9..482e241 100644
--- a/icu4c/source/test/intltest/restsnew.cpp
+++ b/icu4c/source/test/intltest/restsnew.cpp
@@ -1395,6 +1395,14 @@
     }
 }
 
+/*
+ * The following test for ICU-20706 has infinite loops on certain inputs for
+ * locales and calendars.  In order to unblock the build (ICU-21055), those
+ * specific values are temporarily removed.
+ * The issue of the infinite loops and its blocking dependencies were captured
+ * in ICU-21080.
+ */
+
 void NewResourceBundleTest::TestIntervalAliasFallbacks() {
     const char* locales[] = {
         // Thee will not cause infinity loop
@@ -1402,6 +1410,7 @@
         "ja",
 
         // These will cause infinity loop
+#if 0
         "fr_CA",
         "en_150",
         "es_419",
@@ -1413,6 +1422,7 @@
         "zh_Hant",
         "zh_Hant_TW",
         "zh_TW",
+#endif
     };
     const char* calendars[] = {
         // These won't cause infinity loop
@@ -1420,6 +1430,7 @@
         "chinese",
 
         // These will cause infinity loop
+#if 0
         "islamic",
         "islamic-civil",
         "islamic-tbla",
@@ -1428,6 +1439,7 @@
         "islamic-rgsa",
         "japanese",
         "roc",
+#endif
     };
 
     for (int lidx = 0; lidx < UPRV_LENGTHOF(locales); lidx++) {
diff --git a/icu4c/source/test/intltest/restsnew.h b/icu4c/source/test/intltest/restsnew.h
index d3b2d9c..45cc930 100644
--- a/icu4c/source/test/intltest/restsnew.h
+++ b/icu4c/source/test/intltest/restsnew.h
@@ -39,6 +39,7 @@
     void TestGetByFallback(void);
 
     void TestFilter(void);
+
     void TestIntervalAliasFallbacks(void);
 
 #if U_ENABLE_TRACING
diff --git a/icu4c/source/test/intltest/transtst.cpp b/icu4c/source/test/intltest/transtst.cpp
index fd7f733..8e7bcb0 100644
--- a/icu4c/source/test/intltest/transtst.cpp
+++ b/icu4c/source/test/intltest/transtst.cpp
@@ -1562,6 +1562,7 @@
             BASIC_TRANSLITERATOR_ID[i], UTRANS_FORWARD, parseError, status));
         if (translit.get() == nullptr || !U_SUCCESS(status)) {
             dataerrln("FAIL: createInstance %s failed", BASIC_TRANSLITERATOR_ID[i]);
+            continue;
         }
         UnicodeString data(TEST_DATA);
         UnicodeString expected(EXPECTED_RESULTS[i]);
@@ -1570,6 +1571,7 @@
             dataerrln(UnicodeString("FAIL: expected translit(") +
                       BASIC_TRANSLITERATOR_ID[i] + ") = '" +
                       EXPECTED_RESULTS[i] + "' but got '" + data);
+            continue;
         }
     }
     for (int32_t i=0; BASIC_TRANSLITERATOR_RULES[i]; i++) {
@@ -1580,6 +1582,7 @@
             BASIC_TRANSLITERATOR_RULES[i], UTRANS_FORWARD, parseError, status));
         if (translit.get() == nullptr || !U_SUCCESS(status)) {
             dataerrln("FAIL: createFromRules %s failed", BASIC_TRANSLITERATOR_RULES[i]);
+            continue;
         }
     }
 }
diff --git a/icu4j/build.properties b/icu4j/build.properties
index 61b6d0b..10f87c9 100644
--- a/icu4j/build.properties
+++ b/icu4j/build.properties
@@ -6,7 +6,7 @@
 #*******************************************************************************
 api.report.version = 67
 api.report.prev.version = 66
-release.file.ver = 67rc
-api.doc.version = 67 Release Candidate
-maven.pom.ver = 67.1-SNAPSHOT
+release.file.ver = 67_1
+api.doc.version = 67.1
+maven.pom.ver = 67.1
 
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java
index 459e514..4f58c66 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java
@@ -241,8 +241,10 @@
             String compiled = SimpleFormatterImpl
                     .compileToStringMinMaxArguments(rawPerUnitFormat, sb, 2, 2);
             String secondaryFormat = getWithPlural(secondaryData, StandardPlural.ONE);
+
+            // Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale.
             String secondaryCompiled = SimpleFormatterImpl
-                    .compileToStringMinMaxArguments(secondaryFormat, sb, 1, 1);
+                    .compileToStringMinMaxArguments(secondaryFormat, sb, 0, 1);
             String secondaryString = SimpleFormatterImpl.getTextWithNoArguments(secondaryCompiled)
                     .trim();
             perUnitFormat = SimpleFormatterImpl.formatCompiledPattern(compiled, "{0}", secondaryString);
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java
index 03caedc..db6ee5e 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java
@@ -349,4 +349,65 @@
             assertEquals(mode.toString(), modeString, skeleton.substring(14));
         }
     }
+
+    @Test
+    public void perUnitInArabic() {
+        String[][] cases = {
+                {"area", "acre"},
+                {"digital", "bit"},
+                {"digital", "byte"},
+                {"temperature", "celsius"},
+                {"length", "centimeter"},
+                {"duration", "day"},
+                {"angle", "degree"},
+                {"temperature", "fahrenheit"},
+                {"volume", "fluid-ounce"},
+                {"length", "foot"},
+                {"volume", "gallon"},
+                {"digital", "gigabit"},
+                {"digital", "gigabyte"},
+                {"mass", "gram"},
+                {"area", "hectare"},
+                {"duration", "hour"},
+                {"length", "inch"},
+                {"digital", "kilobit"},
+                {"digital", "kilobyte"},
+                {"mass", "kilogram"},
+                {"length", "kilometer"},
+                {"volume", "liter"},
+                {"digital", "megabit"},
+                {"digital", "megabyte"},
+                {"length", "meter"},
+                {"length", "mile"},
+                {"length", "mile-scandinavian"},
+                {"volume", "milliliter"},
+                {"length", "millimeter"},
+                {"duration", "millisecond"},
+                {"duration", "minute"},
+                {"duration", "month"},
+                {"mass", "ounce"},
+                {"concentr", "percent"},
+                {"digital", "petabyte"},
+                {"mass", "pound"},
+                {"duration", "second"},
+                {"mass", "stone"},
+                {"digital", "terabit"},
+                {"digital", "terabyte"},
+                {"duration", "week"},
+                {"length", "yard"},
+                {"duration", "year"},
+        };
+
+        ULocale arabic = new ULocale("ar");
+        for (String[] cas1 : cases) {
+            for (String[] cas2 : cases) {
+                String skeleton = "measure-unit/";
+                skeleton += cas1[0] + "-" + cas1[1] + " per-measure-unit/" + cas2[0] + "-" + cas2[1];
+
+                String actual = NumberFormatter.forSkeleton(skeleton).locale(arabic).format(5142.3)
+                        .toString();
+                // Just make sure it won't throw exception
+            }
+        }
+    }
 }
diff --git a/vendor/double-conversion/upstream/double-conversion/utils.h b/vendor/double-conversion/upstream/double-conversion/utils.h
index 471c3da..438d055 100644
--- a/vendor/double-conversion/upstream/double-conversion/utils.h
+++ b/vendor/double-conversion/upstream/double-conversion/utils.h
@@ -56,15 +56,23 @@
 #endif
 #endif
 
+// Not all compilers support __has_attribute and combining a check for both
+// ifdef and __has_attribute on the same preprocessor line isn't portable.
+#ifdef __has_attribute
+#   define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) __has_attribute(x)
+#else
+#   define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) 0
+#endif
+
 #ifndef DOUBLE_CONVERSION_UNUSED
-#ifdef __GNUC__
+#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(unused)
 #define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
 #else
 #define DOUBLE_CONVERSION_UNUSED
 #endif
 #endif
 
-#if defined(__clang__) && __has_attribute(uninitialized)
+#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(uninitialized)
 #define DOUBLE_CONVERSION_STACK_UNINITIALIZED __attribute__((uninitialized))
 #else
 #define DOUBLE_CONVERSION_STACK_UNINITIALIZED
diff --git a/vendor/double-conversion/upstream/msvc/double-conversion.vcxproj b/vendor/double-conversion/upstream/msvc/double-conversion.vcxproj
index 5616c8a..e2d2ef8 100644
--- a/vendor/double-conversion/upstream/msvc/double-conversion.vcxproj
+++ b/vendor/double-conversion/upstream/msvc/double-conversion.vcxproj
@@ -147,24 +147,25 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
-    <ClCompile Include="..\double-conversion\bignum-dtoa.cc" />
     <ClCompile Include="..\double-conversion\bignum.cc" />
+    <ClCompile Include="..\double-conversion\bignum-dtoa.cc" />
     <ClCompile Include="..\double-conversion\cached-powers.cc" />
-    <ClCompile Include="..\double-conversion\diy-fp.cc" />
-    <ClCompile Include="..\double-conversion\double-conversion.cc" />
+    <ClCompile Include="..\double-conversion\double-to-string.cc" />
     <ClCompile Include="..\double-conversion\fast-dtoa.cc" />
     <ClCompile Include="..\double-conversion\fixed-dtoa.cc" />
+    <ClCompile Include="..\double-conversion\string-to-double.cc" />
     <ClCompile Include="..\double-conversion\strtod.cc" />
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="..\double-conversion\bignum-dtoa.h" />
     <ClInclude Include="..\double-conversion\bignum.h" />
     <ClInclude Include="..\double-conversion\cached-powers.h" />
     <ClInclude Include="..\double-conversion\diy-fp.h" />
     <ClInclude Include="..\double-conversion\double-conversion.h" />
+    <ClInclude Include="..\double-conversion\double-to-string.h" />
     <ClInclude Include="..\double-conversion\fast-dtoa.h" />
     <ClInclude Include="..\double-conversion\fixed-dtoa.h" />
     <ClInclude Include="..\double-conversion\ieee.h" />
+    <ClInclude Include="..\double-conversion\string-to-double.h" />
     <ClInclude Include="..\double-conversion\strtod.h" />
     <ClInclude Include="..\double-conversion\utils.h" />
   </ItemGroup>
diff --git a/vendor/double-conversion/upstream/msvc/double-conversion.vcxproj.filters b/vendor/double-conversion/upstream/msvc/double-conversion.vcxproj.filters
index 664a27f..cebae94 100644
--- a/vendor/double-conversion/upstream/msvc/double-conversion.vcxproj.filters
+++ b/vendor/double-conversion/upstream/msvc/double-conversion.vcxproj.filters
@@ -24,12 +24,6 @@
     <ClCompile Include="..\double-conversion\cached-powers.cc">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="..\double-conversion\diy-fp.cc">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="..\double-conversion\double-conversion.cc">
-      <Filter>Source Files</Filter>
-    </ClCompile>
     <ClCompile Include="..\double-conversion\fast-dtoa.cc">
       <Filter>Source Files</Filter>
     </ClCompile>
@@ -39,14 +33,17 @@
     <ClCompile Include="..\double-conversion\strtod.cc">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\double-conversion\double-to-string.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\double-conversion\string-to-double.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\double-conversion\bignum.h">
       <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="..\double-conversion\bignum-dtoa.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
     <ClInclude Include="..\double-conversion\cached-powers.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -71,5 +68,11 @@
     <ClInclude Include="..\double-conversion\utils.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\double-conversion\double-to-string.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\double-conversion\string-to-double.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/vendor/double-conversion/upstream/msvc/run_tests/run_tests.vcxproj b/vendor/double-conversion/upstream/msvc/run_tests/run_tests.vcxproj
index 05d2873..1cb0d36 100644
--- a/vendor/double-conversion/upstream/msvc/run_tests/run_tests.vcxproj
+++ b/vendor/double-conversion/upstream/msvc/run_tests/run_tests.vcxproj
@@ -109,6 +109,7 @@
       <PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>false</SDLCheck>
       <AdditionalIncludeDirectories>$(SolutionDir)..</AdditionalIncludeDirectories>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>