in intl/icu/source/i18n/smpdtfmt.cpp [3026:3898]
int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, char16_t ch, int32_t count,
UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal,
int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType,
int32_t *dayPeriod) const
{
Formattable number;
int32_t value = 0;
int32_t i;
int32_t ps = 0;
UErrorCode status = U_ZERO_ERROR;
ParsePosition pos(0);
UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
const NumberFormat *currentNumberFormat;
UnicodeString temp;
UBool gotNumber = false;
#if defined (U_DEBUG_CAL)
//fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
#endif
if (patternCharIndex == UDAT_FIELD_COUNT) {
return -start;
}
currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
if (currentNumberFormat == nullptr) {
return -start;
}
UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; // UCAL_FIELD_COUNT if irrelevant
UnicodeString hebr("hebr", 4, US_INV);
if (numericLeapMonthFormatter != nullptr) {
numericLeapMonthFormatter->setFormats(reinterpret_cast<const Format**>(¤tNumberFormat), 1);
}
// If there are any spaces here, skip over them. If we hit the end
// of the string, then fail.
for (;;) {
if (start >= text.length()) {
return -start;
}
UChar32 c = text.char32At(start);
if (!u_isUWhiteSpace(c) /*||*/ && !PatternProps::isWhiteSpace(c)) {
break;
}
start += U16_LENGTH(c);
}
pos.setIndex(start);
UBool isChineseCalendar = typeid(cal) == typeid(ChineseCalendar) ||
typeid(cal) == typeid(DangiCalendar);
// We handle a few special cases here where we need to parse
// a number value. We handle further, more generic cases below. We need
// to handle some of them here because some fields require extra processing on
// the parsed value.
if (patternCharIndex == UDAT_HOUR_OF_DAY1_FIELD || // k
patternCharIndex == UDAT_HOUR_OF_DAY0_FIELD || // H
patternCharIndex == UDAT_HOUR1_FIELD || // h
patternCharIndex == UDAT_HOUR0_FIELD || // K
(patternCharIndex == UDAT_DOW_LOCAL_FIELD && count <= 2) || // e
(patternCharIndex == UDAT_STANDALONE_DAY_FIELD && count <= 2) || // c
(patternCharIndex == UDAT_MONTH_FIELD && count <= 2) || // M
(patternCharIndex == UDAT_STANDALONE_MONTH_FIELD && count <= 2) || // L
(patternCharIndex == UDAT_QUARTER_FIELD && count <= 2) || // Q
(patternCharIndex == UDAT_STANDALONE_QUARTER_FIELD && count <= 2) || // q
patternCharIndex == UDAT_YEAR_FIELD || // y
patternCharIndex == UDAT_YEAR_WOY_FIELD || // Y
patternCharIndex == UDAT_YEAR_NAME_FIELD || // U (falls back to numeric)
(patternCharIndex == UDAT_ERA_FIELD && isChineseCalendar) || // G
patternCharIndex == UDAT_FRACTIONAL_SECOND_FIELD) // S
{
int32_t parseStart = pos.getIndex();
// It would be good to unify this with the obeyCount logic below,
// but that's going to be difficult.
const UnicodeString* src;
UBool parsedNumericLeapMonth = false;
if (numericLeapMonthFormatter != nullptr && (patternCharIndex == UDAT_MONTH_FIELD || patternCharIndex == UDAT_STANDALONE_MONTH_FIELD)) {
int32_t argCount;
Formattable * args = numericLeapMonthFormatter->parse(text, pos, argCount);
if (args != nullptr && argCount == 1 && pos.getIndex() > parseStart && args[0].isNumeric()) {
parsedNumericLeapMonth = true;
number.setLong(args[0].getLong());
cal.set(UCAL_IS_LEAP_MONTH, 1);
delete[] args;
} else {
pos.setIndex(parseStart);
cal.set(UCAL_IS_LEAP_MONTH, 0);
}
}
if (!parsedNumericLeapMonth) {
if (obeyCount) {
if ((start+count) > text.length()) {
return -start;
}
text.extractBetween(0, start + count, temp);
src = &temp;
} else {
src = &text;
}
parseInt(*src, number, pos, allowNegative,currentNumberFormat);
}
int32_t txtLoc = pos.getIndex();
if (txtLoc > parseStart) {
value = number.getLong();
gotNumber = true;
// suffix processing
if (value < 0 ) {
txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, true);
if (txtLoc != pos.getIndex()) {
value *= -1;
}
}
else {
txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, false);
}
if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) {
// Check the range of the value
int32_t bias = gFieldRangeBias[patternCharIndex];
if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) {
return -start;
}
}
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.
switch (patternCharIndex) {
case UDAT_HOUR_OF_DAY1_FIELD:
case UDAT_HOUR_OF_DAY0_FIELD:
case UDAT_HOUR1_FIELD:
case UDAT_HOUR0_FIELD:
// special range check for hours:
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:
// these must be a number
if (! gotNumber) {
return -start;
}
break;
default:
// we check the rest of the fields below.
break;
}
switch (patternCharIndex) {
case UDAT_ERA_FIELD:
if (isChineseCalendar) {
if (!gotNumber) {
return -start;
}
cal.set(UCAL_ERA, value);
return pos.getIndex();
}
if (count == 5) {
ps = matchString(text, start, UCAL_ERA, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount, nullptr, cal);
} else if (count == 4) {
ps = matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, nullptr, cal);
} else {
ps = matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, nullptr, cal);
}
// check return position, if it equals -start, then matchString error
// special case the return code so we don't necessarily fail out until we
// verify no year information also
if (ps == -start)
ps--;
return ps;
case UDAT_YEAR_FIELD:
// If there are 3 or more YEAR pattern characters, this indicates
// that the year value is to be treated literally, without any
// two-digit year adjustments (e.g., from "01" to 2001). Otherwise
// we made adjustments to place the 2-digit year in the proper
// century, for parsed strings from "00" to "99". Any other string
// is treated literally: "2250", "-1", "1", "002".
if (fDateOverride.compare(hebr)==0 && value < 1000) {
value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
} else if (text.moveIndex32(start, 2) == pos.getIndex() && !isChineseCalendar
&& u_isdigit(text.char32At(start))
&& u_isdigit(text.char32At(text.moveIndex32(start, 1))))
{
// only adjust year for patterns less than 3.
if(count < 3) {
// Assume for example that the defaultCenturyStart is 6/18/1903.
// This means that two-digit years will be forced into the range
// 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
// correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
// to 1904, 1905, etc. If the year is 03, then it is 2003 if the
// other fields specify a date before 6/18, or 1903 if they specify a
// date afterwards. As a result, 03 is an ambiguous year. All other
// two-digit years are unambiguous.
if(fHaveDefaultCentury) { // check if this formatter even has a pivot year
int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
ambiguousYear[0] = (value == ambiguousTwoDigitYear);
value += (fDefaultCenturyStartYear/100)*100 +
(value < ambiguousTwoDigitYear ? 100 : 0);
}
}
}
cal.set(UCAL_YEAR, value);
// Delayed checking for adjustment of Hebrew month numbers in non-leap years.
if (saveHebrewMonth >= 0) {
HebrewCalendar *hc = (HebrewCalendar*)&cal;
if (!hc->isLeapYear(value) && saveHebrewMonth >= 6) {
cal.set(UCAL_MONTH,saveHebrewMonth);
} else {
cal.set(UCAL_MONTH,saveHebrewMonth-1);
}
saveHebrewMonth = -1;
}
return pos.getIndex();
case UDAT_YEAR_WOY_FIELD:
// Comment is the same as for UDAT_Year_FIELDs - look above
if (fDateOverride.compare(hebr)==0 && value < 1000) {
value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
} else if (text.moveIndex32(start, 2) == pos.getIndex()
&& u_isdigit(text.char32At(start))
&& u_isdigit(text.char32At(text.moveIndex32(start, 1)))
&& fHaveDefaultCentury )
{
int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
ambiguousYear[0] = (value == ambiguousTwoDigitYear);
value += (fDefaultCenturyStartYear/100)*100 +
(value < ambiguousTwoDigitYear ? 100 : 0);
}
cal.set(UCAL_YEAR_WOY, value);
return pos.getIndex();
case UDAT_YEAR_NAME_FIELD:
if (fSymbols->fShortYearNames != nullptr) {
int32_t newStart = matchString(text, start, UCAL_YEAR, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount, nullptr, cal);
if (newStart > 0) {
return newStart;
}
}
if (gotNumber && (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC,status) || value > fSymbols->fShortYearNamesCount)) {
cal.set(UCAL_YEAR, value);
return pos.getIndex();
}
return -start;
case UDAT_MONTH_FIELD:
case UDAT_STANDALONE_MONTH_FIELD:
if (gotNumber) // i.e., M or MM.
{
// When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
// or not it was a leap year. We may or may not yet know what year it is, so might have to delay checking until
// the year is parsed.
if (typeid(cal) == typeid(HebrewCalendar)) {
HebrewCalendar *hc = (HebrewCalendar*)&cal;
if (cal.isSet(UCAL_YEAR)) {
UErrorCode monthStatus = U_ZERO_ERROR;
if (!hc->isLeapYear(hc->get(UCAL_YEAR, monthStatus)) && value >= 6) {
cal.set(UCAL_MONTH, value);
} else {
cal.set(UCAL_MONTH, value - 1);
}
} else {
saveHebrewMonth = value;
}
} else {
// Don't want to parse the month if it is a string
// while pattern uses numeric style: M/MM, L/LL
// [We computed 'value' above.]
cal.set(UCAL_MONTH, value - 1);
}
return pos.getIndex();
} else {
// count >= 3 // i.e., MMM/MMMM, LLL/LLLL
// Want to be able to parse both short and long forms.
// Try count == 4 first:
UnicodeString * wideMonthPat = nullptr;
UnicodeString * shortMonthPat = nullptr;
if (fSymbols->fLeapMonthPatterns != nullptr && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
if (patternCharIndex==UDAT_MONTH_FIELD) {
wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide];
shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev];
} else {
wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide];
shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev];
}
}
int32_t newStart = 0;
if (patternCharIndex==UDAT_MONTH_FIELD) {
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) && count>=3 && count <=4 &&
fSymbols->fLeapMonthPatterns==nullptr && fSymbols->fMonthsCount==fSymbols->fShortMonthsCount) {
// single function to check both wide and short, an experiment
newStart = matchAlphaMonthStrings(text, start, fSymbols->fMonths, fSymbols->fShortMonths, fSymbols->fMonthsCount, cal); // try MMMM,MMM
if (newStart > 0) {
return newStart;
}
}
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
newStart = matchString(text, start, UCAL_MONTH, fSymbols->fMonths, fSymbols->fMonthsCount, wideMonthPat, cal); // try MMMM
if (newStart > 0) {
return newStart;
}
}
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
newStart = matchString(text, start, UCAL_MONTH, fSymbols->fShortMonths, fSymbols->fShortMonthsCount, shortMonthPat, cal); // try MMM
}
} else {
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) && count>=3 && count <=4 &&
fSymbols->fLeapMonthPatterns==nullptr && fSymbols->fStandaloneMonthsCount==fSymbols->fStandaloneShortMonthsCount) {
// single function to check both wide and short, an experiment
newStart = matchAlphaMonthStrings(text, start, fSymbols->fStandaloneMonths, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneMonthsCount, cal); // try MMMM,MMM
if (newStart > 0) {
return newStart;
}
}
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, wideMonthPat, cal); // try LLLL
if (newStart > 0) {
return newStart;
}
}
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, shortMonthPat, cal); // try LLL
}
}
if (newStart > 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) // currently we do not try to parse MMMMM/LLLLL: #8860
return newStart;
// else we allowing parsing as number, below
}
break;
case UDAT_HOUR_OF_DAY1_FIELD:
// [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();
case UDAT_FRACTIONAL_SECOND_FIELD:
// Fractional seconds left-justify
i = countDigits(text, start, pos.getIndex());
if (i < 3) {
while (i < 3) {
value *= 10;
i++;
}
} else {
int32_t a = 1;
while (i > 3) {
a *= 10;
i--;
}
value /= a;
}
cal.set(UCAL_MILLISECOND, value);
return pos.getIndex();
case UDAT_DOW_LOCAL_FIELD:
if (gotNumber) // i.e., e or ee
{
// [We computed 'value' above.]
cal.set(UCAL_DOW_LOCAL, 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.
// Try count == 4 (EEEE) wide first:
int32_t newStart = 0;
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
fSymbols->fWeekdays, fSymbols->fWeekdaysCount, nullptr, cal)) > 0)
return newStart;
}
// EEEE wide failed, now try EEE abbreviated
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, nullptr, cal)) > 0)
return newStart;
}
// EEE abbreviated failed, now try EEEEEE short
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 6) {
if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
fSymbols->fShorterWeekdays, fSymbols->fShorterWeekdaysCount, nullptr, cal)) > 0)
return newStart;
}
// EEEEEE short failed, now try EEEEE narrow
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
fSymbols->fNarrowWeekdays, fSymbols->fNarrowWeekdaysCount, nullptr, cal)) > 0)
return newStart;
}
if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status) || patternCharIndex == UDAT_DAY_OF_WEEK_FIELD)
return newStart;
// else we allowing parsing as number, below
}
break;
case UDAT_STANDALONE_DAY_FIELD:
{
if (gotNumber) // c or cc
{
// [We computed 'value' above.]
cal.set(UCAL_DOW_LOCAL, value);
return pos.getIndex();
}
// Want to be able to parse both short and long forms.
// Try count == 4 (cccc) first:
int32_t newStart = 0;
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, nullptr, cal)) > 0)
return newStart;
}
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, nullptr, cal)) > 0)
return newStart;
}
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 6) {
if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
fSymbols->fStandaloneShorterWeekdays, fSymbols->fStandaloneShorterWeekdaysCount, nullptr, cal)) > 0)
return newStart;
}
if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
return newStart;
// else we allowing parsing as number, below
}
break;
case UDAT_AM_PM_FIELD:
{
// optionally try both wide/abbrev and narrow forms
int32_t newStart = 0;
// try wide/abbrev
if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count < 5 ) {
if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, nullptr, cal)) > 0) {
return newStart;
}
}
// try narrow
if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count >= 5 ) {
if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fNarrowAmPms, fSymbols->fNarrowAmPmsCount, nullptr, cal)) > 0) {
return newStart;
}
}
// no matches for given options
return -start;
}
case UDAT_HOUR1_FIELD:
// [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();
case UDAT_QUARTER_FIELD:
if (gotNumber) // i.e., Q or QQ.
{
// Don't want to parse the month if it is a string
// while pattern uses numeric style: Q or QQ.
// [We computed 'value' above.]
cal.set(UCAL_MONTH, (value - 1) * 3);
return pos.getIndex();
} else {
// count >= 3 // i.e., QQQ or QQQQ
// Want to be able to parse short, long, and narrow forms.
// Try count == 4 first:
int32_t newStart = 0;
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
fSymbols->fQuarters, fSymbols->fQuartersCount, cal)) > 0)
return newStart;
}
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal)) > 0)
return newStart;
}
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
fSymbols->fNarrowQuarters, fSymbols->fNarrowQuartersCount, cal)) > 0)
return newStart;
}
if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
return newStart;
// else we allowing parsing as number, below
if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status))
return -start;
}
break;
case UDAT_STANDALONE_QUARTER_FIELD:
if (gotNumber) // i.e., q or qq.
{
// Don't want to parse the month if it is a string
// while pattern uses numeric style: q or q.
// [We computed 'value' above.]
cal.set(UCAL_MONTH, (value - 1) * 3);
return pos.getIndex();
} else {
// count >= 3 // i.e., qqq or qqqq
// Want to be able to parse both short and long forms.
// Try count == 4 first:
int32_t newStart = 0;
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount, cal)) > 0)
return newStart;
}
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal)) > 0)
return newStart;
}
if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
fSymbols->fStandaloneNarrowQuarters, fSymbols->fStandaloneNarrowQuartersCount, cal)) > 0)
return newStart;
}
if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
return newStart;
// else we allowing parsing as number, below
if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status))
return -start;
}
break;
case UDAT_TIMEZONE_FIELD: // 'z'
{
UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_SPECIFIC_SHORT : UTZFMT_STYLE_SPECIFIC_LONG;
const TimeZoneFormat *tzfmt = tzFormat(status);
if (U_SUCCESS(status)) {
TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
if (tz != nullptr) {
cal.adoptTimeZone(tz);
return pos.getIndex();
}
}
return -start;
}
break;
case UDAT_TIMEZONE_RFC_FIELD: // 'Z'
{
UTimeZoneFormatStyle style = (count < 4) ?
UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL : ((count == 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL: UTZFMT_STYLE_LOCALIZED_GMT);
const TimeZoneFormat *tzfmt = tzFormat(status);
if (U_SUCCESS(status)) {
TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
if (tz != nullptr) {
cal.adoptTimeZone(tz);
return pos.getIndex();
}
}
return -start;
}
case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
{
UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_GENERIC_SHORT : UTZFMT_STYLE_GENERIC_LONG;
const TimeZoneFormat *tzfmt = tzFormat(status);
if (U_SUCCESS(status)) {
TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
if (tz != nullptr) {
cal.adoptTimeZone(tz);
return pos.getIndex();
}
}
return -start;
}
case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
{
UTimeZoneFormatStyle style;
switch (count) {
case 1:
style = UTZFMT_STYLE_ZONE_ID_SHORT;
break;
case 2:
style = UTZFMT_STYLE_ZONE_ID;
break;
case 3:
style = UTZFMT_STYLE_EXEMPLAR_LOCATION;
break;
default:
style = UTZFMT_STYLE_GENERIC_LOCATION;
break;
}
const TimeZoneFormat *tzfmt = tzFormat(status);
if (U_SUCCESS(status)) {
TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
if (tz != nullptr) {
cal.adoptTimeZone(tz);
return pos.getIndex();
}
}
return -start;
}
case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O'
{
UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT : UTZFMT_STYLE_LOCALIZED_GMT;
const TimeZoneFormat *tzfmt = tzFormat(status);
if (U_SUCCESS(status)) {
TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
if (tz != nullptr) {
cal.adoptTimeZone(tz);
return pos.getIndex();
}
}
return -start;
}
case UDAT_TIMEZONE_ISO_FIELD: // 'X'
{
UTimeZoneFormatStyle style;
switch (count) {
case 1:
style = UTZFMT_STYLE_ISO_BASIC_SHORT;
break;
case 2:
style = UTZFMT_STYLE_ISO_BASIC_FIXED;
break;
case 3:
style = UTZFMT_STYLE_ISO_EXTENDED_FIXED;
break;
case 4:
style = UTZFMT_STYLE_ISO_BASIC_FULL;
break;
default:
style = UTZFMT_STYLE_ISO_EXTENDED_FULL;
break;
}
const TimeZoneFormat *tzfmt = tzFormat(status);
if (U_SUCCESS(status)) {
TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
if (tz != nullptr) {
cal.adoptTimeZone(tz);
return pos.getIndex();
}
}
return -start;
}
case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x'
{
UTimeZoneFormatStyle style;
switch (count) {
case 1:
style = UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT;
break;
case 2:
style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED;
break;
case 3:
style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED;
break;
case 4:
style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL;
break;
default:
style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL;
break;
}
const TimeZoneFormat *tzfmt = tzFormat(status);
if (U_SUCCESS(status)) {
TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
if (tz != nullptr) {
cal.adoptTimeZone(tz);
return pos.getIndex();
}
}
return -start;
}
// 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 char16_t def_sep = DateFormatSymbols::DEFAULT_TIME_SEPARATOR;
static const char16_t alt_sep = DateFormatSymbols::ALTERNATE_TIME_SEPARATOR;
// Try matching a time separator.
int32_t count_sep = 1;
UnicodeString data[3];
fSymbols->getTimeSeparatorString(data[0]);
// Add the default, if different from the locale.
if (data[0].compare(&def_sep, 1) != 0) {
data[count_sep++].setTo(def_sep);
}
// If lenient, add also the alternate, if different from the locale.
if (isLenient() && data[0].compare(&alt_sep, 1) != 0) {
data[count_sep++].setTo(alt_sep);
}
return matchString(text, start, UCAL_FIELD_COUNT /* => nothing to set */, data, count_sep, nullptr, cal);
}
case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
{
U_ASSERT(dayPeriod != nullptr);
int32_t ampmStart = subParse(text, start, 0x61, count,
obeyCount, allowNegative, ambiguousYear, saveHebrewMonth, cal,
patLoc, numericLeapMonthFormatter, tzTimeType);
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 != nullptr);
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
break;
}
// Handle "generic" fields:
// switch default case now handled here (outside switch block) to allow
// parsing of some string fields as digits for lenient case
int32_t parseStart = pos.getIndex();
const UnicodeString* src;
if (obeyCount) {
if ((start+count) > text.length()) {
return -start;
}
text.extractBetween(0, start + count, temp);
src = &temp;
} else {
src = &text;
}
parseInt(*src, number, pos, allowNegative,currentNumberFormat);
if (obeyCount && !isLenient() && pos.getIndex() < start + count) {
return -start;
}
if (pos.getIndex() != parseStart) {
int32_t val = number.getLong();
// Don't need suffix processing here (as in number processing at the beginning of the function);
// the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) {
// Check the range of the value
int32_t bias = gFieldRangeBias[patternCharIndex];
if (bias >= 0 && (val > cal.getMaximum(field) + bias || val < cal.getMinimum(field) + bias)) {
return -start;
}
}
// For the following, need to repeat some of the "if (gotNumber)" code above:
// UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
// UDAT_[STANDALONE_]QUARTER_FIELD
switch (patternCharIndex) {
case UDAT_MONTH_FIELD:
// See notes under UDAT_MONTH_FIELD case above
if (typeid(cal) == typeid(HebrewCalendar)) {
HebrewCalendar *hc = (HebrewCalendar*)&cal;
if (cal.isSet(UCAL_YEAR)) {
UErrorCode monthStatus = U_ZERO_ERROR;
if (!hc->isLeapYear(hc->get(UCAL_YEAR, monthStatus)) && val >= 6) {
cal.set(UCAL_MONTH, val);
} else {
cal.set(UCAL_MONTH, val - 1);
}
} else {
saveHebrewMonth = val;
}
} else {
cal.set(UCAL_MONTH, val - 1);
}
break;
case UDAT_STANDALONE_MONTH_FIELD:
cal.set(UCAL_MONTH, val - 1);
break;
case UDAT_DOW_LOCAL_FIELD:
case UDAT_STANDALONE_DAY_FIELD:
cal.set(UCAL_DOW_LOCAL, val);
break;
case UDAT_QUARTER_FIELD:
case UDAT_STANDALONE_QUARTER_FIELD:
cal.set(UCAL_MONTH, (val - 1) * 3);
break;
case UDAT_RELATED_YEAR_FIELD:
cal.setRelatedYear(val);
break;
default:
cal.set(field, val);
break;
}
return pos.getIndex();
}
return -start;
}