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());
}
}
}