ICU-20436 Add getDefaultHourCycle to DateTimePatternGenerator

See #901
diff --git a/icu4c/source/i18n/dtptngen.cpp b/icu4c/source/i18n/dtptngen.cpp
index 4948d2c..09faf5b 100644
--- a/icu4c/source/i18n/dtptngen.cpp
+++ b/icu4c/source/i18n/dtptngen.cpp
@@ -687,6 +687,22 @@
     }
 }
 
+UDateFormatHourCycle
+DateTimePatternGenerator::getDefaultHourCycle(UErrorCode& /*status*/) const {
+  switch(fDefaultHourFormatChar) {
+    case CAP_K:
+      return UDAT_HOUR_CYCLE_11;
+    case LOW_H:
+      return UDAT_HOUR_CYCLE_12;
+    case CAP_H:
+      return UDAT_HOUR_CYCLE_23;
+    case LOW_K:
+      return UDAT_HOUR_CYCLE_24;
+    default:
+      UPRV_UNREACHABLE;
+  }
+}
+
 UnicodeString
 DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode&
 /*status*/) {
diff --git a/icu4c/source/i18n/udatpg.cpp b/icu4c/source/i18n/udatpg.cpp
index febf73b..332636a 100644
--- a/icu4c/source/i18n/udatpg.cpp
+++ b/icu4c/source/i18n/udatpg.cpp
@@ -291,4 +291,9 @@
     return result.getBuffer();
 }
 
+U_CAPI UDateFormatHourCycle U_EXPORT2
+udatpg_getDefaultHourCycle(const UDateTimePatternGenerator *dtpg, UErrorCode* pErrorCode) {
+    return ((const DateTimePatternGenerator *)dtpg)->getDefaultHourCycle(*pErrorCode);
+}
+
 #endif
diff --git a/icu4c/source/i18n/unicode/dtptngen.h b/icu4c/source/i18n/unicode/dtptngen.h
index a71938b..af74059 100644
--- a/icu4c/source/i18n/unicode/dtptngen.h
+++ b/icu4c/source/i18n/unicode/dtptngen.h
@@ -483,6 +483,17 @@
      */
     const UnicodeString& getDecimal() const;
 
+#ifndef U_HIDE_DRAFT_API
+    /**
+     * Get the default hour cycle.
+     * @param status  Output param set to success/failure code on exit,
+     *               which must not indicate a failure before the function call.
+     * @return the default hour cycle.
+     * @draft ICU 67
+     */
+    UDateFormatHourCycle getDefaultHourCycle(UErrorCode& status) const;
+#endif  /* U_HIDE_DRAFT_API */
+
     /**
      * ICU "poor man's RTTI", returns a UClassID for the actual class.
      *
diff --git a/icu4c/source/i18n/unicode/udat.h b/icu4c/source/i18n/unicode/udat.h
index bdbd080..cf7a165 100644
--- a/icu4c/source/i18n/unicode/udat.h
+++ b/icu4c/source/i18n/unicode/udat.h
@@ -958,7 +958,37 @@
 U_CAPI void U_EXPORT2
 udat_setBooleanAttribute(UDateFormat *fmt, UDateFormatBooleanAttribute attr, UBool newValue, UErrorCode* status);
 
+#ifndef U_HIDE_DRAFT_API
+/**
+ * Hour Cycle.
+ * @draft ICU 67
+ */
+typedef enum UDateFormatHourCycle {
+    /**
+     * Hour in am/pm (0~11)
+     * @draft ICU 67
+     */
+    UDAT_HOUR_CYCLE_11,
 
+    /**
+     * Hour in am/pm (1~12)
+     * @draft ICU 67
+     */
+    UDAT_HOUR_CYCLE_12,
+
+    /**
+     * Hour in day (0~23)
+     * @draft ICU 67
+     */
+    UDAT_HOUR_CYCLE_23,
+
+    /**
+     * Hour in day (1~24)
+     * @draft ICU 67
+     */
+    UDAT_HOUR_CYCLE_24
+} UDateFormatHourCycle;
+#endif  /* U_HIDE_DRAFT_API */
 
 #if U_SHOW_CPLUSPLUS_API
 
diff --git a/icu4c/source/i18n/unicode/udatpg.h b/icu4c/source/i18n/unicode/udatpg.h
index 7f28b5a..2b39828 100644
--- a/icu4c/source/i18n/unicode/udatpg.h
+++ b/icu4c/source/i18n/unicode/udatpg.h
@@ -20,6 +20,7 @@
 #define __UDATPG_H__
 
 #include "unicode/utypes.h"
+#include "unicode/udat.h"
 #include "unicode/uenum.h"
 #include "unicode/localpointer.h"
 
@@ -651,4 +652,18 @@
                              const UChar *skeleton, int32_t skeletonLength,
                              int32_t *pLength);
 
+#ifndef U_HIDE_DRAFT_API
+/**
+ * Return the default hour cycle.
+ *
+ * @param dtpg a pointer to UDateTimePatternGenerator.
+ * @param pErrorCode a pointer to the UErrorCode which must not indicate a
+ *                   failure before the function call.
+ * @return the default hour cycle.
+ * @draft ICU 67
+ */
+U_DRAFT UDateFormatHourCycle U_EXPORT2
+udatpg_getDefaultHourCycle(const UDateTimePatternGenerator *dtpg, UErrorCode* pErrorCode);
+#endif  /* U_HIDE_DRAFT_API */
+
 #endif
diff --git a/icu4c/source/test/cintltst/udatpg_test.c b/icu4c/source/test/cintltst/udatpg_test.c
index 2338a2f..b7d8cd7 100644
--- a/icu4c/source/test/cintltst/udatpg_test.c
+++ b/icu4c/source/test/cintltst/udatpg_test.c
@@ -43,6 +43,7 @@
 static void TestBuilder(void);
 static void TestOptions(void);
 static void TestGetFieldDisplayNames(void);
+static void TestGetDefaultHourCycle(void);
 
 void addDateTimePatternGeneratorTest(TestNode** root) {
     TESTCASE(TestOpenClose);
@@ -50,6 +51,7 @@
     TESTCASE(TestBuilder);
     TESTCASE(TestOptions);
     TESTCASE(TestGetFieldDisplayNames);
+    TESTCASE(TestGetDefaultHourCycle);
 }
 
 /*
@@ -510,4 +512,46 @@
     }
 }
 
+typedef struct HourCycleData {
+    const char *         locale;
+    UDateFormatHourCycle   expected;
+} HourCycleData;
+
+static void TestGetDefaultHourCycle() {
+    const HourCycleData testData[] = {
+        /*loc      expected */
+        { "ar_EG",    UDAT_HOUR_CYCLE_12 },
+        { "de_DE",    UDAT_HOUR_CYCLE_23 },
+        { "en_AU",    UDAT_HOUR_CYCLE_12 },
+        { "en_CA",    UDAT_HOUR_CYCLE_12 },
+        { "en_US",    UDAT_HOUR_CYCLE_12 },
+        { "es_ES",    UDAT_HOUR_CYCLE_23 },
+        { "fi",       UDAT_HOUR_CYCLE_23 },
+        { "fr",       UDAT_HOUR_CYCLE_23 },
+        { "ja_JP",    UDAT_HOUR_CYCLE_23 },
+        { "zh_CN",    UDAT_HOUR_CYCLE_12 },
+        { "zh_HK",    UDAT_HOUR_CYCLE_12 },
+        { "zh_TW",    UDAT_HOUR_CYCLE_12 },
+        { "ko_KR",    UDAT_HOUR_CYCLE_12 },
+    };
+    int count = UPRV_LENGTHOF(testData);
+    const HourCycleData * testDataPtr = testData;
+    for (; count-- > 0; ++testDataPtr) {
+        UErrorCode status = U_ZERO_ERROR;
+        UDateTimePatternGenerator * dtpgen =
+            udatpg_open(testDataPtr->locale, &status);
+        if ( U_FAILURE(status) ) {
+            log_data_err( "ERROR udatpg_open failed for locale %s : %s - (Are you missing data?)\n",
+                         testDataPtr->locale, myErrorName(status));
+        } else {
+            UDateFormatHourCycle actual = udatpg_getDefaultHourCycle(dtpgen, &status);
+            if (U_FAILURE(status) || testDataPtr->expected != actual) {
+                log_err("ERROR dtpgen locale %s udatpg_getDefaultHourCycle expecte to get %d but get %d\n",
+                        testDataPtr->locale, testDataPtr->expected, actual);
+            }
+            udatpg_close(dtpgen);
+        }
+    }
+}
+
 #endif
diff --git a/icu4c/source/test/intltest/dtptngts.cpp b/icu4c/source/test/intltest/dtptngts.cpp
index 02745c1..0a9c5ea 100644
--- a/icu4c/source/test/intltest/dtptngts.cpp
+++ b/icu4c/source/test/intltest/dtptngts.cpp
@@ -1410,18 +1410,19 @@
         const char* localeName;
         const char16_t* expectedDtpgPattern;
         const char16_t* expectedTimePattern;
+        UDateFormatHourCycle expectedDefaultHourCycle;
     } cases[] = {
         // ars is interesting because it does not have a region, but it aliases
         // to ar_SA, which has a region.
-        {"ars", u"h a", u"h:mm a"},
+        {"ars", u"h a", u"h:mm a", UDAT_HOUR_CYCLE_12},
         // en_NH is interesting because NH is a deprecated region code;
         // formerly New Hebrides, now Vanuatu => VU => h.
-        {"en_NH", u"h a", u"h:mm a"},
+        {"en_NH", u"h a", u"h:mm a", UDAT_HOUR_CYCLE_12},
         // ch_ZH is a typo (should be zh_CN), but we should fail gracefully.
         // {"cn_ZH", u"HH", u"H:mm"}, // TODO(ICU-20653): Desired behavior
-        {"cn_ZH", u"HH", u"h:mm a"}, // Actual behavior
+        {"cn_ZH", u"HH", u"h:mm a", UDAT_HOUR_CYCLE_23 }, // Actual behavior
         // a non-BCP47 locale without a country code should not fail
-        {"ja_TRADITIONAL", u"H時", u"H:mm"},
+        {"ja_TRADITIONAL", u"H時", u"H:mm", UDAT_HOUR_CYCLE_23},
     };
 
     for (auto& cas : cases) {
@@ -1440,11 +1441,17 @@
         if (status.errIfFailureAndReset()) {
             return;
         }
+        UDateFormatHourCycle defaultHourCycle = dtpg->getDefaultHourCycle(status);
+        if (status.errIfFailureAndReset()) {
+            return;
+        }
 
         assertEquals(UnicodeString("dtpgPattern ") + cas.localeName,
             cas.expectedDtpgPattern, dtpgPattern);
         assertEquals(UnicodeString("timePattern ") + cas.localeName,
             cas.expectedTimePattern, timePattern);
+        assertEquals(UnicodeString("defaultHour ") + cas.localeName,
+            cas.expectedDefaultHourCycle, defaultHourCycle);
     }
 
 }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java
index 5eb207e..bd42fbd 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java
@@ -535,6 +535,36 @@
      */
     private EnumSet<BooleanAttribute> booleanAttributes = EnumSet.allOf(BooleanAttribute.class);
 
+    /**
+     * Hour Cycle
+     * @draft ICU 67
+     */
+    public enum HourCycle {
+        /**
+         * hour in am/pm (0~11)
+         * @draft ICU 67
+         */
+        HOUR_CYCLE_11,
+
+        /**
+         * hour in am/pm (1~12)
+         * @draft ICU 67
+         */
+        HOUR_CYCLE_12,
+
+        /**
+         * hour in day (0~23)
+         * @draft ICU 67
+         */
+        HOUR_CYCLE_23,
+
+        /**
+         * hour in day (1~24)
+         * @draft ICU 67
+         */
+        HOUR_CYCLE_24;
+    };
+
     /*
      * Capitalization setting, hoisted to DateFormat ICU 53
      * Note that SimpleDateFormat serialization may call getContext/setContext to read/write
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java
index eb23318..96becb5 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java
@@ -1205,7 +1205,6 @@
     private static final int APPENDITEM_WIDTH_INT = APPENDITEM_WIDTH.ordinal();
     private static final DisplayWidth[] CLDR_FIELD_WIDTH = DisplayWidth.values();
 
-
     // Option masks for getBestPattern, replaceFieldTypes (individual masks may be ORed together)
 
     /**
@@ -1314,6 +1313,20 @@
     }
 
     /**
+     * Return the default hour cycle.
+     * @draft ICU 67
+     */
+    public DateFormat.HourCycle getDefaultHourCycle() {
+      switch(getDefaultHourFormatChar()) {
+        case 'h': return DateFormat.HourCycle.HOUR_CYCLE_12;
+        case 'H': return DateFormat.HourCycle.HOUR_CYCLE_23;
+        case 'k': return DateFormat.HourCycle.HOUR_CYCLE_24;
+        case 'K': return DateFormat.HourCycle.HOUR_CYCLE_11;
+        default: throw new AssertionError("should be unreachable");
+      }
+    }
+
+    /**
      * The private interface to set a display name for a particular date/time field,
      * in one of several possible display widths.
      *
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateTimeGeneratorTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateTimeGeneratorTest.java
index 767bb60..d7188b7 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateTimeGeneratorTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateTimeGeneratorTest.java
@@ -1733,14 +1733,14 @@
         String[][] cases = new String[][]{
             // ars is interesting because it does not have a region, but it aliases
             // to ar_SA, which has a region.
-            {"ars", "h a", "h:mm a"},
+            {"ars", "h a", "h:mm a", "HOUR_CYCLE_12"},
             // en_NH is interesting because NH is a depregated region code.
-            {"en_NH", "h a", "h:mm a"},
+            {"en_NH", "h a", "h:mm a", "HOUR_CYCLE_12"},
             // ch_ZH is a typo (should be zh_CN), but we should fail gracefully.
             // {"cn_ZH", "HH", "H:mm"}, // TODO(ICU-20653): Desired behavior
-            {"cn_ZH", "HH", "h:mm a"}, // Actual behavior
+            {"cn_ZH", "HH", "h:mm a", "HOUR_CYCLE_23"}, // Actual behavior
             // a non-BCP47 locale without a country code should not fail
-            {"ja_TRADITIONAL", "H時", "H:mm"},
+            {"ja_TRADITIONAL", "H時", "H:mm", "HOUR_CYCLE_23"},
         };
 
         for (String[] cas : cases) {
@@ -1755,6 +1755,8 @@
                 cas[1], dtpgPattern);
             assertEquals("timePattern " + cas[1],
                 cas[2], timePattern);
+            assertEquals("default hour cycle " + cas[3],
+                cas[3], dtpg.getDefaultHourCycle().toString());
         }
     }
 }