/*
*******************************************************************************
-* Copyright (C) 1997-2015, International Business Machines Corporation and *
+* Copyright (C) 1997-2016, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
* 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
#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>
-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[] = {
-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),
//----------------------------------------------------------------------
-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;
fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
initialize(fLocale, status);
initializeDefaultCentury();
- updateTimeSepFromPattern(fPattern, fSymbols);
+
}
//----------------------------------------------------------------------
fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
initialize(fLocale, status);
initializeDefaultCentury();
- updateTimeSepFromPattern(fPattern, fSymbols);
}
//----------------------------------------------------------------------
fHaveDefaultCentury = other.fHaveDefaultCentury;
fPattern = other.fPattern;
+ fHasMinute = other.fHasMinute;
+ fHasSecond = other.fHasSecond;
fLocale = other.fLocale;
// TimeZoneFormat can now be set independently via setter.
// 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;
}
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)) {
}
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);
}
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
{
status = U_MISSING_RESOURCE_ERROR;
}
+
+ parsePattern();
}
/* Initialize the fields we use to disambiguate ambiguous years. Separate
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);
}
FieldPositionHandler& handler, UErrorCode& status) const
{
if ( U_FAILURE(status) ) {
- return appendTo;
+ return appendTo;
}
Calendar* workCal = &cal;
Calendar* calClone = NULL;
-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 [ \ ] ^ _
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
/*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
/*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
};
//----------------------------------------------------------------------
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);
}
}
}
if (type==kOvrStrDate) {
break;
}
+ U_FALLTHROUGH;
}
case kOvrStrTime : {
for ( int8_t i=0 ; i<kTimeFieldsCount; i++ ) {
return;
}
UnicodeString hebr("hebr", 4, US_INV);
-
+
switch (patternCharIndex) {
// for any "G" symbol, write out the appropriate era string
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:
return;
}
// fall through, do not break here
+ U_FALLTHROUGH;
case UDAT_DAY_OF_WEEK_FIELD:
if (count == 5) {
_appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays,
}
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;
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
fixNumberFormatForDates(*formatToAdopt);
delete fNumberFormat;
fNumberFormat = formatToAdopt;
-
+
// We successfully set the default number format. Now delete the overrides
// (can't fail).
if (fSharedNumberFormatters) {
}
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;
goto ExitParse;
}
}
-
+
if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
numericLeapMonthFormatter = new MessageFormat(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric], fLocale, status);
if (numericLeapMonthFormatter == NULL) {
// 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
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;
}
}
}
+ // 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.
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)
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) {
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);
t += 1;
}
}
-
+
// Get ignorables, move up here
const UnicodeSet *ignorables = NULL;
UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(pattern.charAt(i));
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).
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) {
} 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)
// an error in strict mode
return FALSE;
}
-
+
// In strict mode, this run of whitespace
// may have been at the end.
if (p >= literal.length()) {
++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) {
) {
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;
}
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;
*/
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;
if (txtLoc > parseStart) {
value = number.getLong();
gotNumber = TRUE;
-
+
// suffix processing
if (value < 0 ) {
txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, TRUE);
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.
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:
if (! gotNumber) {
return -start;
}
-
+
break;
-
+
default:
// we check the rest of the fields below.
break;
// [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();
}
// 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.
// [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();
}
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;
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
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
SimpleDateFormat::applyPattern(const UnicodeString& pattern)
{
fPattern = pattern;
- updateTimeSepFromPattern(fPattern, fSymbols);
+ parsePattern();
}
//----------------------------------------------------------------------
translatePattern(pattern, fPattern,
fSymbols->fLocalPatternChars,
UnicodeString(DateFormatSymbols::getPatternUChars()), status);
- updateTimeSepFromPattern(fPattern, fSymbols);
}
//----------------------------------------------------------------------
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 */