X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/48b980fed3435926e0b3a8d72ecb58be703a1c7a..729e4ab9bc6618bc3d8a898e575df7f4019e29ca:/icuSources/i18n/smpdtfmt.cpp diff --git a/icuSources/i18n/smpdtfmt.cpp b/icuSources/i18n/smpdtfmt.cpp index 5b93dc49..8dde9ac1 100644 --- a/icuSources/i18n/smpdtfmt.cpp +++ b/icuSources/i18n/smpdtfmt.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 1997-2009, International Business Machines Corporation and * +* Copyright (C) 1997-2010, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * @@ -48,7 +48,9 @@ #include "unicode/vtzone.h" #include "olsontz.h" #include "util.h" -#include "gregoimp.h" +#include "fphdlimp.h" +#include "gregoimp.h" +#include "hebrwcal.h" #include "cstring.h" #include "uassert.h" #include "zstrfmt.h" @@ -83,15 +85,58 @@ static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */ static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */ static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */ +static const UChar gUt[] = {0x0055, 0x0054, 0x0000}; // "UT" +static const UChar gUtc[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT" + typedef enum GmtPatSize { kGmtLen = 3, kGmtPatLen = 6, kNegHmsLen = 9, kNegHmLen = 6, kPosHmsLen = 9, - kPosHmLen = 6 + kPosHmLen = 6, + kUtLen = 2, + kUtcLen = 3 } GmtPatSize; +// Stuff needed for numbering system overrides + +typedef enum OvrStrType { + kOvrStrDate = 0, + kOvrStrTime = 1, + kOvrStrBoth = 2 +} OvrStrType; + +static const UDateFormatField kDateFields[] = { + UDAT_YEAR_FIELD, + UDAT_MONTH_FIELD, + UDAT_DATE_FIELD, + UDAT_DAY_OF_YEAR_FIELD, + UDAT_DAY_OF_WEEK_IN_MONTH_FIELD, + UDAT_WEEK_OF_YEAR_FIELD, + UDAT_WEEK_OF_MONTH_FIELD, + UDAT_YEAR_WOY_FIELD, + UDAT_EXTENDED_YEAR_FIELD, + UDAT_JULIAN_DAY_FIELD, + UDAT_STANDALONE_DAY_FIELD, + UDAT_STANDALONE_MONTH_FIELD, + UDAT_QUARTER_FIELD, + UDAT_STANDALONE_QUARTER_FIELD }; +static const int8_t kDateFieldsCount = 13; + +static const UDateFormatField kTimeFields[] = { + UDAT_HOUR_OF_DAY1_FIELD, + UDAT_HOUR_OF_DAY0_FIELD, + UDAT_MINUTE_FIELD, + UDAT_SECOND_FIELD, + UDAT_FRACTIONAL_SECOND_FIELD, + UDAT_HOUR1_FIELD, + UDAT_HOUR0_FIELD, + UDAT_MILLISECONDS_IN_DAY_FIELD, + UDAT_TIMEZONE_RFC_FIELD }; +static const int8_t kTimeFieldsCount = 9; + + // This is a pattern-of-last-resort used when we can't load a usable pattern out // of a resource. static const UChar gDefaultPattern[] = @@ -112,14 +157,6 @@ static const char gDateTimePatternsTag[]="DateTimePatterns"; static const UChar gEtcUTC[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC" static const UChar QUOTE = 0x27; // Single quote -enum { - kGMTNegativeHMS = 0, - kGMTNegativeHM, - kGMTPositiveHMS, - kGMTPositiveHM, - - kNumGMTFormatters -}; /* * The field range check bias for each UDateFormatField. @@ -179,6 +216,17 @@ SimpleDateFormat::~SimpleDateFormat() } } uprv_free(fGMTFormatters); + + } + if (fNumberFormatters) { + uprv_free(fNumberFormatters); + } + + while (fOverrideList) { + NSOverride *cur = fOverrideList; + fOverrideList = cur->next; + delete cur->nf; + uprv_free(cur); } } @@ -187,7 +235,9 @@ SimpleDateFormat::~SimpleDateFormat() SimpleDateFormat::SimpleDateFormat(UErrorCode& status) : fLocale(Locale::getDefault()), fSymbols(NULL), - fGMTFormatters(NULL) + fGMTFormatters(NULL), + fNumberFormatters(NULL), + fOverrideList(NULL) { construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status); initializeDefaultCentury(); @@ -200,11 +250,37 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, : fPattern(pattern), fLocale(Locale::getDefault()), fSymbols(NULL), - fGMTFormatters(NULL) + fGMTFormatters(NULL), + fNumberFormatters(NULL), + fOverrideList(NULL) +{ + fDateOverride.setToBogus(); + fTimeOverride.setToBogus(); + initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); + initialize(fLocale, status); + initializeDefaultCentury(); + +} +//---------------------------------------------------------------------- + +SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, + const UnicodeString& override, + UErrorCode &status) +: fPattern(pattern), + fLocale(Locale::getDefault()), + fSymbols(NULL), + fGMTFormatters(NULL), + fNumberFormatters(NULL), + fOverrideList(NULL) { + fDateOverride.setTo(override); + fTimeOverride.setToBogus(); initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); initialize(fLocale, status); initializeDefaultCentury(); + + processOverrideString(fLocale,override,kOvrStrBoth,status); + } //---------------------------------------------------------------------- @@ -214,8 +290,14 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, UErrorCode& status) : fPattern(pattern), fLocale(locale), - fGMTFormatters(NULL) + fGMTFormatters(NULL), + fNumberFormatters(NULL), + fOverrideList(NULL) { + + fDateOverride.setToBogus(); + fTimeOverride.setToBogus(); + initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); initialize(fLocale, status); initializeDefaultCentury(); @@ -223,14 +305,44 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, //---------------------------------------------------------------------- +SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, + const UnicodeString& override, + const Locale& locale, + UErrorCode& status) +: fPattern(pattern), + fLocale(locale), + fGMTFormatters(NULL), + fNumberFormatters(NULL), + fOverrideList(NULL) +{ + + fDateOverride.setTo(override); + fTimeOverride.setToBogus(); + + initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); + initialize(fLocale, status); + initializeDefaultCentury(); + + processOverrideString(locale,override,kOvrStrBoth,status); + +} + +//---------------------------------------------------------------------- + SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, DateFormatSymbols* symbolsToAdopt, UErrorCode& status) : fPattern(pattern), fLocale(Locale::getDefault()), fSymbols(symbolsToAdopt), - fGMTFormatters(NULL) + fGMTFormatters(NULL), + fNumberFormatters(NULL), + fOverrideList(NULL) { + + fDateOverride.setToBogus(); + fTimeOverride.setToBogus(); + initializeCalendar(NULL,fLocale,status); initialize(fLocale, status); initializeDefaultCentury(); @@ -244,8 +356,14 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, : fPattern(pattern), fLocale(Locale::getDefault()), fSymbols(new DateFormatSymbols(symbols)), - fGMTFormatters(NULL) + fGMTFormatters(NULL), + fNumberFormatters(NULL), + fOverrideList(NULL) { + + fDateOverride.setToBogus(); + fTimeOverride.setToBogus(); + initializeCalendar(NULL, fLocale, status); initialize(fLocale, status); initializeDefaultCentury(); @@ -260,7 +378,9 @@ SimpleDateFormat::SimpleDateFormat(EStyle timeStyle, UErrorCode& status) : fLocale(locale), fSymbols(NULL), - fGMTFormatters(NULL) + fGMTFormatters(NULL), + fNumberFormatters(NULL), + fOverrideList(NULL) { construct(timeStyle, dateStyle, fLocale, status); if(U_SUCCESS(status)) { @@ -280,7 +400,9 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale, : fPattern(gDefaultPattern), fLocale(locale), fSymbols(NULL), - fGMTFormatters(NULL) + fGMTFormatters(NULL), + fNumberFormatters(NULL), + fOverrideList(NULL) { if (U_FAILURE(status)) return; initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status); @@ -297,6 +419,9 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale, } } + fDateOverride.setToBogus(); + fTimeOverride.setToBogus(); + initialize(fLocale, status); if(U_SUCCESS(status)) { initializeDefaultCentury(); @@ -308,7 +433,9 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale, SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other) : DateFormat(other), fSymbols(NULL), - fGMTFormatters(NULL) + fGMTFormatters(NULL), + fNumberFormatters(NULL), + fOverrideList(NULL) { *this = other; } @@ -379,6 +506,8 @@ void SimpleDateFormat::construct(EStyle timeStyle, CalendarData calData(locale, fCalendar?fCalendar->getType():NULL, status); UResourceBundle *dateTimePatterns = calData.getByKey(gDateTimePatternsTag, status); + UResourceBundle *currentBundle; + if (U_FAILURE(status)) return; if (ures_getSize(dateTimePatterns) <= kDateTime) @@ -399,8 +528,10 @@ void SimpleDateFormat::construct(EStyle timeStyle, return; } - const UChar *resStr; - int32_t resStrLen = 0; + const UChar *resStr,*ovrStr; + int32_t resStrLen,ovrStrLen = 0; + fDateOverride.setToBogus(); + fTimeOverride.setToBogus(); // 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 @@ -413,7 +544,31 @@ void SimpleDateFormat::construct(EStyle timeStyle, // use Formattable::adoptString() so that we can use fastCopyFrom() // instead of Formattable::setString()'s unaware, safe, deep string clone // see Jitterbug 2296 - resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)timeStyle, &resStrLen, &status); + + currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)timeStyle, NULL, &status); + if (U_FAILURE(status)) { + status = U_INVALID_FORMAT_ERROR; + return; + } + switch (ures_getType(currentBundle)) { + case URES_STRING: { + resStr = ures_getString(currentBundle, &resStrLen, &status); + break; + } + case URES_ARRAY: { + resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); + ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); + fTimeOverride.setTo(TRUE, ovrStr, ovrStrLen); + break; + } + default: { + status = U_INVALID_FORMAT_ERROR; + ures_close(currentBundle); + return; + } + } + ures_close(currentBundle); + UnicodeString *tempus1 = new UnicodeString(TRUE, resStr, resStrLen); // NULL pointer check if (tempus1 == NULL) { @@ -421,8 +576,31 @@ void SimpleDateFormat::construct(EStyle timeStyle, return; } timeDateArray[0].adoptString(tempus1); - - resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)dateStyle, &resStrLen, &status); + + currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)dateStyle, NULL, &status); + if (U_FAILURE(status)) { + status = U_INVALID_FORMAT_ERROR; + return; + } + switch (ures_getType(currentBundle)) { + case URES_STRING: { + resStr = ures_getString(currentBundle, &resStrLen, &status); + break; + } + case URES_ARRAY: { + resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); + ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); + fDateOverride.setTo(TRUE, ovrStr, ovrStrLen); + break; + } + default: { + status = U_INVALID_FORMAT_ERROR; + ures_close(currentBundle); + return; + } + } + ures_close(currentBundle); + UnicodeString *tempus2 = new UnicodeString(TRUE, resStr, resStrLen); // Null pointer check if (tempus2 == NULL) { @@ -431,21 +609,72 @@ void SimpleDateFormat::construct(EStyle timeStyle, } timeDateArray[1].adoptString(tempus2); - resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)kDateTime, &resStrLen, &status); + int32_t glueIndex = kDateTime; + int32_t patternsSize = ures_getSize(dateTimePatterns); + if (patternsSize >= (kDateTimeOffset + kShort + 1)) { + // Get proper date time format + glueIndex = (int32_t)(kDateTimeOffset + (dateStyle - kDateOffset)); + } + + resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &status); MessageFormat::format(UnicodeString(TRUE, resStr, resStrLen), timeDateArray, 2, fPattern, status); } // if the pattern includes just time data or just date date, load the appropriate // pattern string from the resources // setTo() - see DateFormatSymbols::assignArray comments else if (timeStyle != kNone) { - resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)timeStyle, &resStrLen, &status); + currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)timeStyle, NULL, &status); + if (U_FAILURE(status)) { + status = U_INVALID_FORMAT_ERROR; + return; + } + switch (ures_getType(currentBundle)) { + case URES_STRING: { + resStr = ures_getString(currentBundle, &resStrLen, &status); + break; + } + case URES_ARRAY: { + resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); + ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); + fDateOverride.setTo(TRUE, ovrStr, ovrStrLen); + break; + } + default: { + status = U_INVALID_FORMAT_ERROR; + ures_close(currentBundle); + return; + } + } fPattern.setTo(TRUE, resStr, resStrLen); + ures_close(currentBundle); } else if (dateStyle != kNone) { - resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)dateStyle, &resStrLen, &status); + currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)dateStyle, NULL, &status); + if (U_FAILURE(status)) { + status = U_INVALID_FORMAT_ERROR; + return; + } + switch (ures_getType(currentBundle)) { + case URES_STRING: { + resStr = ures_getString(currentBundle, &resStrLen, &status); + break; + } + case URES_ARRAY: { + resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); + ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); + fDateOverride.setTo(TRUE, ovrStr, ovrStrLen); + break; + } + default: { + status = U_INVALID_FORMAT_ERROR; + ures_close(currentBundle); + return; + } + } fPattern.setTo(TRUE, resStr, resStrLen); + ures_close(currentBundle); } - + // and if it includes _neither_, that's an error else status = U_INVALID_FORMAT_ERROR; @@ -500,13 +729,18 @@ SimpleDateFormat::initialize(const Locale& locale, // show the decimal point, and recognizes integers only when parsing fNumberFormat->setGroupingUsed(FALSE); - if (fNumberFormat->getDynamicClassID() == DecimalFormat::getStaticClassID()) - ((DecimalFormat*)fNumberFormat)->setDecimalSeparatorAlwaysShown(FALSE); + DecimalFormat* decfmt = dynamic_cast(fNumberFormat); + if (decfmt != NULL) { + decfmt->setDecimalSeparatorAlwaysShown(FALSE); + } fNumberFormat->setParseIntegerOnly(TRUE); fNumberFormat->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00" - - // TODO: Really, the default should be lenient... - fNumberFormat->setParseStrict(FALSE); + + // TODO: Really, the default should be lenient... + fNumberFormat->setParseStrict(FALSE); + + initNumberFormatters(locale,status); + } else if (U_SUCCESS(status)) { @@ -517,7 +751,7 @@ SimpleDateFormat::initialize(const Locale& locale, /* Initialize the fields we use to disambiguate ambiguous years. Separate * so we can call it from readObject(). */ -void SimpleDateFormat::initializeDefaultCentury() +void SimpleDateFormat::initializeDefaultCentury() { if(fCalendar) { fHaveDefaultCentury = fCalendar->haveDefaultCentury(); @@ -534,7 +768,7 @@ void SimpleDateFormat::initializeDefaultCentury() /* Define one-century window into which to disambiguate dates using * two-digit years. Make public in JDK 1.2. */ -void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status) +void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status) { if(U_FAILURE(status)) { return; @@ -543,7 +777,7 @@ void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& s status = U_ILLEGAL_ARGUMENT_ERROR; return; } - + fCalendar->setTime(startDate, status); if(U_SUCCESS(status)) { fHaveDefaultCentury = TRUE; @@ -551,28 +785,58 @@ void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& s fDefaultCenturyStartYear = fCalendar->get(UCAL_YEAR, status); } } - + //---------------------------------------------------------------------- UnicodeString& SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const { - UErrorCode status = U_ZERO_ERROR; - pos.setBeginIndex(0); - pos.setEndIndex(0); + UErrorCode status = U_ZERO_ERROR; + FieldPositionOnlyHandler handler(pos); + return _format(cal, appendTo, handler, status); +} + +//---------------------------------------------------------------------- + +UnicodeString& +SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, + FieldPositionIterator* posIter, UErrorCode& status) const +{ + FieldPositionIteratorHandler handler(posIter, status); + return _format(cal, appendTo, handler, status); +} + +//---------------------------------------------------------------------- + +UnicodeString& +SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, FieldPositionHandler& handler, + UErrorCode& status) const +{ + Calendar *workCal = &cal; + TimeZone *backupTZ = NULL; + if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) { + // Different calendar type + // We use the time and time zone from the input calendar, but + // do not use the input calendar for field calculation. + UDate t = cal.getTime(status); + fCalendar->setTime(t, status); + backupTZ = fCalendar->getTimeZone().clone(); + fCalendar->setTimeZone(cal.getTimeZone()); + workCal = fCalendar; + } UBool inQuote = FALSE; UChar prevCh = 0; int32_t count = 0; - + // loop through the pattern string character by character for (int32_t i = 0; i < fPattern.length() && U_SUCCESS(status); ++i) { UChar ch = fPattern[i]; - + // Use subFormat() to format a repeated pattern character // when a different pattern or non-pattern character is seen if (ch != prevCh && count > 0) { - subFormat(appendTo, prevCh, count, pos, cal, status); + subFormat(appendTo, prevCh, count, handler, *workCal, status); count = 0; } if (ch == QUOTE) { @@ -584,8 +848,8 @@ SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& } else { inQuote = ! inQuote; } - } - else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) + } + else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { // ch is a date-time pattern character to be interpreted // by subFormat(); count the number of times it is repeated @@ -600,31 +864,15 @@ SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& // Format the last item in the pattern, if any if (count > 0) { - subFormat(appendTo, prevCh, count, pos, cal, status); + subFormat(appendTo, prevCh, count, handler, *workCal, status); } - // and if something failed (e.g., an invalid format character), reset our FieldPosition - // to (0, 0) to show that - // {sfb} look at this later- are these being set correctly? - if (U_FAILURE(status)) { - pos.setBeginIndex(0); - pos.setEndIndex(0); + if (backupTZ != NULL) { + // Restore the original time zone + fCalendar->adoptTimeZone(backupTZ); } - - return appendTo; -} -UnicodeString& -SimpleDateFormat::format(const Formattable& obj, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const -{ - // this is just here to get around the hiding problem - // (the previous format() override would hide the version of - // format() on DateFormat that this function correspond to, so we - // have to redefine it here) - return DateFormat::format(obj, appendTo, pos, status); + return appendTo; } //---------------------------------------------------------------------- @@ -642,10 +890,10 @@ SimpleDateFormat::fgCalendarFieldToLevel[] = /*wW*/ 20, 30, /*dDEF*/ 30, 20, 30, 30, /*ahHm*/ 40, 50, 50, 60, - /*sS..*/ 70, 80, - /*z?Y*/ 0, 0, 10, + /*sS..*/ 70, 80, + /*z?Y*/ 0, 0, 10, /*eug*/ 30, 10, 0, - /*A*/ 40 + /*A*/ 40 }; @@ -724,13 +972,13 @@ _appendSymbol(UnicodeString& dst, //--------------------------------------------------------------------- void -SimpleDateFormat::appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const{ +SimpleDateFormat::appendGMT(NumberFormat *currentNumberFormat,UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const{ int32_t offset = cal.get(UCAL_ZONE_OFFSET, status) + cal.get(UCAL_DST_OFFSET, status); if (U_FAILURE(status)) { return; } if (isDefaultGMTFormat()) { - formatGMTDefault(appendTo, offset); + formatGMTDefault(currentNumberFormat,appendTo, offset); } else { ((SimpleDateFormat*)this)->initGMTFormatters(status); if (U_SUCCESS(status)) { @@ -769,7 +1017,8 @@ SimpleDateFormat::parseGMT(const UnicodeString &text, ParsePosition &pos) const // Try negative Hms fGMTFormatters[kGMTNegativeHMS]->parseObject(text, parsed, pos); - if (pos.getErrorIndex() == -1 && pos.getIndex() > start) { + if (pos.getErrorIndex() == -1 && + (pos.getIndex() - start) >= fGMTFormatHmsMinLen[kGMTNegativeHMSMinLenIdx]) { parsed.getArray(parsedCount); if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) { return (int32_t)(-1 * (int64_t)parsed[0].getDate()); @@ -782,7 +1031,8 @@ SimpleDateFormat::parseGMT(const UnicodeString &text, ParsePosition &pos) const // Try positive Hms fGMTFormatters[kGMTPositiveHMS]->parseObject(text, parsed, pos); - if (pos.getErrorIndex() == -1 && pos.getIndex() > start) { + if (pos.getErrorIndex() == -1 && + (pos.getIndex() - start) >= fGMTFormatHmsMinLen[kGMTPositiveHMSMinLenIdx]) { parsed.getArray(parsedCount); if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) { return (int32_t)((int64_t)parsed[0].getDate()); @@ -826,7 +1076,7 @@ SimpleDateFormat::parseGMT(const UnicodeString &text, ParsePosition &pos) const } void -SimpleDateFormat::formatGMTDefault(UnicodeString &appendTo, int32_t offset) const { +SimpleDateFormat::formatGMTDefault(NumberFormat *currentNumberFormat,UnicodeString &appendTo, int32_t offset) const { if (offset < 0) { appendTo += gGmtMinus; offset = -offset; // suppress the '-' sign for text display. @@ -841,31 +1091,35 @@ SimpleDateFormat::formatGMTDefault(UnicodeString &appendTo, int32_t offset) cons int32_t hour = offset / 60; - zeroPaddingNumber(appendTo, hour, 2, 2); + zeroPaddingNumber(currentNumberFormat,appendTo, hour, 2, 2); appendTo += (UChar)0x003A /*':'*/; - zeroPaddingNumber(appendTo, min, 2, 2); + zeroPaddingNumber(currentNumberFormat,appendTo, min, 2, 2); if (sec != 0) { appendTo += (UChar)0x003A /*':'*/; - zeroPaddingNumber(appendTo, sec, 2, 2); + zeroPaddingNumber(currentNumberFormat,appendTo, sec, 2, 2); } } int32_t SimpleDateFormat::parseGMTDefault(const UnicodeString &text, ParsePosition &pos) const { int32_t start = pos.getIndex(); + NumberFormat *currentNumberFormat = getNumberFormatByIndex(UDAT_TIMEZONE_RFC_FIELD); - if (start + kGmtLen + 1 >= text.length()) { + if (start + kUtLen + 1 >= text.length()) { pos.setErrorIndex(start); return 0; } int32_t cur = start; // "GMT" - if (text.compare(start, kGmtLen, gGmt) != 0) { + if (text.compare(start, kGmtLen, gGmt) == 0) { + cur += kGmtLen; + } else if (text.compare(start, kUtLen, gUt) == 0) { + cur += kUtLen; + } else { pos.setErrorIndex(start); return 0; } - cur += kGmtLen; // Sign UBool negative = FALSE; if (text.charAt(cur) == (UChar)0x002D /* minus */) { @@ -881,7 +1135,7 @@ SimpleDateFormat::parseGMTDefault(const UnicodeString &text, ParsePosition &pos) pos.setIndex(cur); Formattable number; - parseInt(text, number, 6, pos, FALSE); + parseInt(text, number, 6, pos, FALSE,currentNumberFormat); numLen = pos.getIndex() - cur; if (numLen <= 0) { @@ -903,7 +1157,7 @@ SimpleDateFormat::parseGMTDefault(const UnicodeString &text, ParsePosition &pos) if (cur + 2 < text.length() && text.charAt(cur) == (UChar)0x003A /* colon */) { cur++; pos.setIndex(cur); - parseInt(text, number, 2, pos, FALSE); + parseInt(text, number, 2, pos, FALSE,currentNumberFormat); numLen = pos.getIndex() - cur; if (numLen == 2) { // got minute field @@ -912,7 +1166,7 @@ SimpleDateFormat::parseGMTDefault(const UnicodeString &text, ParsePosition &pos) if (cur + 2 < text.length() && text.charAt(cur) == (UChar)0x003A /* colon */) { cur++; pos.setIndex(cur); - parseInt(text, number, 2, pos, FALSE); + parseInt(text, number, 2, pos, FALSE,currentNumberFormat); numLen = pos.getIndex() - cur; if (numLen == 2) { // got second field @@ -1040,13 +1294,37 @@ SimpleDateFormat::initGMTFormatters(UErrorCode &status) { break; } fGMTFormatters[i] = new MessageFormat(fSymbols->fGmtFormat, status); + GregorianCalendar *gcal = new GregorianCalendar(TimeZone::createTimeZone(UnicodeString(gEtcUTC)), status); if (U_FAILURE(status)) { break; } SimpleDateFormat *sdf = (SimpleDateFormat*)this->clone(); - sdf->adoptTimeZone(TimeZone::createTimeZone(UnicodeString(gEtcUTC))); + sdf->adoptCalendar(gcal); sdf->applyPattern(*hourPattern); + + // This prevents a hours format pattern like "-HH:mm:ss" from matching + // in a string like "GMT-07:00 10:08:11 PM" + sdf->setLenient(FALSE); + fGMTFormatters[i]->adoptFormat(0, sdf); + + // For parsing, we only allow Hms patterns to be equal or longer + // than its length with fixed minutes/seconds digits. + // See #6880 + if (i == kGMTNegativeHMS || i == kGMTPositiveHMS) { + UnicodeString tmp; + Formattable tmpParam(60*60*1000, Formattable::kIsDate); + FieldPosition fpos(0); + fGMTFormatters[i]->format(&tmpParam, 1, tmp, fpos, status); + if (U_FAILURE(status)) { + break; + } + if (i == kGMTNegativeHMS) { + fGMTFormatHmsMinLen[kGMTNegativeHMSMinLenIdx] = tmp.length(); + } else { + fGMTFormatHmsMinLen[kGMTPositiveHMSMinLenIdx] = tmp.length(); + } + } } } else { status = U_MEMORY_ALLOCATION_ERROR; @@ -1055,12 +1333,162 @@ SimpleDateFormat::initGMTFormatters(UErrorCode &status) { umtx_unlock(&LOCK); } +void +SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if ( fDateOverride.isBogus() && fTimeOverride.isBogus() ) { + return; + } + umtx_lock(&LOCK); + if (fNumberFormatters == NULL) { + fNumberFormatters = (NumberFormat**)uprv_malloc(UDAT_FIELD_COUNT * sizeof(NumberFormat*)); + if (fNumberFormatters) { + for (int32_t i = 0; i < UDAT_FIELD_COUNT; i++) { + fNumberFormatters[i] = fNumberFormat; + } + } else { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + umtx_unlock(&LOCK); + + processOverrideString(locale,fDateOverride,kOvrStrDate,status); + processOverrideString(locale,fTimeOverride,kOvrStrTime,status); + +} + +void +SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeString &str, int8_t type, UErrorCode &status) { + if (str.isBogus()) { + return; + } + int32_t start = 0; + int32_t len; + UnicodeString nsName; + UnicodeString ovrField; + UBool moreToProcess = TRUE; + + while (moreToProcess) { + int32_t delimiterPosition = str.indexOf(ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE,start); + if (delimiterPosition == -1) { + moreToProcess = FALSE; + len = str.length() - start; + } else { + len = delimiterPosition - start; + } + UnicodeString currentString(str,start,len); + int32_t equalSignPosition = currentString.indexOf(ULOC_KEYWORD_ASSIGN_UNICODE,0); + if (equalSignPosition == -1) { // Simple override string such as "hebrew" + nsName.setTo(currentString); + ovrField.setToBogus(); + } else { // Field specific override string such as "y=hebrew" + nsName.setTo(currentString,equalSignPosition+1); + ovrField.setTo(currentString,0,1); // We just need the first character. + } + + int32_t nsNameHash = nsName.hashCode(); + // See if the numbering system is in the override list, if not, then add it. + NSOverride *cur = fOverrideList; + NumberFormat *nf = NULL; + UBool found = FALSE; + while ( cur && !found ) { + if ( cur->hash == nsNameHash ) { + nf = cur->nf; + found = TRUE; + } + cur = cur->next; + } + + if (!found) { + cur = (NSOverride *)uprv_malloc(sizeof(NSOverride)); + if (cur) { + char kw[ULOC_KEYWORD_AND_VALUES_CAPACITY]; + uprv_strcpy(kw,"numbers="); + nsName.extract(0,len,kw+8,ULOC_KEYWORD_AND_VALUES_CAPACITY-8,US_INV); + + Locale ovrLoc(locale.getLanguage(),locale.getCountry(),locale.getVariant(),kw); + nf = NumberFormat::createInstance(ovrLoc,status); + + // no matter what the locale's default number format looked like, we want + // to modify it so that it doesn't use thousands separators, doesn't always + // show the decimal point, and recognizes integers only when parsing + + if (U_SUCCESS(status)) { + nf->setGroupingUsed(FALSE); + DecimalFormat* decfmt = dynamic_cast(nf); + if (decfmt != NULL) { + decfmt->setDecimalSeparatorAlwaysShown(FALSE); + } + nf->setParseIntegerOnly(TRUE); + nf->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00" + + cur->nf = nf; + cur->hash = nsNameHash; + cur->next = fOverrideList; + fOverrideList = cur; + } + else { + // clean up before returning + if (cur != NULL) { + uprv_free(cur); + } + return; + } + + } else { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + + // Now that we have an appropriate number formatter, fill in the appropriate spaces in the + // number formatters table. + + if (ovrField.isBogus()) { + switch (type) { + case kOvrStrDate: + case kOvrStrBoth: { + for ( int8_t i=0 ; iisLeapYear(hc->get(UCAL_YEAR,status)) && value == 6 && count >= 3 ) + value = 13; // Show alternate form for Adar II in leap years in Hebrew calendar. + if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6 && count < 3 ) + value--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7. + } + if (count == 5) _appendSymbol(appendTo, value, fSymbols->fNarrowMonths, fSymbols->fNarrowMonthsCount); - else if (count == 4) + else if (count == 4) _appendSymbol(appendTo, value, fSymbols->fMonths, fSymbols->fMonthsCount); - else if (count == 3) + else if (count == 3) _appendSymbol(appendTo, value, fSymbols->fShortMonths, fSymbols->fShortMonthsCount); - else - zeroPaddingNumber(appendTo, value + 1, count, maxIntCount); + else + zeroPaddingNumber(currentNumberFormat,appendTo, value + 1, count, maxIntCount); break; // for "LLLL", write out the whole month name, for "LLL", write out the month @@ -1142,42 +1581,42 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, // appropriate number of digits // for "LLLLL", use the narrow form case UDAT_STANDALONE_MONTH_FIELD: - if (count == 5) + if (count == 5) _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowMonths, fSymbols->fStandaloneNarrowMonthsCount); - else if (count == 4) + else if (count == 4) _appendSymbol(appendTo, value, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount); - else if (count == 3) + else if (count == 3) _appendSymbol(appendTo, value, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount); - else - zeroPaddingNumber(appendTo, value + 1, count, maxIntCount); + else + zeroPaddingNumber(currentNumberFormat,appendTo, value + 1, count, maxIntCount); break; // for "k" and "kk", write out the hour, adjusting midnight to appear as "24" case UDAT_HOUR_OF_DAY1_FIELD: - if (value == 0) - zeroPaddingNumber(appendTo, cal.getMaximum(UCAL_HOUR_OF_DAY) + 1, count, maxIntCount); - else - zeroPaddingNumber(appendTo, value, count, maxIntCount); + if (value == 0) + zeroPaddingNumber(currentNumberFormat,appendTo, cal.getMaximum(UCAL_HOUR_OF_DAY) + 1, count, maxIntCount); + else + zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); break; case UDAT_FRACTIONAL_SECOND_FIELD: // Fractional seconds left-justify { - fNumberFormat->setMinimumIntegerDigits((count > 3) ? 3 : count); - fNumberFormat->setMaximumIntegerDigits(maxIntCount); + currentNumberFormat->setMinimumIntegerDigits((count > 3) ? 3 : count); + currentNumberFormat->setMaximumIntegerDigits(maxIntCount); if (count == 1) { - value = (value + 50) / 100; + value /= 100; } else if (count == 2) { - value = (value + 5) / 10; + value /= 10; } FieldPosition p(0); - fNumberFormat->format(value, appendTo, p); + currentNumberFormat->format(value, appendTo, p); if (count > 3) { - fNumberFormat->setMinimumIntegerDigits(count - 3); - fNumberFormat->format((int32_t)0, appendTo, p); + currentNumberFormat->setMinimumIntegerDigits(count - 3); + currentNumberFormat->format((int32_t)0, appendTo, p); } } break; @@ -1188,7 +1627,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name case UDAT_DOW_LOCAL_FIELD: if ( count < 3 ) { - zeroPaddingNumber(appendTo, value, count, maxIntCount); + zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); break; } // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week, @@ -1199,10 +1638,10 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, } // fall through, do not break here case UDAT_DAY_OF_WEEK_FIELD: - if (count == 5) + if (count == 5) _appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays, fSymbols->fNarrowWeekdaysCount); - else if (count == 4) + else if (count == 4) _appendSymbol(appendTo, value, fSymbols->fWeekdays, fSymbols->fWeekdaysCount); else @@ -1215,7 +1654,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, // for "ccccc", use the narrow day-of-the-week name case UDAT_STANDALONE_DAY_FIELD: if ( count < 3 ) { - zeroPaddingNumber(appendTo, value, 1, maxIntCount); + zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, maxIntCount); break; } // fall through to alpha DOW handling, but for that we don't want local day-of-week, @@ -1224,10 +1663,10 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, if (U_FAILURE(status)) { return; } - if (count == 5) + if (count == 5) _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowWeekdays, fSymbols->fStandaloneNarrowWeekdaysCount); - else if (count == 4) + else if (count == 4) _appendSymbol(appendTo, value, fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount); else // count == 3 @@ -1244,10 +1683,10 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, // for "h" and "hh", write out the hour, adjusting noon and midnight to show up // as "12" case UDAT_HOUR1_FIELD: - if (value == 0) - zeroPaddingNumber(appendTo, cal.getLeastMaximum(UCAL_HOUR) + 1, count, maxIntCount); - else - zeroPaddingNumber(appendTo, value, count, maxIntCount); + if (value == 0) + zeroPaddingNumber(currentNumberFormat,appendTo, cal.getLeastMaximum(UCAL_HOUR) + 1, count, maxIntCount); + else + zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); break; // for the "z" symbols, we have to check our time zone data first. If we have a @@ -1256,9 +1695,9 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, // If we don't have a localized time zone name, // then the time zone shows up as "GMT+hh:mm" or "GMT-hh:mm" (where "hh:mm" is the // offset from GMT) regardless of how many z's were in the pattern symbol - case UDAT_TIMEZONE_FIELD: + case UDAT_TIMEZONE_FIELD: case UDAT_TIMEZONE_GENERIC_FIELD: - case UDAT_TIMEZONE_SPECIAL_FIELD: + case UDAT_TIMEZONE_SPECIAL_FIELD: { UnicodeString zoneString; const ZoneStringFormat *zsf = fSymbols->getZoneStringFormat(); @@ -1293,7 +1732,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, } } if (zoneString.isEmpty()) { - appendGMT(appendTo, cal, status); + appendGMT(currentNumberFormat,appendTo, cal, status); } else { appendTo += zoneString; } @@ -1307,59 +1746,65 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, formatRFC822TZ(appendTo, value); } else { // long form, localized GMT pattern - appendGMT(appendTo, cal, status); + appendGMT(currentNumberFormat,appendTo, cal, status); } break; case UDAT_QUARTER_FIELD: - if (count >= 4) + if (count >= 4) _appendSymbol(appendTo, value/3, fSymbols->fQuarters, fSymbols->fQuartersCount); - else if (count == 3) + else if (count == 3) _appendSymbol(appendTo, value/3, fSymbols->fShortQuarters, fSymbols->fShortQuartersCount); - else - zeroPaddingNumber(appendTo, (value/3) + 1, count, maxIntCount); + else + zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount); break; case UDAT_STANDALONE_QUARTER_FIELD: - if (count >= 4) + if (count >= 4) _appendSymbol(appendTo, value/3, fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount); - else if (count == 3) + else if (count == 3) _appendSymbol(appendTo, value/3, fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount); - else - zeroPaddingNumber(appendTo, (value/3) + 1, count, maxIntCount); + else + zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount); break; // all of the other pattern symbols can be formatted as simple numbers with // appropriate zero padding default: - zeroPaddingNumber(appendTo, value, count, maxIntCount); + zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); break; } - // if the field we're formatting is the one the FieldPosition says it's interested - // in, fill in the FieldPosition with this field's positions - if (pos.getBeginIndex() == pos.getEndIndex() && - pos.getField() == fgPatternIndexToDateFormatField[patternCharIndex]) { - pos.setBeginIndex(beginOffset); - pos.setEndIndex(appendTo.length()); + handler.addAttribute(fgPatternIndexToDateFormatField[patternCharIndex], beginOffset, appendTo.length()); +} + +//---------------------------------------------------------------------- + +NumberFormat * +SimpleDateFormat::getNumberFormatByIndex(UDateFormatField index) const { + if (fNumberFormatters != NULL) { + return fNumberFormatters[index]; + } else { + return fNumberFormat; } } //---------------------------------------------------------------------- void -SimpleDateFormat::zeroPaddingNumber(UnicodeString &appendTo, int32_t value, int32_t minDigits, int32_t maxDigits) const +SimpleDateFormat::zeroPaddingNumber(NumberFormat *currentNumberFormat,UnicodeString &appendTo, + int32_t value, int32_t minDigits, int32_t maxDigits) const { - if (fNumberFormat!=NULL) { + if (currentNumberFormat!=NULL) { FieldPosition pos(0); - fNumberFormat->setMinimumIntegerDigits(minDigits); - fNumberFormat->setMaximumIntegerDigits(maxDigits); - fNumberFormat->format(value, appendTo, pos); // 3rd arg is there to speed up processing + currentNumberFormat->setMinimumIntegerDigits(minDigits); + currentNumberFormat->setMaximumIntegerDigits(maxDigits); + currentNumberFormat->format(value, appendTo, pos); // 3rd arg is there to speed up processing } } @@ -1387,9 +1832,11 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& UErrorCode status = U_ZERO_ERROR; int32_t pos = parsePos.getIndex(); int32_t start = pos; + UBool ambiguousYear[] = { FALSE }; + int32_t saveHebrewMonth = -1; int32_t count = 0; - + UBool lenient = isLenient(); // hack, reset tztype, cast away const @@ -1407,6 +1854,21 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& const UnicodeString numericFormatChars(NUMERIC_FORMAT_CHARS); + TimeZone *backupTZ = NULL; + Calendar *workCal = &cal; + if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) { + // Different calendar type + // We use the time/zone from the input calendar, but + // do not use the input calendar for field calculation. + fCalendar->setTime(cal.getTime(status),status); + if (U_FAILURE(status)) { + goto ExitParse; + } + backupTZ = fCalendar->getTimeZone().clone(); + fCalendar->setTimeZone(cal.getTimeZone()); + workCal = fCalendar; + } + for (int32_t i=0; igetTime(status); // {sfb} check internalGetDefaultCenturyStart @@ -1573,16 +2053,16 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& copy = cal.clone(); // Check for failed cloning. if (copy == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - goto ExitParse; + status = U_MEMORY_ALLOCATION_ERROR; + goto ExitParse; } const TimeZone & tz = cal.getTimeZone(); BasicTimeZone *btz = NULL; - if (tz.getDynamicClassID() == OlsonTimeZone::getStaticClassID() - || tz.getDynamicClassID() == SimpleTimeZone::getStaticClassID() - || tz.getDynamicClassID() == RuleBasedTimeZone::getStaticClassID() - || tz.getDynamicClassID() == VTimeZone::getStaticClassID()) { + if (dynamic_cast(&tz) != NULL + || dynamic_cast(&tz) != NULL + || dynamic_cast(&tz) != NULL + || dynamic_cast(&tz) != NULL) { btz = (BasicTimeZone*)&tz; } @@ -1678,13 +2158,25 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& delete copy; } } -ExitParse: +ExitParse: + // Set the parsed result if local calendar is used + // instead of the input calendar + if (U_SUCCESS(status) && workCal != &cal) { + cal.setTimeZone(workCal->getTimeZone()); + cal.setTime(workCal->getTime(status), status); + } + + // Restore the original time zone if required + if (backupTZ != NULL) { + fCalendar->adoptTimeZone(backupTZ); + } + // If any Calendar calls failed, we pretend that we // couldn't parse the string, when in reality this isn't quite accurate-- // we did parse it; the Calendar calls just failed. - if (U_FAILURE(status)) { + if (U_FAILURE(status)) { parsePos.setErrorIndex(pos); - parsePos.setIndex(start); + parsePos.setIndex(start); } } @@ -1738,7 +2230,7 @@ int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text, lcase.fastCopyFrom(data[i]).foldCase(); int32_t length = lcase.length(); - + if (length > bestMatchLength && lcaseText.compareBetween(0, length, lcase, 0, length) == 0) { @@ -1777,7 +2269,7 @@ int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text, } } } - + return -start; } @@ -1828,7 +2320,7 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern, t += 1; } } - + for (p = 0; p < literal.length() && t < text.length(); p += 1, t += 1) { UBool needWhitespace = FALSE; @@ -1945,7 +2437,7 @@ int32_t SimpleDateFormat::matchString(const UnicodeString& text, lcase.fastCopyFrom(data[i]).foldCase(); int32_t length = lcase.length(); - + if (length > bestMatchLength && lcaseText.compareBetween(0, length, lcase, 0, length) == 0) { @@ -1955,7 +2447,13 @@ int32_t SimpleDateFormat::matchString(const UnicodeString& text, } if (bestMatch >= 0) { - cal.set(field, bestMatch); + // Adjustment for Hebrew Calendar month Adar II + if (!strcmp(cal.getType(),"hebrew") && field==UCAL_MONTH && bestMatch==13) { + cal.set(field,6); + } + else { + cal.set(field, bestMatch); + } // Once we have a match, we have to determine the length of the // original source string. This will usually be == the length of @@ -1984,7 +2482,7 @@ int32_t SimpleDateFormat::matchString(const UnicodeString& text, } } } - + return -start; } @@ -2007,13 +2505,16 @@ SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status) * indicating matching failure, otherwise. */ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count, - UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], Calendar& cal) const + UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal, + int32_t patLoc) const { Formattable number; int32_t value = 0; int32_t i; + int32_t ps = 0; ParsePosition pos(0); - int32_t patternCharIndex; + UDateFormatField patternCharIndex; + NumberFormat *currentNumberFormat; UnicodeString temp; UChar *patternCharPtr = u_strchr(DateFormatSymbols::getPatternUChars(), ch); UBool lenient = isLenient(); @@ -2028,7 +2529,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC } patternCharIndex = (UDateFormatField)(patternCharPtr - DateFormatSymbols::getPatternUChars()); - + currentNumberFormat = getNumberFormatByIndex(patternCharIndex); UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; // If there are any spaces here, skip over them. If we hit the end @@ -2039,8 +2540,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC } UChar32 c = text.char32At(start); - - if (!u_isUWhiteSpace(c)) { + if (!u_isUWhiteSpace(c) /*||*/ && !uprv_isRuleWhiteSpace(c)) { break; } @@ -2083,20 +2583,34 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC src = &text; } - parseInt(*src, number, pos, allowNegative); + parseInt(*src, number, pos, allowNegative,currentNumberFormat); + + int32_t txtLoc = pos.getIndex(); - if (pos.getIndex() > parseStart) { + 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); + } + // 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 @@ -2148,17 +2662,24 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC // we check the rest of the fields below. break; } - + switch (patternCharIndex) { case UDAT_ERA_FIELD: if (count == 5) { - return matchString(text, start, UCAL_ERA, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount, cal); - } - if (count == 4) { - return matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, cal); + ps = matchString(text, start, UCAL_ERA, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount, cal); + } else if (count == 4) { + ps = matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, cal); + } else { + ps = matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, cal); } - return matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, 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 @@ -2187,6 +2708,17 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC } } 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: @@ -2207,10 +2739,27 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC case UDAT_MONTH_FIELD: if (gotNumber) // i.e., M or MM. { - // Don't want to parse the month if it is a string - // while pattern uses numeric style: M or MM. - // [We computed 'value' above.] - cal.set(UCAL_MONTH, value - 1); + // 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 (!strcmp(cal.getType(),"hebrew")) { + HebrewCalendar *hc = (HebrewCalendar*)&cal; + if (cal.isSet(UCAL_YEAR)) { + UErrorCode status = U_ZERO_ERROR; + if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && 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 or MM. + // [We computed 'value' above.] + cal.set(UCAL_MONTH, value - 1); + } return pos.getIndex(); } else { // count >= 3 // i.e., MMM or MMMM @@ -2250,11 +2799,11 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC case UDAT_HOUR_OF_DAY1_FIELD: // [We computed 'value' above.] - if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1) + if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1) value = 0; // fall through to set field - + case UDAT_HOUR_OF_DAY0_FIELD: cal.set(UCAL_HOUR_OF_DAY, value); return pos.getIndex(); @@ -2329,15 +2878,15 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC case UDAT_HOUR1_FIELD: // [We computed 'value' above.] - if (value == cal.getLeastMaximum(UCAL_HOUR)+1) + if (value == cal.getLeastMaximum(UCAL_HOUR)+1) value = 0; // fall through to set field - + case UDAT_HOUR0_FIELD: cal.set(UCAL_HOUR, value); return pos.getIndex(); - + case UDAT_QUARTER_FIELD: if (gotNumber) // i.e., Q or QQ. { @@ -2416,7 +2965,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC // Parse digits int32_t orgPos = start + 1; pos.setIndex(orgPos); - parseInt(text, number, 6, pos, FALSE); + parseInt(text, number, 6, pos, FALSE,currentNumberFormat); int32_t numLen = pos.getIndex() - orgPos; if (numLen <= 0) { break; @@ -2465,7 +3014,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC if (parsed) { // offset was successfully parsed as either a long GMT string or RFC822 zone offset // string. Create normalized zone ID for the offset. - + UnicodeString tzID(gGmt); formatRFC822TZ(tzID, offset); //TimeZone *customTZ = TimeZone::createTimeZone(tzID); @@ -2508,6 +3057,8 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC zsinfo = zsf->findGenericLocation(text, start, matchLen, status); } break; + default: + break; } if (U_SUCCESS(status) && zsinfo != NULL) { @@ -2528,6 +3079,22 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC return start + matchLen; } } + // Step 4 + // Final attempt - is this standalone GMT/UT/UTC? + int32_t gmtLen = 0; + if (text.compare(start, kGmtLen, gGmt) == 0) { + gmtLen = kGmtLen; + } else if (text.compare(start, kUtcLen, gUtc) == 0) { + gmtLen = kUtcLen; + } else if (text.compare(start, kUtLen, gUt) == 0) { + gmtLen = kUtLen; + } + if (gmtLen > 0) { + TimeZone *tz = TimeZone::createTimeZone(UnicodeString("Etc/GMT")); + cal.adoptTimeZone(tz); + return start + gmtLen; + } + // complete failure return -start; } @@ -2545,7 +3112,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC } else { src = &text; } - parseInt(*src, number, pos, allowNegative); + parseInt(*src, number, pos, allowNegative,currentNumberFormat); if (pos.getIndex() != parseStart) { int32_t value = number.getLong(); @@ -2557,7 +3124,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC return pos.getIndex(); } } - + return -start; } } @@ -2569,8 +3136,9 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC void SimpleDateFormat::parseInt(const UnicodeString& text, Formattable& number, ParsePosition& pos, - UBool allowNegative) const { - parseInt(text, number, -1, pos, allowNegative); + UBool allowNegative, + NumberFormat *fmt) const { + parseInt(text, number, -1, pos, allowNegative,fmt); } /** @@ -2580,17 +3148,16 @@ void SimpleDateFormat::parseInt(const UnicodeString& text, Formattable& number, int32_t maxDigits, ParsePosition& pos, - UBool allowNegative) const { + UBool allowNegative, + NumberFormat *fmt) const { UnicodeString oldPrefix; DecimalFormat* df = NULL; - if (!allowNegative && - fNumberFormat->getDynamicClassID() == DecimalFormat::getStaticClassID()) { - df = (DecimalFormat*)fNumberFormat; + if (!allowNegative && (df = dynamic_cast(fmt)) != NULL) { df->getNegativePrefix(oldPrefix); df->setNegativePrefix(SUPPRESS_NEGATIVE_PREFIX); } int32_t oldPos = pos.getIndex(); - fNumberFormat->parse(text, number, pos); + fmt->parse(text, number, pos); if (df != NULL) { df->setNegativePrefix(oldPrefix); } @@ -2626,21 +3193,21 @@ void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern, // 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 // to localized patterns or localized to generic. - if (U_FAILURE(status)) + if (U_FAILURE(status)) return; - + translatedPattern.remove(); UBool inQuote = FALSE; for (int32_t i = 0; i < originalPattern.length(); ++i) { UChar c = originalPattern[i]; if (inQuote) { - if (c == QUOTE) + if (c == QUOTE) inQuote = FALSE; } else { - if (c == QUOTE) + if (c == QUOTE) inQuote = TRUE; - else if ((c >= 0x0061 /*'a'*/ && c <= 0x007A) /*'z'*/ + else if ((c >= 0x0061 /*'a'*/ && c <= 0x007A) /*'z'*/ || (c >= 0x0041 /*'A'*/ && c <= 0x005A /*'Z'*/)) { int32_t ci = from.indexOf(c); if (ci == -1) { @@ -2727,7 +3294,7 @@ void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt) { UErrorCode status = U_ZERO_ERROR; DateFormat::adoptCalendar(calendarToAdopt); - delete fSymbols; + delete fSymbols; fSymbols=NULL; initializeSymbols(fLocale, fCalendar, status); // we need new symbols initializeDefaultCentury(); // we need a new century (possibly) @@ -2744,7 +3311,7 @@ SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field) const { UBool -SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern, +SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern, UCalendarDateFields field) { int32_t fieldLevel = fgCalendarFieldToLevel[field]; int32_t level; @@ -2769,8 +3336,8 @@ SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern, } else { inQuote = ! inQuote; } - } - else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) + } + else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { prevCh = ch; ++count; @@ -2786,14 +3353,159 @@ SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern, return TRUE; } - +//---------------------------------------------------------------------- const Locale& SimpleDateFormat::getSmpFmtLocale(void) const { return fLocale; } +//---------------------------------------------------------------------- + +int32_t +SimpleDateFormat::checkIntSuffix(const UnicodeString& text, int32_t start, + int32_t patLoc, UBool isNegative) const { + // local variables + UnicodeString suf; + int32_t patternMatch; + int32_t textPreMatch; + int32_t textPostMatch; + + // check that we are still in range + if ( (start > text.length()) || + (start < 0) || + (patLoc < 0) || + (patLoc > fPattern.length())) { + // out of range, don't advance location in text + return start; + } + + // get the suffix + DecimalFormat* decfmt = dynamic_cast(fNumberFormat); + if (decfmt != NULL) { + if (isNegative) { + suf = decfmt->getNegativeSuffix(suf); + } + else { + suf = decfmt->getPositiveSuffix(suf); + } + } + + // check for suffix + if (suf.length() <= 0) { + return start; + } + + // check suffix will be encountered in the pattern + patternMatch = compareSimpleAffix(suf,fPattern,patLoc); + + // check if a suffix will be encountered in the text + textPreMatch = compareSimpleAffix(suf,text,start); + + // check if a suffix was encountered in the text + textPostMatch = compareSimpleAffix(suf,text,start-suf.length()); + // check for suffix match + if ((textPreMatch >= 0) && (patternMatch >= 0) && (textPreMatch == patternMatch)) { + return start; + } + else if ((textPostMatch >= 0) && (patternMatch >= 0) && (textPostMatch == patternMatch)) { + return start - suf.length(); + } + + // should not get here + return start; +} + +//---------------------------------------------------------------------- + +int32_t +SimpleDateFormat::compareSimpleAffix(const UnicodeString& affix, + const UnicodeString& input, + int32_t pos) const { + int32_t start = pos; + for (int32_t i=0; i