ICU-21984 Fix DateIntervalFormat.normalizeHourMetacharacters() so that it doesn't require the hour and day-period
fields to appear in any particular order or position in the skeleton string.
diff --git a/icu4c/source/i18n/dtitvfmt.cpp b/icu4c/source/i18n/dtitvfmt.cpp
index d51ddcd..df9d23b 100644
--- a/icu4c/source/i18n/dtitvfmt.cpp
+++ b/icu4c/source/i18n/dtitvfmt.cpp
@@ -966,23 +966,26 @@
     
     UChar hourMetachar = u'\0';
     UChar dayPeriodChar = u'\0';
-    int32_t metacharStart = 0;
-    int32_t metacharCount = 0;
+    int32_t hourFieldStart = 0;
+    int32_t hourFieldLength = 0;
+    int32_t dayPeriodStart = 0;
+    int32_t dayPeriodLength = 0;
     for (int32_t i = 0; i < result.length(); i++) {
         UChar c = result[i];
         if (c == LOW_J || c == CAP_J || c == CAP_C || c == LOW_H || c == CAP_H || c == LOW_K || c == CAP_K) {
             if (hourMetachar == u'\0') {
                 hourMetachar = c;
-                metacharStart = i;
+                hourFieldStart = i;
             }
-            ++metacharCount;
+            ++hourFieldLength;
         } else if (c == LOW_A || c == LOW_B || c == CAP_B) {
             if (dayPeriodChar == u'\0') {
                 dayPeriodChar = c;
+                dayPeriodStart = i;
             }
-            ++metacharCount;
+            ++dayPeriodLength;
         } else {
-            if (hourMetachar != u'\0') {
+            if (hourMetachar != u'\0' && dayPeriodChar != u'\0') {
                 break;
             }
         }
@@ -1022,31 +1025,27 @@
             }
         }
         
-        if (hourChar == CAP_H || hourChar == LOW_K) {
-            result.replace(metacharStart, metacharCount, hourChar);
-        } else {
-            UnicodeString hourAndDayPeriod(hourChar);
-            switch (metacharCount) {
-                case 1:
-                case 2:
-                default:
-                    hourAndDayPeriod.append(UnicodeString(dayPeriodChar));
-                    break;
-                case 3:
-                case 4:
-                    for (int32_t i = 0; i < 4; i++) {
-                        hourAndDayPeriod.append(dayPeriodChar);
-                    }
-                    break;
-                case 5:
-                case 6:
-                    for (int32_t i = 0; i < 5; i++) {
-                        hourAndDayPeriod.append(dayPeriodChar);
-                    }
-                    break;
+        UnicodeString hourAndDayPeriod(hourChar);
+        if (hourChar != CAP_H && hourChar != LOW_K) {
+            int32_t newDayPeriodLength = 0;
+            if (dayPeriodLength >= 5 || hourFieldLength >= 5) {
+                newDayPeriodLength = 5;
+            } else if (dayPeriodLength >= 3 || hourFieldLength >= 3) {
+                newDayPeriodLength = 3;
+            } else {
+                newDayPeriodLength = 1;
             }
-            result.replace(metacharStart, metacharCount, hourAndDayPeriod);
+            for (int32_t i = 0; i < newDayPeriodLength; i++) {
+                hourAndDayPeriod.append(dayPeriodChar);
+            }
         }
+        result.replace(hourFieldStart, hourFieldLength, hourAndDayPeriod);
+        if (dayPeriodStart > hourFieldStart) {
+            // before deleting the original day period field, adjust its position in case
+            // we just changed the size of the hour field (and new day period field)
+            dayPeriodStart += hourAndDayPeriod.length() - hourFieldLength;
+        }
+        result.remove(dayPeriodStart, dayPeriodLength);
     }
     return result;
 }
diff --git a/icu4c/source/test/intltest/dtifmtts.cpp b/icu4c/source/test/intltest/dtifmtts.cpp
index 605eae4..f277443 100644
--- a/icu4c/source/test/intltest/dtifmtts.cpp
+++ b/icu4c/source/test/intltest/dtifmtts.cpp
@@ -1194,6 +1194,10 @@
         "en", "CE 2010 09 27 00:00:00", "CE 2010 09 27 01:00:00", "KK", "12 \\u2013 1 AM", // (this was producing "0 - 1 AM" before)
         "en", "CE 2010 09 27 00:00:00", "CE 2010 09 27 00:00:00", "jj", "12 AM",
         "en", "CE 2010 09 27 00:00:00", "CE 2010 09 27 01:00:00", "jj", "12 \\u2013 1 AM",
+        
+        // regression test for ICU-21984 (multiple day-period characters in date-interval patterns)
+        "en", "CE 2010 09 27 00:00:00", "CE 2010 09 27 01:00:00", "MMMdhhmma", "Sep 27, 12:00 \\u2013 1:00 AM",
+        "sq", "CE 2010 09 27 00:00:00", "CE 2010 09 27 01:00:00", "Bhm", "12:00 \\u2013 1:00 e nat\\u00EBs",
     };
     expect(DATA, UPRV_LENGTHOF(DATA));
 }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
index aba183f..53d2b97 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
@@ -1616,23 +1616,26 @@
 
         char hourMetachar = '\0';
         char dayPeriodChar = '\0';
-        int metacharStart = 0;
-        int metacharCount = 0;
+        int hourFieldStart = 0;
+        int hourFieldLength = 0;
+        int dayPeriodStart = 0;
+        int dayPeriodLength = 0;
         for (int i = 0; i < result.length(); i++) {
             char c = result.charAt(i);
             if (c == 'j' || c == 'J' || c == 'C' || c == 'h' || c == 'H' || c == 'k' || c == 'K') {
                 if (hourMetachar == '\0') {
                     hourMetachar = c;
-                    metacharStart = i;
+                    hourFieldStart = i;
                 }
-                ++metacharCount;
+                ++hourFieldLength;
             } else if (c == 'a' || c == 'b' || c == 'B') {
                 if (dayPeriodChar == '\0') {
                     dayPeriodChar = c;
+                    dayPeriodStart = i;
                 }
-                ++metacharCount;
+                ++dayPeriodLength;
             } else {
-                if (hourMetachar != '\0') {
+                if (hourMetachar != '\0' && dayPeriodChar != '\0') {
                     break;
                 }
             }
@@ -1671,32 +1674,26 @@
                 dayPeriodChar = 'a';
             }
 
-            if (hourChar == 'H' || hourChar == 'k') {
-                result.replace(metacharStart, metacharStart + metacharCount, String.valueOf(hourChar));
-            } else {
-                StringBuilder hourAndDayPeriod = new StringBuilder();
-                hourAndDayPeriod.append(hourChar);
-                switch (metacharCount) {
-                    case 1:
-                    case 2:
-                    default:
-                        hourAndDayPeriod.append(dayPeriodChar);
-                        break;
-                    case 3:
-                    case 4:
-                        for (int i = 0; i < 4; i++) {
-                            hourAndDayPeriod.append(dayPeriodChar);
-                        }
-                        break;
-                    case 5:
-                    case 6:
-                        for (int i = 0; i < 5; i++) {
-                            hourAndDayPeriod.append(dayPeriodChar);
-                        }
-                        break;
+            StringBuilder hourAndDayPeriod = new StringBuilder();
+            hourAndDayPeriod.append(hourChar);
+            if (hourChar != 'H' && hourChar != 'k') {
+                int newDayPeriodLength = 0;
+                if (dayPeriodLength >= 5 || hourFieldLength >= 5) {
+                    newDayPeriodLength = 5;
+                } else if (dayPeriodLength >= 3 || hourFieldLength >= 3) {
+                    newDayPeriodLength = 3;
+                } else {
+                    newDayPeriodLength = 1;
                 }
-                result.replace(metacharStart, metacharStart + metacharCount, hourAndDayPeriod.toString());
+                for (int i = 0; i < newDayPeriodLength; i++) {
+                    hourAndDayPeriod.append(dayPeriodChar);
+                }
             }
+            result.replace(hourFieldStart, hourFieldStart + hourFieldLength, hourAndDayPeriod.toString());
+            if (dayPeriodStart > hourFieldStart) {
+                dayPeriodStart += hourAndDayPeriod.length() - hourFieldLength;
+            }
+            result.delete(dayPeriodStart, dayPeriodStart + dayPeriodLength);
         }
         return result.toString();
     }
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
index 7ad08e1..858d933 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
@@ -839,6 +839,10 @@
             "en", "CE 2010 09 27 00:00:00", "CE 2010 09 27 01:00:00", "KK", "12 \\u2013 1 AM", // (this was producing "0 - 1 AM" before)
             "en", "CE 2010 09 27 00:00:00", "CE 2010 09 27 00:00:00", "jj", "12 AM",
             "en", "CE 2010 09 27 00:00:00", "CE 2010 09 27 01:00:00", "jj", "12 \\u2013 1 AM",
+
+            // regression test for ICU-21984 (multiple day-period characters in date-interval patterns)
+            "en", "CE 2010 09 27 00:00:00", "CE 2010 09 27 01:00:00", "MMMdhhmma", "Sep 27, 12:00 \\u2013 1:00 AM",
+            "sq", "CE 2010 09 27 00:00:00", "CE 2010 09 27 01:00:00", "Bhm", "12:00 \\u2013 1:00 e nat\\u00EBs",
         };
         expect(DATA, DATA.length);
     }