+/**
+ * This is a utility function used by the various date formatting classes to determine whether a particular pattern string will produce an all-numeric date.
+ * It does this by examining the pattern string. If the range between the first d, M, or y and the last d, M, or y in the pattern contains nothing but d, M, y,
+ * punctuation, whitespace, and Unicode right-to-left marks, and it doesn't contain more than two Ms in a row, it's considered to have a "numeric core"--
+ * that is, the part of the pattern that generates a date (minus fields like the day of the week and the era) produces an all-numeric date.
+ */
+extern UBool datePatternHasNumericCore(const UnicodeString& datePattern) {
+ StringCharacterIterator it = StringCharacterIterator(datePattern);
+ int32_t coreStart = -1;
+ int32_t coreEnd = -1;
+ int32_t firstLetterAfterCoreStart = -1;
+ int32_t numMs = 0;
+ UBool sawD = FALSE, sawY = FALSE;
+ for (UChar c = it.first(); it.hasNext(); c = it.next()) {
+ switch (c) {
+ case u'y': case u'Y': case u'r': case u'u':
+ case u'M': case u'L': case u'd':
+ if (coreStart == -1) {
+ coreStart = it.getIndex();
+ }
+ coreEnd = it.getIndex();
+ switch (c) {
+ case u'y': case u'Y': case u'r': case u'u':
+ sawY = TRUE;
+ break;
+ case u'M': case u'L':
+ // if the pattern contains more than 2 M's, the month is a word, not a number, which means
+ // we don't have a numeric core
+ ++numMs;
+ if (numMs > 2) {
+ return FALSE;
+ }
+ break;
+ case 'd':
+ sawD = TRUE;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ if (u_isalpha(c)) {
+ if (coreStart != -1 && firstLetterAfterCoreStart == -1) {
+ firstLetterAfterCoreStart = it.getIndex();
+ }
+ } else if (!u_isspace(c) && !u_ispunct(c) && c != u'\u200f') {
+ // the numeric core must contain nothing but d, M, y, whitespace, punctuation, and the Unicode right-to-left mark
+ return FALSE;
+ }
+ break;
+ }
+ }
+
+ // if we didn't find d, M, or y in the pattern, return FALSE
+ if (coreStart < 0 || coreEnd < 0) {
+ return FALSE;
+ }
+
+ // if there's quoted literal text anywhere in the pattern, whether in the "core" or not, treat it as though
+ // we don't have a numeric core
+ if (datePattern.indexOf(u'\'') != -1) {
+ return FALSE;
+ }
+
+ // if we found a letter other than d, M, or y between the first d, M, or y and the last one,
+ // we don't have a numeric core
+ if (firstLetterAfterCoreStart != -1 && firstLetterAfterCoreStart < coreEnd) {
+ return FALSE;
+ }
+
+ // if the format contains only one numeric field (out of d, M, or y), we don't count it as a numeric core
+ if (((numMs > 0) ? 1 : 0) + (sawY ? 1 : 0) + (sawD ? 1 : 0) <= 1) {
+ return FALSE;
+ }
+
+ // if we get to here, we have a numeric core
+ return TRUE;
+}
+