]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/smpdtfmt.cpp
ICU-57131.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / smpdtfmt.cpp
index b5ca4a0c72957df48a00efa84e3fa30616fe2464..2bcb5920f15308600a75dba2e1ca5a459959ad8e 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-* Copyright (C) 1997-2015, International Business Machines Corporation and    *
+* Copyright (C) 1997-2016, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 *
@@ -17,7 +17,7 @@
 *                             Removed getZoneIndex (added in DateFormatSymbols)
 *                             Removed subParseLong
 *                             Removed chk
-*  02/22/99     stephen     Removed character literals for EBCDIC safety
+*   02/22/99    stephen     Removed character literals for EBCDIC safety
 *   10/14/99    aliu        Updated 2-digit year parsing so that only "00" thru
 *                           "99" are recognized. {j28 4182066}
 *   11/15/99    weiv        Added support for week of year/day of week format
@@ -42,6 +42,7 @@
 #include "unicode/uniset.h"
 #include "unicode/ustring.h"
 #include "unicode/basictz.h"
+#include "unicode/simpleformatter.h"
 #include "unicode/simpletz.h"
 #include "unicode/rbtz.h"
 #include "unicode/tzfmt.h"
 #include "smpdtfst.h"
 #include "sharednumberformat.h"
 #include "ustr_imp.h"
+#include "charstr.h"
+#include "uvector.h"
+#include "cstr.h"
+#include "dayperiodrules.h"
 
 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
 #include <stdio.h>
@@ -209,7 +214,11 @@ static const int32_t gFieldRangeBias[] = {
     -1,  // 'X' - UDAT_TIMEZONE_ISO_FIELD
     -1,  // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
     -1,  // 'r' - UDAT_RELATED_YEAR_FIELD
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
     -1,  // ':' - UDAT_TIME_SEPARATOR_FIELD
+#else
+    -1,  // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD
+#endif
 };
 // A slightly looser range check for lenient parsing
 static const int32_t gFieldRangeBiasLenient[] = {
@@ -248,7 +257,11 @@ static const int32_t gFieldRangeBiasLenient[] = {
     -1,  // 'X' - UDAT_TIMEZONE_ISO_FIELD
     -1,  // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
     -1,  // 'r' - UDAT_RELATED_YEAR_FIELD
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
     -1,  // ':' - UDAT_TIME_SEPARATOR_FIELD
+#else
+    -1,  // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD
+#endif
 };
 
 // When calendar uses hebr numbering (i.e. he@calendar=hebrew),
@@ -393,32 +406,6 @@ class SimpleDateFormatMutableNFs : public UMemory {
 
 //----------------------------------------------------------------------
 
-static void updateTimeSepFromPattern(
-                        const UnicodeString& pattern,
-                        DateFormatSymbols* symbols ) {
-    UnicodeString hourMinChars("hHKkm", -1, US_INV); // pattern chars for hours, minutes
-    UnicodeString colon(":", -1, US_INV);
-    UBool inQuoted = FALSE;
-    UBool lastPatCharWasHourMin = FALSE;
-    int32_t patPos, patLen = pattern.length();
-    for (patPos = 0; patPos < patLen; patPos++) {
-        UChar patChr = pattern.charAt(patPos);
-        if (patChr == 0x27 /* ASCII-range single quote */) {
-            inQuoted = !inQuoted;
-        } else if (!inQuoted) {
-            if (patChr == 0x3A /*colon*/ && lastPatCharWasHourMin) {
-                symbols->setTimeSeparatorString(colon);
-                break;
-            }
-            if ((patChr >= 0x41 && patChr <= 0x5A) || (patChr >= 0x61 && patChr <= 0x7A)) {
-                lastPatCharWasHourMin = (hourMinChars.indexOf(patChr) >= 0);
-            }
-        }
-    }
-}
-
-//----------------------------------------------------------------------
-
 SimpleDateFormat::~SimpleDateFormat()
 {
     delete fSymbols;
@@ -466,7 +453,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
     fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
     initialize(fLocale, status);
     initializeDefaultCentury();
-    updateTimeSepFromPattern(fPattern, fSymbols);
+
 }
 
 //----------------------------------------------------------------------
@@ -513,7 +500,6 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
     fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
     initialize(fLocale, status);
     initializeDefaultCentury();
-    updateTimeSepFromPattern(fPattern, fSymbols);
 }
 
 //----------------------------------------------------------------------
@@ -684,6 +670,8 @@ SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
     fHaveDefaultCentury          = other.fHaveDefaultCentury;
 
     fPattern = other.fPattern;
+    fHasMinute = other.fHasMinute;
+    fHasSecond = other.fHasSecond;
 
     fLocale = other.fLocale;
     // TimeZoneFormat can now be set independently via setter.
@@ -798,16 +786,9 @@ void SimpleDateFormat::construct(EStyle timeStyle,
 
     // if the pattern should include both date and time information, use the date/time
     // pattern string as a guide to tell use how to glue together the appropriate date
-    // and time pattern strings.  The actual gluing-together is handled by a convenience
-    // method on MessageFormat.
+    // and time pattern strings.
     if ((timeStyle != kNone) && (dateStyle != kNone))
     {
-        Formattable timeDateArray[2];
-
-        // use Formattable::adoptString() so that we can use fastCopyFrom()
-        // instead of Formattable::setString()'s unaware, safe, deep string clone
-        // see Jitterbug 2296
-
         currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)timeStyle, NULL, &status);
         if (U_FAILURE(status)) {
            status = U_INVALID_FORMAT_ERROR;
@@ -832,13 +813,7 @@ void SimpleDateFormat::construct(EStyle timeStyle,
         }
         ures_close(currentBundle);
 
-        UnicodeString *tempus1 = new UnicodeString(TRUE, resStr, resStrLen);
-        // NULL pointer check
-        if (tempus1 == NULL) {
-            status = U_MEMORY_ALLOCATION_ERROR;
-            return;
-        }
-        timeDateArray[0].adoptString(tempus1);
+        UnicodeString tempus1(TRUE, resStr, resStrLen);
 
         currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)dateStyle, NULL, &status);
         if (U_FAILURE(status)) {
@@ -864,13 +839,7 @@ void SimpleDateFormat::construct(EStyle timeStyle,
         }
         ures_close(currentBundle);
 
-        UnicodeString *tempus2 = new UnicodeString(TRUE, resStr, resStrLen);
-        // Null pointer check
-        if (tempus2 == NULL) {
-            status = U_MEMORY_ALLOCATION_ERROR;
-            return;
-        }
-        timeDateArray[1].adoptString(tempus2);
+        UnicodeString tempus2(TRUE, resStr, resStrLen);
 
         int32_t glueIndex = kDateTime;
         int32_t patternsSize = ures_getSize(dateTimePatterns);
@@ -880,7 +849,8 @@ void SimpleDateFormat::construct(EStyle timeStyle,
         }
 
         resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &status);
-        MessageFormat::format(UnicodeString(TRUE, resStr, resStrLen), timeDateArray, 2, fPattern, status);
+        SimpleFormatter(UnicodeString(TRUE, resStr, resStrLen), 2, 2, status).
+                format(tempus1, tempus2, fPattern, status);
     }
     // if the pattern includes just time data or just date date, load the appropriate
     // pattern string from the resources
@@ -993,6 +963,8 @@ SimpleDateFormat::initialize(const Locale& locale,
     {
         status = U_MISSING_RESOURCE_ERROR;
     }
+
+    parsePattern();
 }
 
 /* Initialize the fields we use to disambiguate ambiguous years. Separate
@@ -1021,7 +993,7 @@ void SimpleDateFormat::initializeBooleanAttributes()
 
     setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status);
     setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
-    setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status);
+    setBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, true, status);
     setBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, true, status);
 }
 
@@ -1073,7 +1045,7 @@ SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo,
                             FieldPositionHandler& handler, UErrorCode& status) const
 {
     if ( U_FAILURE(status) ) {
-       return appendTo; 
+       return appendTo;
     }
     Calendar* workCal = &cal;
     Calendar* calClone = NULL;
@@ -1179,8 +1151,13 @@ int32_t SimpleDateFormat::getLevelFromChar(UChar ch) {
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
         //       !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
         //   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  0, -1, -1, -1, -1, -1,
+#else
+        //   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+#endif
         //   @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
             -1, 40, -1, -1, 20, 30, 30,  0, 50, -1, -1, 50, 20, 20, -1,  0,
         //   P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _
@@ -1210,8 +1187,13 @@ UBool SimpleDateFormat::isSyntaxChar(UChar ch) {
         FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
         //  0      1      2      3      4      5      6      7
         FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
         //  8      9      :      ;      <      =      >      ?
         FALSE, FALSE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE,
+#else
+        //  8      9      :      ;      <      =      >      ?
+        FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+#endif
         //  @      A      B      C      D      E      F      G
         FALSE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE,
         //  H      I      J      K      L      M      N      O
@@ -1255,7 +1237,12 @@ SimpleDateFormat::fgPatternIndexToCalendarField[] =
     /*O*/   UCAL_ZONE_OFFSET,
     /*Xx*/  UCAL_ZONE_OFFSET, UCAL_ZONE_OFFSET,
     /*r*/   UCAL_EXTENDED_YEAR,
+    /*bB*/   UCAL_FIELD_COUNT, UCAL_FIELD_COUNT,  // no mappings to calendar fields
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
     /*:*/   UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */
+#else
+    /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/   UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */
+#endif
 };
 
 // Map index into pattern character string to DateFormat field number
@@ -1279,7 +1266,12 @@ SimpleDateFormat::fgPatternIndexToDateFormatField[] = {
     /*O*/   UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD,
     /*Xx*/  UDAT_TIMEZONE_ISO_FIELD, UDAT_TIMEZONE_ISO_LOCAL_FIELD,
     /*r*/   UDAT_RELATED_YEAR_FIELD,
+    /*bB*/  UDAT_AM_PM_MIDNIGHT_NOON_FIELD, UDAT_FLEXIBLE_DAY_PERIOD_FIELD,
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
     /*:*/   UDAT_TIME_SEPARATOR_FIELD,
+#else
+    /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/   UDAT_TIME_SEPARATOR_FIELD,
+#endif
 };
 
 //----------------------------------------------------------------------
@@ -1307,8 +1299,7 @@ _appendSymbolWithMonthPattern(UnicodeString& dst, int32_t value, const UnicodeSt
         if (monthPattern == NULL) {
             dst += symbols[value];
         } else {
-            Formattable monthName((const UnicodeString&)(symbols[value]));
-            MessageFormat::format(*monthPattern, &monthName, 1, dst, status);
+            SimpleFormatter(*monthPattern, 1, 1, status).format(symbols[value], dst, status);
         }
     }
 }
@@ -1424,6 +1415,7 @@ SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeStrin
                     if (type==kOvrStrDate) {
                         break;
                     }
+                    U_FALLTHROUGH;
                 }
                 case kOvrStrTime : {
                     for ( int8_t i=0 ; i<kTimeFieldsCount; i++ ) {
@@ -1506,7 +1498,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
         return;
     }
     UnicodeString hebr("hebr", 4, US_INV);
-    
+
     switch (patternCharIndex) {
 
     // for any "G" symbol, write out the appropriate era string
@@ -1535,6 +1527,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
             break;
         }
         // else fall through to numeric year handling, do not break here
+        U_FALLTHROUGH;
 
    // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
     // NEW: UTS#35:
@@ -1654,6 +1647,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
             return;
         }
         // fall through, do not break here
+        U_FALLTHROUGH;
     case UDAT_DAY_OF_WEEK_FIELD:
         if (count == 5) {
             _appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays,
@@ -1719,7 +1713,8 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
         }
         break;
 
-    // for ":", write out the time separator string
+    // if we see pattern character for UDAT_TIME_SEPARATOR_FIELD (none currently defined),
+    // write out the time separator string. Leave support in for future definition.
     case UDAT_TIME_SEPARATOR_FIELD:
         {
             UnicodeString separator;
@@ -1874,6 +1869,140 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
             zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount);
         break;
 
+    case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
+    {
+        const UnicodeString *toAppend = NULL;
+        int32_t hour = cal.get(UCAL_HOUR_OF_DAY, status);
+
+        // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
+        // For ICU 57 output of "midnight" is temporarily suppressed.
+
+        // For "midnight" and "noon":
+        // Time, as displayed, must be exactly noon or midnight.
+        // This means minutes and seconds, if present, must be zero.
+        if ((/*hour == 0 ||*/ hour == 12) &&
+                (!fHasMinute || cal.get(UCAL_MINUTE, status) == 0) &&
+                (!fHasSecond || cal.get(UCAL_SECOND, status) == 0)) {
+            // Stealing am/pm value to use as our array index.
+            // It works out: am/midnight are both 0, pm/noon are both 1,
+            // 12 am is 12 midnight, and 12 pm is 12 noon.
+            int32_t value = cal.get(UCAL_AM_PM, status);
+
+            if (count <= 3) {
+                toAppend = &fSymbols->fAbbreviatedDayPeriods[value];
+            } else if (count == 4 || count > 5) {
+                toAppend = &fSymbols->fWideDayPeriods[value];
+            } else { // count == 5
+                toAppend = &fSymbols->fNarrowDayPeriods[value];
+            }
+        }
+
+        // toAppend is NULL if time isn't exactly midnight or noon (as displayed).
+        // toAppend is bogus if time is midnight or noon, but no localized string exists.
+        // In either case, fall back to am/pm.
+        if (toAppend == NULL || toAppend->isBogus()) {
+            // Reformat with identical arguments except ch, now changed to 'a'.
+            subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
+                      handler, cal, mutableNFs, status);
+        } else {
+            appendTo += *toAppend;
+        }
+
+        break;
+    }
+
+    case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
+    {
+        // TODO: Maybe fetch the DayperiodRules during initialization (instead of at the first
+        // loading of an instance) if a relevant pattern character (b or B) is used.
+        const DayPeriodRules *ruleSet = DayPeriodRules::getInstance(this->getSmpFmtLocale(), status);
+        if (U_FAILURE(status)) {
+            // Data doesn't conform to spec, therefore loading failed.
+            break;
+        }
+        if (ruleSet == NULL) {
+            // Data doesn't exist for the locale we're looking for.
+            // Falling back to am/pm.
+            subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
+                      handler, cal, mutableNFs, status);
+            break;
+        }
+
+        // Get current display time.
+        int32_t hour = cal.get(UCAL_HOUR_OF_DAY, status);
+        int32_t minute = 0;
+        if (fHasMinute) {
+            minute = cal.get(UCAL_MINUTE, status);
+        }
+        int32_t second = 0;
+        if (fHasSecond) {
+            second = cal.get(UCAL_SECOND, status);
+        }
+
+        // Determine day period.
+        DayPeriodRules::DayPeriod periodType;
+        if (hour == 0 && minute == 0 && second == 0 && ruleSet->hasMidnight()) {
+            periodType = DayPeriodRules::DAYPERIOD_MIDNIGHT;
+        } else if (hour == 12 && minute == 0 && second == 0 && ruleSet->hasNoon()) {
+            periodType = DayPeriodRules::DAYPERIOD_NOON;
+        } else {
+            periodType = ruleSet->getDayPeriodForHour(hour);
+        }
+
+        // Rule set exists, therefore periodType can't be UNKNOWN.
+        // Get localized string.
+        U_ASSERT(periodType != DayPeriodRules::DAYPERIOD_UNKNOWN);
+        UnicodeString *toAppend = NULL;
+        int32_t index;
+
+        // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
+        // For ICU 57 output of "midnight" is temporarily suppressed.
+
+        if (periodType != DayPeriodRules::DAYPERIOD_AM &&
+                periodType != DayPeriodRules::DAYPERIOD_PM &&
+                periodType != DayPeriodRules::DAYPERIOD_MIDNIGHT) {
+            index = (int32_t)periodType;
+            if (count <= 3) {
+                toAppend = &fSymbols->fAbbreviatedDayPeriods[index];  // i.e. short
+            } else if (count == 4 || count > 5) {
+                toAppend = &fSymbols->fWideDayPeriods[index];
+            } else {  // count == 5
+                toAppend = &fSymbols->fNarrowDayPeriods[index];
+            }
+        }
+
+        // Fallback schedule:
+        // Midnight/Noon -> General Periods -> AM/PM.
+
+        // Midnight/Noon -> General Periods.
+        if ((toAppend == NULL || toAppend->isBogus()) &&
+                (periodType == DayPeriodRules::DAYPERIOD_MIDNIGHT ||
+                 periodType == DayPeriodRules::DAYPERIOD_NOON)) {
+            periodType = ruleSet->getDayPeriodForHour(hour);
+            index = (int32_t)periodType;
+
+            if (count <= 3) {
+                toAppend = &fSymbols->fAbbreviatedDayPeriods[index];  // i.e. short
+            } else if (count == 4 || count > 5) {
+                toAppend = &fSymbols->fWideDayPeriods[index];
+            } else {  // count == 5
+                toAppend = &fSymbols->fNarrowDayPeriods[index];
+            }
+        }
+
+        // General Periods -> AM/PM.
+        if (periodType == DayPeriodRules::DAYPERIOD_AM ||
+            periodType == DayPeriodRules::DAYPERIOD_PM ||
+            toAppend->isBogus()) {
+            subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
+                      handler, cal, mutableNFs, status);
+        }
+        else {
+            appendTo += *toAppend;
+        }
+
+        break;
+    }
 
     // all of the other pattern symbols can be formatted as simple numbers with
     // appropriate zero padding
@@ -1916,7 +2045,7 @@ void SimpleDateFormat::adoptNumberFormat(NumberFormat *formatToAdopt) {
     fixNumberFormatForDates(*formatToAdopt);
     delete fNumberFormat;
     fNumberFormat = formatToAdopt;
-    
+
     // We successfully set the default number format. Now delete the overrides
     // (can't fail).
     if (fSharedNumberFormatters) {
@@ -2042,6 +2171,9 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
     }
     int32_t start = pos;
 
+    // Hold the day period until everything else is parsed, because we need
+    // the hour to interpret time correctly.
+    int32_t dayPeriodInt = -1;
 
     UBool ambiguousYear[] = { FALSE };
     int32_t saveHebrewMonth = -1;
@@ -2080,7 +2212,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
             goto ExitParse;
         }
     }
-    
+
     if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
         numericLeapMonthFormatter = new MessageFormat(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric], fLocale, status);
         if (numericLeapMonthFormatter == NULL) {
@@ -2156,7 +2288,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
             // fields.
             else if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
                 int32_t s = subParse(text, pos, ch, count,
-                               FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, mutableNFs);
+                               FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, mutableNFs, &dayPeriodInt);
 
                 if (s == -pos-1) {
                     // era not present, in special cases allow this to continue
@@ -2192,8 +2324,8 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
         else {
 
             abutPat = -1; // End of any abutting fields
-            
-            if (! matchLiterals(fPattern, i, text, pos, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status), getBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, status), isLenient())) {
+
+            if (! matchLiterals(fPattern, i, text, pos, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status), getBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, status), isLenient())) {
                 status = U_PARSE_ERROR;
                 goto ExitParse;
             }
@@ -2208,6 +2340,76 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
         }
     }
 
+    // If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
+    if (dayPeriodInt >= 0) {
+        DayPeriodRules::DayPeriod dayPeriod = (DayPeriodRules::DayPeriod)dayPeriodInt;
+        const DayPeriodRules *ruleSet = DayPeriodRules::getInstance(this->getSmpFmtLocale(), status);
+
+        if (!cal.isSet(UCAL_HOUR) && !cal.isSet(UCAL_HOUR_OF_DAY)) {
+            // If hour is not set, set time to the midpoint of current day period, overwriting
+            // minutes if it's set.
+            double midPoint = ruleSet->getMidPointForDayPeriod(dayPeriod, status);
+
+            // If we can't get midPoint we do nothing.
+            if (U_SUCCESS(status)) {
+                // Truncate midPoint toward zero to get the hour.
+                // Any leftover means it was a half-hour.
+                int32_t midPointHour = (int32_t) midPoint;
+                int32_t midPointMinute = (midPoint - midPointHour) > 0 ? 30 : 0;
+
+                // No need to set am/pm because hour-of-day is set last therefore takes precedence.
+                cal.set(UCAL_HOUR_OF_DAY, midPointHour);
+                cal.set(UCAL_MINUTE, midPointMinute);
+            }
+        } else {
+            int hourOfDay;
+
+            if (cal.isSet(UCAL_HOUR_OF_DAY)) {  // Hour is parsed in 24-hour format.
+                hourOfDay = cal.get(UCAL_HOUR_OF_DAY, status);
+            } else {  // Hour is parsed in 12-hour format.
+                hourOfDay = cal.get(UCAL_HOUR, status);
+                // cal.get() turns 12 to 0 for 12-hour time; change 0 to 12
+                // so 0 unambiguously means a 24-hour time from above.
+                if (hourOfDay == 0) { hourOfDay = 12; }
+            }
+            U_ASSERT(0 <= hourOfDay && hourOfDay <= 23);
+
+
+            // If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
+            if (hourOfDay == 0 || (13 <= hourOfDay && hourOfDay <= 23)) {
+                // Make hour-of-day take precedence over (hour + am/pm) by setting it again.
+                cal.set(UCAL_HOUR_OF_DAY, hourOfDay);
+            } else {
+                // We have a 12-hour time and need to choose between am and pm.
+                // Behave as if dayPeriod spanned 6 hours each way from its center point.
+                // This will parse correctly for consistent time + period (e.g. 10 at night) as
+                // well as provide a reasonable recovery for inconsistent time + period (e.g.
+                // 9 in the afternoon).
+
+                // Assume current time is in the AM.
+                // - Change 12 back to 0 for easier handling of 12am.
+                // - Append minutes as fractional hours because e.g. 8:15 and 8:45 could be parsed
+                // into different half-days if center of dayPeriod is at 14:30.
+                // - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
+                if (hourOfDay == 12) { hourOfDay = 0; }
+                double currentHour = hourOfDay + (cal.get(UCAL_MINUTE, status)) / 60.0;
+                double midPointHour = ruleSet->getMidPointForDayPeriod(dayPeriod, status);
+
+                if (U_SUCCESS(status)) {
+                    double hoursAheadMidPoint = currentHour - midPointHour;
+
+                    // Assume current time is in the AM.
+                    if (-6 <= hoursAheadMidPoint && hoursAheadMidPoint < 6) {
+                        // Assumption holds; set time as such.
+                        cal.set(UCAL_AM_PM, 0);
+                    } else {
+                        cal.set(UCAL_AM_PM, 1);
+                    }
+                }
+            }
+        }
+    }
+
     // At this point the fields of Calendar have been set.  Calendar
     // will fill in default values for missing fields when the time
     // is computed.
@@ -2432,6 +2634,29 @@ int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text,
     return -start;
 }
 
+int32_t SimpleDateFormat::matchDayPeriodStrings(const UnicodeString& text, int32_t start,
+                              const UnicodeString* data, int32_t dataCount,
+                              int32_t &dayPeriod) const
+{
+
+    int32_t bestMatchLength = 0, bestMatch = -1;
+
+    for (int32_t i = 0; i < dataCount; ++i) {
+        int32_t matchLength = 0;
+        if ((matchLength = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) {
+            bestMatchLength = matchLength;
+            bestMatch = i;
+        }
+    }
+
+    if (bestMatch >= 0) {
+        dayPeriod = bestMatch;
+        return start + bestMatchLength;
+    }
+
+    return -start;
+}
+
 //----------------------------------------------------------------------
 #define IS_BIDI_MARK(c) (c==0x200E || c==0x200F || c==0x061C) 
 
@@ -2444,17 +2669,17 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
                                       UBool oldLeniency)
 {
     UBool inQuote = FALSE;
-    UnicodeString literal;    
+    UnicodeString literal;
     int32_t i = patternOffset;
 
     // scan pattern looking for contiguous literal characters
     for ( ; i < pattern.length(); i += 1) {
         UChar ch = pattern.charAt(i);
-        
+
         if (!inQuote && isSyntaxChar(ch)) {
             break;
         }
-        
+
         if (ch == QUOTE) {
             // Match a quote literal ('') inside OR outside of quotes
             if ((i + 1) < pattern.length() && pattern.charAt(i + 1) == QUOTE) {
@@ -2464,21 +2689,21 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
                 continue;
             }
         }
-        
+
         if (!IS_BIDI_MARK(ch)) {
             literal += ch;
         }
     }
-    
+
     // at this point, literal contains the pattern literal text (without bidi marks)
     // and i is the index of the next non-literal pattern character.
     int32_t p;
     int32_t t = textOffset;
-    
+
     if (whitespaceLenient) {
         // trim leading, trailing whitespace from the pattern literal
         literal.trim();
-        
+
         // ignore any leading whitespace (or bidi marks) in the text
         while (t < text.length()) {
             UChar ch = text.charAt(t);
@@ -2488,7 +2713,7 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
             t += 1;
         }
     }
-        
+
     // Get ignorables, move up here
     const  UnicodeSet *ignorables = NULL;
     UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(pattern.charAt(i));
@@ -2498,7 +2723,7 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
 
     for (p = 0; p < literal.length() && t < text.length();) {
         UBool needWhitespace = FALSE;
-        
+
         // Skip any whitespace at current position in pattern,
         // but remember whether we found whitespace in the pattern
         // (we already deleted any bidi marks in the pattern).
@@ -2506,7 +2731,7 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
             needWhitespace = TRUE;
             p += 1;
         }
-        
+
         // If the pattern has whitespace at this point, skip it in text as well
         // (if the text does not have any, that may be an error for strict parsing)
         if (needWhitespace) {
@@ -2521,10 +2746,10 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
                 } else if (!IS_BIDI_MARK(tch)) {
                     break;
                 }
-                
+
                 t += 1;
             }
-            
+
             // TODO: should we require internal spaces
             // in lenient mode? (There won't be any
             // leading or trailing spaces)
@@ -2533,7 +2758,7 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
                 // an error in strict mode
                 return FALSE;
             }
-            
+
             // In strict mode, this run of whitespace
             // may have been at the end.
             if (p >= literal.length()) {
@@ -2556,15 +2781,15 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
                     ++t;
                     continue;  // Do not update p.
                 }
-                // if it is actual whitespace and we're whitespace lenient it's OK                
-                
+                // if it is actual whitespace and we're whitespace lenient it's OK
+
                 UChar wsc = text.charAt(t);
                 if(PatternProps::isWhiteSpace(wsc)) {
                     // Lenient mode and it's just whitespace we skip it
                     ++t;
                     continue;  // Do not update p.
                 }
-            } 
+            }
             // hack around oldleniency being a bit of a catch-all bucket and we're just adding support specifically for paritial matches
             // This fix is for http://bugs.icu-project.org/trac/ticket/10855 and adds "&& oldLeniency"
             //if(partialMatchLenient && oldLeniency) {
@@ -2576,33 +2801,33 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
               ) {
                 break;
             }
-            
+
             return FALSE;
         }
         ++p;
         ++t;
     }
-    
+
     // At this point if we're in strict mode we have a complete match.
     // If we're in lenient mode we may have a partial match, or no
     // match at all.
     if (p <= 0) {
         // no match. Pretend it matched a run of whitespace
         // and ignorables in the text.
-        
+
         for (t = textOffset; t < text.length(); t += 1) {
             UChar ch = text.charAt(t);
-            
+
             if (!IS_BIDI_MARK(ch) && (ignorables == NULL || !ignorables->contains(ch))) {
                 break;
             }
         }
     }
-    
+
     // if we get here, we've got a complete match.
     patternOffset = i - 1;
     textOffset = t;
-    
+
     return TRUE;
 }
 
@@ -2639,8 +2864,7 @@ int32_t SimpleDateFormat::matchString(const UnicodeString& text,
         if (monthPattern != NULL) {
             UErrorCode status = U_ZERO_ERROR;
             UnicodeString leapMonthName;
-            Formattable monthName((const UnicodeString&)(data[i]));
-            MessageFormat::format(*monthPattern, &monthName, 1, leapMonthName, status);
+            SimpleFormatter(*monthPattern, 1, 1, status).format(data[i], leapMonthName, status);
             if (U_SUCCESS(status)) {
                 if ((matchLen = matchStringWithOptionalDot(text, start, leapMonthName)) > bestMatchLength) {
                     bestMatch = i;
@@ -2711,7 +2935,8 @@ SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status)
  */
 int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count,
                            UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal,
-                           int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType, SimpleDateFormatMutableNFs &mutableNFs) const
+                           int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType, SimpleDateFormatMutableNFs &mutableNFs,
+                           int32_t *dayPeriod) const
 {
     Formattable number;
     int32_t value = 0;
@@ -2819,7 +3044,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
         if (txtLoc > parseStart) {
             value = number.getLong();
             gotNumber = TRUE;
-            
+
             // suffix processing
             if (value < 0 ) {
                 txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, TRUE);
@@ -2847,7 +3072,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
             pos.setIndex(txtLoc);
         }
     }
-    
+
     // Make sure that we got a number if
     // we want one, and didn't get one
     // if we don't want one.
@@ -2860,9 +3085,9 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
             if (value < 0 || value > 24) {
                 return -start;
             }
-            
+
             // fall through to gotNumber check
-            
+            U_FALLTHROUGH;
         case UDAT_YEAR_FIELD:
         case UDAT_YEAR_WOY_FIELD:
         case UDAT_FRACTIONAL_SECOND_FIELD:
@@ -2870,9 +3095,9 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
             if (! gotNumber) {
                 return -start;
             }
-            
+
             break;
-            
+
         default:
             // we check the rest of the fields below.
             break;
@@ -3051,9 +3276,9 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
         // [We computed 'value' above.]
         if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1)
             value = 0;
-            
+
         // fall through to set field
-            
+        U_FALLTHROUGH;
     case UDAT_HOUR_OF_DAY0_FIELD:
         cal.set(UCAL_HOUR_OF_DAY, value);
         return pos.getIndex();
@@ -3086,6 +3311,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
         }
         // else for eee-eeeee fall through to handling of EEE-EEEEE
         // fall through, do not break here
+        U_FALLTHROUGH;
     case UDAT_DAY_OF_WEEK_FIELD:
         {
             // Want to be able to parse both short and long forms.
@@ -3176,9 +3402,9 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
         // [We computed 'value' above.]
         if (value == cal.getLeastMaximum(UCAL_HOUR)+1)
             value = 0;
-            
+
         // fall through to set field
-            
+        U_FALLTHROUGH;
     case UDAT_HOUR0_FIELD:
         cal.set(UCAL_HOUR, value);
         return pos.getIndex();
@@ -3366,7 +3592,9 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
             }
             return -start;
         }
-    case UDAT_TIME_SEPARATOR_FIELD: // ':'
+    // currently no pattern character is defined for UDAT_TIME_SEPARATOR_FIELD
+    // so we should not get here. Leave support in for future definition.
+    case UDAT_TIME_SEPARATOR_FIELD:
         {
             static const UChar def_sep = DateFormatSymbols::DEFAULT_TIME_SEPARATOR;
             static const UChar alt_sep = DateFormatSymbols::ALTERNATE_TIME_SEPARATOR;
@@ -3389,6 +3617,70 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
             return matchString(text, start, UCAL_FIELD_COUNT /* => nothing to set */, data, count, NULL, cal);
         }
 
+    case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
+    {
+        U_ASSERT(dayPeriod != NULL);
+        int32_t ampmStart = subParse(text, start, 0x61, count,
+                           obeyCount, allowNegative, ambiguousYear, saveHebrewMonth, cal,
+                           patLoc, numericLeapMonthFormatter, tzTimeType, mutableNFs);
+
+        if (ampmStart > 0) {
+            return ampmStart;
+        } else {
+            int32_t newStart = 0;
+
+            // Only match the first two strings from the day period strings array.
+            if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
+                if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fAbbreviatedDayPeriods,
+                                                        2, *dayPeriod)) > 0) {
+                    return newStart;
+                }
+            }
+            if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
+                if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fNarrowDayPeriods,
+                                                        2, *dayPeriod)) > 0) {
+                    return newStart;
+                }
+            }
+            // count == 4, but allow other counts
+            if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status)) {
+                if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fWideDayPeriods,
+                                                        2, *dayPeriod)) > 0) {
+                    return newStart;
+                }
+            }
+
+            return -start;
+        }
+    }
+
+    case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
+    {
+        U_ASSERT(dayPeriod != NULL);
+        int32_t newStart = 0;
+
+        if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
+            if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fAbbreviatedDayPeriods,
+                                fSymbols->fAbbreviatedDayPeriodsCount, *dayPeriod)) > 0) {
+                return newStart;
+            }
+        }
+        if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
+            if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fNarrowDayPeriods,
+                                fSymbols->fNarrowDayPeriodsCount, *dayPeriod)) > 0) {
+                return newStart;
+            }
+        }
+        if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
+            if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fWideDayPeriods,
+                                fSymbols->fWideDayPeriodsCount, *dayPeriod)) > 0) {
+                return newStart;
+            }
+        }
+
+        return -start;
+    }
+
     default:
         // Handle "generic" fields
         // this is now handled below, outside the switch block
@@ -3533,7 +3825,7 @@ void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern,
                                         UErrorCode& status)
 {
     // run through the pattern and convert any pattern symbols from the version
-    // in "from" to the corresponding character ion "to".  This code takes
+    // in "from" to the corresponding character in "to".  This code takes
     // quoted strings into account (it doesn't try to translate them), and it signals
     // an error if a particular "pattern character" doesn't appear in "from".
     // Depending on the values of "from" and "to" this can convert from generic
@@ -3597,7 +3889,7 @@ void
 SimpleDateFormat::applyPattern(const UnicodeString& pattern)
 {
     fPattern = pattern;
-    updateTimeSepFromPattern(fPattern, fSymbols);
+    parsePattern();
 }
 
 //----------------------------------------------------------------------
@@ -3609,7 +3901,6 @@ SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern,
     translatePattern(pattern, fPattern,
                      fSymbols->fLocalPatternChars,
                      UnicodeString(DateFormatSymbols::getPatternUChars()), status);
-    updateTimeSepFromPattern(fPattern, fSymbols);
 }
 
 //----------------------------------------------------------------------
@@ -3926,6 +4217,28 @@ SimpleDateFormat::tzFormat() const {
     return fTimeZoneFormat;
 }
 
+void SimpleDateFormat::parsePattern() {
+    fHasMinute = FALSE;
+    fHasSecond = FALSE;
+
+    int len = fPattern.length();
+    UBool inQuote = FALSE;
+    for (int32_t i = 0; i < len; ++i) {
+        UChar ch = fPattern[i];
+        if (ch == QUOTE) {
+            inQuote = !inQuote;
+        }
+        if (!inQuote) {
+            if (ch == 0x6D) {  // 0x6D == 'm'
+                fHasMinute = TRUE;
+            }
+            if (ch == 0x73) {  // 0x73 == 's'
+                fHasSecond = TRUE;
+            }
+        }
+    }
+}
+
 U_NAMESPACE_END
 
 #endif /* #if !UCONFIG_NO_FORMATTING */