/*
*******************************************************************************
-* Copyright (C) 1997-2010, International Business Machines Corporation and *
+* Copyright (C) 1997-2012, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
#include "unicode/basictz.h"
#include "unicode/simpletz.h"
#include "unicode/rbtz.h"
+#include "unicode/tzfmt.h"
+#include "unicode/utf16.h"
#include "unicode/vtzone.h"
#include "olsontz.h"
-#include "util.h"
+#include "patternprops.h"
#include "fphdlimp.h"
#include "gregoimp.h"
#include "hebrwcal.h"
#include "cstring.h"
#include "uassert.h"
-#include "zstrfmt.h"
#include "cmemory.h"
#include "umutex.h"
-#include "smpdtfst.h"
#include <float.h>
+#include "smpdtfst.h"
#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
#include <stdio.h>
UDAT_STANDALONE_DAY_FIELD,
UDAT_STANDALONE_MONTH_FIELD,
UDAT_QUARTER_FIELD,
- UDAT_STANDALONE_QUARTER_FIELD };
-static const int8_t kDateFieldsCount = 13;
+ UDAT_STANDALONE_QUARTER_FIELD,
+ UDAT_YEAR_NAME_FIELD };
+static const int8_t kDateFieldsCount = 15;
static const UDateFormatField kTimeFields[] = {
UDAT_HOUR_OF_DAY1_FIELD,
-1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
-1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
0, // 'm' - UDAT_MINUTE_FIELD
- 0, // 's' - UDAT_SEOND_FIELD
+ 0, // 's' - UDAT_SECOND_FIELD
-1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
-1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
-1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
1, // 'L' - UDAT_STANDALONE_MONTH_FIELD
-1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
-1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
- -1 // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
+ -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
+ -1 // 'U' - UDAT_YEAR_NAME_FIELD
+};
+// A slightly looser range check for lenient parsing
+static const int32_t gFieldRangeBiasLenient[] = {
+ -1, // 'G' - UDAT_ERA_FIELD
+ -1, // 'y' - UDAT_YEAR_FIELD
+ 8, // 'M' - UDAT_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
+ 18, // 'd' - UDAT_DATE_FIELD (allow calendar max + 18, e.g. 49 for grego; tests require at least 40 for grego)
+ -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
+ -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
+ 40, // 'm' - UDAT_MINUTE_FIELD (allow calendar max + 40, e.g. 99)
+ 40, // 's' - UDAT_SECOND_FIELD (allow calendar max + 40, e.g. 99)
+ -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
+ -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
+ -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
+ -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
+ -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
+ -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
+ -1, // 'a' - UDAT_AM_PM_FIELD
+ -1, // 'h' - UDAT_HOUR1_FIELD
+ -1, // 'K' - UDAT_HOUR0_FIELD
+ -1, // 'z' - UDAT_TIMEZONE_FIELD
+ -1, // 'Y' - UDAT_YEAR_WOY_FIELD
+ -1, // 'e' - UDAT_DOW_LOCAL_FIELD
+ -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
+ -1, // 'g' - UDAT_JULIAN_DAY_FIELD
+ -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
+ -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
+ -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
+ 18, // 'c' - UDAT_STANDALONE_DAY_FIELD (allow calendar max + 18, e.g. 49 for grego)
+ 8, // 'L' - UDAT_STANDALONE_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
+ -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
+ -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
+ -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
+ -1 // 'U' - UDAT_YEAR_NAME_FIELD
};
static UMTX LOCK;
SimpleDateFormat::~SimpleDateFormat()
{
delete fSymbols;
- if (fGMTFormatters) {
- for (int32_t i = 0; i < kNumGMTFormatters; i++) {
- if (fGMTFormatters[i]) {
- delete fGMTFormatters[i];
- }
- }
- uprv_free(fGMTFormatters);
-
- }
if (fNumberFormatters) {
uprv_free(fNumberFormatters);
}
+ if (fTimeZoneFormat) {
+ delete fTimeZoneFormat;
+ }
while (fOverrideList) {
NSOverride *cur = fOverrideList;
SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
: fLocale(Locale::getDefault()),
fSymbols(NULL),
- fGMTFormatters(NULL),
+ fTimeZoneFormat(NULL),
fNumberFormatters(NULL),
- fOverrideList(NULL)
+ fOverrideList(NULL),
+ fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN)
{
construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status);
initializeDefaultCentury();
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(NULL),
- fGMTFormatters(NULL),
+ fTimeZoneFormat(NULL),
fNumberFormatters(NULL),
- fOverrideList(NULL)
+ fOverrideList(NULL),
+ fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN)
{
fDateOverride.setToBogus();
fTimeOverride.setToBogus();
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(NULL),
- fGMTFormatters(NULL),
+ fTimeZoneFormat(NULL),
fNumberFormatters(NULL),
- fOverrideList(NULL)
+ fOverrideList(NULL),
+ fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN)
{
fDateOverride.setTo(override);
fTimeOverride.setToBogus();
UErrorCode& status)
: fPattern(pattern),
fLocale(locale),
- fGMTFormatters(NULL),
+ fTimeZoneFormat(NULL),
fNumberFormatters(NULL),
- fOverrideList(NULL)
+ fOverrideList(NULL),
+ fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN)
{
fDateOverride.setToBogus();
UErrorCode& status)
: fPattern(pattern),
fLocale(locale),
- fGMTFormatters(NULL),
+ fTimeZoneFormat(NULL),
fNumberFormatters(NULL),
- fOverrideList(NULL)
+ fOverrideList(NULL),
+ fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN)
{
fDateOverride.setTo(override);
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(symbolsToAdopt),
- fGMTFormatters(NULL),
+ fTimeZoneFormat(NULL),
fNumberFormatters(NULL),
- fOverrideList(NULL)
+ fOverrideList(NULL),
+ fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN)
{
fDateOverride.setToBogus();
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(new DateFormatSymbols(symbols)),
- fGMTFormatters(NULL),
+ fTimeZoneFormat(NULL),
fNumberFormatters(NULL),
- fOverrideList(NULL)
+ fOverrideList(NULL),
+ fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN)
{
fDateOverride.setToBogus();
UErrorCode& status)
: fLocale(locale),
fSymbols(NULL),
- fGMTFormatters(NULL),
+ fTimeZoneFormat(NULL),
fNumberFormatters(NULL),
- fOverrideList(NULL)
+ fOverrideList(NULL),
+ fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN)
{
construct(timeStyle, dateStyle, fLocale, status);
if(U_SUCCESS(status)) {
: fPattern(gDefaultPattern),
fLocale(locale),
fSymbols(NULL),
- fGMTFormatters(NULL),
+ fTimeZoneFormat(NULL),
fNumberFormatters(NULL),
- fOverrideList(NULL)
+ fOverrideList(NULL),
+ fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN)
{
if (U_FAILURE(status)) return;
initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status);
SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
: DateFormat(other),
+ fLocale(other.fLocale),
fSymbols(NULL),
- fGMTFormatters(NULL),
+ fTimeZoneFormat(NULL),
fNumberFormatters(NULL),
- fOverrideList(NULL)
+ fOverrideList(NULL),
+ fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN)
{
*this = other;
}
fPattern = other.fPattern;
+ // TimeZoneFormat in ICU4C only depends on a locale for now
+ if (fLocale != other.fLocale) {
+ delete fTimeZoneFormat;
+ fTimeZoneFormat = NULL; // forces lazy instantiation with the other locale
+ fLocale = other.fLocale;
+ }
+
+ fDefaultCapitalizationContext = other.fDefaultCapitalizationContext;
+
return *this;
}
that->fSymbols != NULL && // Check for pathological object
*fSymbols == *that->fSymbols &&
fHaveDefaultCentury == that->fHaveDefaultCentury &&
- fDefaultCenturyStart == that->fDefaultCenturyStart);
+ fDefaultCenturyStart == that->fDefaultCenturyStart &&
+ fDefaultCapitalizationContext == that->fDefaultCapitalizationContext);
}
return FALSE;
}
fNumberFormat->setParseIntegerOnly(TRUE);
fNumberFormat->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
- // TODO: Really, the default should be lenient...
- fNumberFormat->setParseStrict(FALSE);
+ //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
initNumberFormatters(locale,status);
{
UErrorCode status = U_ZERO_ERROR;
FieldPositionOnlyHandler handler(pos);
- return _format(cal, appendTo, handler, status);
+ return _format(cal, fDefaultCapitalizationContext, appendTo, handler, status);
+}
+
+//----------------------------------------------------------------------
+
+UnicodeString&
+SimpleDateFormat::format(Calendar& cal, const UDateFormatContextType* types, const UDateFormatContextValue* values,
+ int32_t typesAndValuesCount, UnicodeString& appendTo, FieldPosition& pos) const
+{
+ UErrorCode status = U_ZERO_ERROR;
+ FieldPositionOnlyHandler handler(pos);
+ UDateFormatContextValue capitalizationContext = fDefaultCapitalizationContext;
+ if (types != NULL && values != NULL && typesAndValuesCount==1 && types[0]==UDAT_CAPITALIZATION) {
+ capitalizationContext = values[0];
+ }
+ return _format(cal, capitalizationContext, appendTo, handler, status);
}
//----------------------------------------------------------------------
FieldPositionIterator* posIter, UErrorCode& status) const
{
FieldPositionIteratorHandler handler(posIter, status);
- return _format(cal, appendTo, handler, status);
+ return _format(cal, fDefaultCapitalizationContext, appendTo, handler, status);
}
//----------------------------------------------------------------------
UnicodeString&
-SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, FieldPositionHandler& handler,
- UErrorCode& status) const
+SimpleDateFormat::_format(Calendar& cal, UDateFormatContextValue capitalizationContext,
+ UnicodeString& appendTo, FieldPositionHandler& handler, UErrorCode& status) const
{
- Calendar *workCal = &cal;
- TimeZone *backupTZ = NULL;
+ if ( U_FAILURE(status) ) {
+ return appendTo;
+ }
+ Calendar* workCal = &cal;
+ Calendar* calClone = 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;
+ calClone = fCalendar->clone();
+ if (calClone != NULL) {
+ UDate t = cal.getTime(status);
+ calClone->setTime(t, status);
+ calClone->setTimeZone(cal.getTimeZone());
+ workCal = calClone;
+ } else {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return appendTo;
+ }
}
UBool inQuote = FALSE;
UChar prevCh = 0;
int32_t count = 0;
+ int32_t fieldNum = 0;
// loop through the pattern string character by character
for (int32_t i = 0; i < fPattern.length() && U_SUCCESS(status); ++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, handler, *workCal, status);
+ subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, status);
count = 0;
}
if (ch == QUOTE) {
// Format the last item in the pattern, if any
if (count > 0) {
- subFormat(appendTo, prevCh, count, handler, *workCal, status);
+ subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, status);
}
- if (backupTZ != NULL) {
- // Restore the original time zone
- fCalendar->adoptTimeZone(backupTZ);
+ if (calClone != NULL) {
+ delete calClone;
}
return appendTo;
// 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, -1,
// P Q R S T U V W X Y Z
- -1, 20, -1, 80, -1, -1, 0, 30, -1, 10, 0, -1, -1, -1, -1, -1,
+ -1, 20, -1, 80, -1, 10, 0, 30, -1, 10, 0, -1, -1, -1, -1, -1,
// a b c d e f g h i j k l m n o
-1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, -1, 60, -1, -1,
// p q r s t u v w x y z
/*Q*/ UCAL_MONTH,
/*q*/ UCAL_MONTH,
/*V*/ UCAL_ZONE_OFFSET,
+ /*U*/ UCAL_YEAR,
};
// Map index into pattern character string to DateFormat field number
/*Q*/ UDAT_QUARTER_FIELD,
/*q*/ UDAT_STANDALONE_QUARTER_FIELD,
/*V*/ UDAT_TIMEZONE_SPECIAL_FIELD,
+ /*U*/ UDAT_YEAR_NAME_FIELD,
};
//----------------------------------------------------------------------
}
}
-//---------------------------------------------------------------------
-void
-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(currentNumberFormat,appendTo, offset);
- } else {
- ((SimpleDateFormat*)this)->initGMTFormatters(status);
- if (U_SUCCESS(status)) {
- int32_t type;
- if (offset < 0) {
- offset = -offset;
- type = (offset % U_MILLIS_PER_MINUTE) == 0 ? kGMTNegativeHM : kGMTNegativeHMS;
- } else {
- type = (offset % U_MILLIS_PER_MINUTE) == 0 ? kGMTPositiveHM : kGMTPositiveHMS;
- }
- Formattable param(offset, Formattable::kIsDate);
- FieldPosition fpos(0);
- fGMTFormatters[type]->format(¶m, 1, appendTo, fpos, status);
- }
- }
-}
-
-int32_t
-SimpleDateFormat::parseGMT(const UnicodeString &text, ParsePosition &pos) const {
- if (!isDefaultGMTFormat()) {
- int32_t start = pos.getIndex();
-
- // Quick check
- UBool prefixMatch = FALSE;
- int32_t prefixLen = fSymbols->fGmtFormat.indexOf((UChar)0x007B /* '{' */);
- if (prefixLen > 0 && text.compare(start, prefixLen, fSymbols->fGmtFormat, 0, prefixLen) == 0) {
- prefixMatch = TRUE;
- }
- if (prefixMatch) {
- // Prefix matched
- UErrorCode status = U_ZERO_ERROR;
- ((SimpleDateFormat*)this)->initGMTFormatters(status);
- if (U_SUCCESS(status)) {
- Formattable parsed;
- int32_t parsedCount;
-
- // Try negative Hms
- fGMTFormatters[kGMTNegativeHMS]->parseObject(text, parsed, pos);
- 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());
- }
- }
-
- // Reset ParsePosition
- pos.setIndex(start);
- pos.setErrorIndex(-1);
-
- // Try positive Hms
- fGMTFormatters[kGMTPositiveHMS]->parseObject(text, parsed, pos);
- 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());
- }
- }
-
- // Reset ParsePosition
- pos.setIndex(start);
- pos.setErrorIndex(-1);
-
- // Try negative Hm
- fGMTFormatters[kGMTNegativeHM]->parseObject(text, parsed, pos);
- if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
- parsed.getArray(parsedCount);
- if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
- return (int32_t)(-1 * (int64_t)parsed[0].getDate());
- }
- }
-
- // Reset ParsePosition
- pos.setIndex(start);
- pos.setErrorIndex(-1);
-
- // Try positive Hm
- fGMTFormatters[kGMTPositiveHM]->parseObject(text, parsed, pos);
- if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
- parsed.getArray(parsedCount);
- if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
- return (int32_t)((int64_t)parsed[0].getDate());
- }
- }
-
- // Reset ParsePosition
- pos.setIndex(start);
- pos.setErrorIndex(-1);
- }
- // fall through to the default GMT parsing method
- }
- }
- return parseGMTDefault(text, pos);
-}
-
-void
-SimpleDateFormat::formatGMTDefault(NumberFormat *currentNumberFormat,UnicodeString &appendTo, int32_t offset) const {
- if (offset < 0) {
- appendTo += gGmtMinus;
- offset = -offset; // suppress the '-' sign for text display.
- }else{
- appendTo += gGmtPlus;
- }
-
- offset /= U_MILLIS_PER_SECOND; // now in seconds
- int32_t sec = offset % 60;
- offset /= 60;
- int32_t min = offset % 60;
- int32_t hour = offset / 60;
-
-
- zeroPaddingNumber(currentNumberFormat,appendTo, hour, 2, 2);
- appendTo += (UChar)0x003A /*':'*/;
- zeroPaddingNumber(currentNumberFormat,appendTo, min, 2, 2);
- if (sec != 0) {
- appendTo += (UChar)0x003A /*':'*/;
- 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 + kUtLen + 1 >= text.length()) {
- pos.setErrorIndex(start);
- return 0;
- }
-
- int32_t cur = start;
- // "GMT"
- 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;
- }
- // Sign
- UBool negative = FALSE;
- if (text.charAt(cur) == (UChar)0x002D /* minus */) {
- negative = TRUE;
- } else if (text.charAt(cur) != (UChar)0x002B /* plus */) {
- pos.setErrorIndex(cur);
- return 0;
- }
- cur++;
-
- // Numbers
- int32_t numLen;
- pos.setIndex(cur);
-
- Formattable number;
- parseInt(text, number, 6, pos, FALSE,currentNumberFormat);
- numLen = pos.getIndex() - cur;
-
- if (numLen <= 0) {
- pos.setIndex(start);
- pos.setErrorIndex(cur);
- return 0;
- }
-
- int32_t numVal = number.getLong();
-
- int32_t hour = 0;
- int32_t min = 0;
- int32_t sec = 0;
-
- if (numLen <= 2) {
- // H[H][:mm[:ss]]
- hour = numVal;
- cur += numLen;
- if (cur + 2 < text.length() && text.charAt(cur) == (UChar)0x003A /* colon */) {
- cur++;
- pos.setIndex(cur);
- parseInt(text, number, 2, pos, FALSE,currentNumberFormat);
- numLen = pos.getIndex() - cur;
- if (numLen == 2) {
- // got minute field
- min = number.getLong();
- cur += numLen;
- if (cur + 2 < text.length() && text.charAt(cur) == (UChar)0x003A /* colon */) {
- cur++;
- pos.setIndex(cur);
- parseInt(text, number, 2, pos, FALSE,currentNumberFormat);
- numLen = pos.getIndex() - cur;
- if (numLen == 2) {
- // got second field
- sec = number.getLong();
- } else {
- // reset position
- pos.setIndex(cur - 1);
- pos.setErrorIndex(-1);
- }
- }
- } else {
- // reset postion
- pos.setIndex(cur - 1);
- pos.setErrorIndex(-1);
- }
- }
- } else if (numLen == 3 || numLen == 4) {
- // Hmm or HHmm
- hour = numVal / 100;
- min = numVal % 100;
- } else if (numLen == 5 || numLen == 6) {
- // Hmmss or HHmmss
- hour = numVal / 10000;
- min = (numVal % 10000) / 100;
- sec = numVal % 100;
- } else {
- // HHmmss followed by bogus numbers
- pos.setIndex(cur + 6);
-
- int32_t shift = numLen - 6;
- while (shift > 0) {
- numVal /= 10;
- shift--;
- }
- hour = numVal / 10000;
- min = (numVal % 10000) / 100;
- sec = numVal % 100;
- }
-
- int32_t offset = ((hour*60 + min)*60 + sec)*1000;
- if (negative) {
- offset = -offset;
- }
- return offset;
-}
-
-UBool
-SimpleDateFormat::isDefaultGMTFormat() const {
- // GMT pattern
- if (fSymbols->fGmtFormat.length() == 0) {
- // No GMT pattern is set
- return TRUE;
- } else if (fSymbols->fGmtFormat.compare(gDefGmtPat, kGmtPatLen) != 0) {
- return FALSE;
- }
- // Hour patterns
- if (fSymbols->fGmtHourFormats == NULL || fSymbols->fGmtHourFormatsCount != DateFormatSymbols::GMT_HOUR_COUNT) {
- // No Hour pattern is set
- return TRUE;
- } else if ((fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HMS].compare(gDefGmtNegHmsPat, kNegHmsLen) != 0)
- || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HM].compare(gDefGmtNegHmPat, kNegHmLen) != 0)
- || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HMS].compare(gDefGmtPosHmsPat, kPosHmsLen) != 0)
- || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HM].compare(gDefGmtPosHmPat, kPosHmLen) != 0)) {
- return FALSE;
- }
- return TRUE;
-}
-
-void
-SimpleDateFormat::formatRFC822TZ(UnicodeString &appendTo, int32_t offset) const {
- UChar sign = 0x002B /* '+' */;
- if (offset < 0) {
- offset = -offset;
- sign = 0x002D /* '-' */;
- }
- appendTo.append(sign);
-
- int32_t offsetH = offset / U_MILLIS_PER_HOUR;
- offset = offset % U_MILLIS_PER_HOUR;
- int32_t offsetM = offset / U_MILLIS_PER_MINUTE;
- offset = offset % U_MILLIS_PER_MINUTE;
- int32_t offsetS = offset / U_MILLIS_PER_SECOND;
-
- int32_t num = 0, denom = 0;
- if (offsetS == 0) {
- offset = offsetH*100 + offsetM; // HHmm
- num = offset % 10000;
- denom = 1000;
- } else {
- offset = offsetH*10000 + offsetM*100 + offsetS; // HHmmss
- num = offset % 1000000;
- denom = 100000;
- }
- while (denom >= 1) {
- UChar digit = (UChar)0x0030 + (num / denom);
- appendTo.append(digit);
- num = num % denom;
- denom /= 10;
- }
-}
-
-void
-SimpleDateFormat::initGMTFormatters(UErrorCode &status) {
- if (U_FAILURE(status)) {
- return;
- }
- umtx_lock(&LOCK);
- if (fGMTFormatters == NULL) {
- fGMTFormatters = (MessageFormat**)uprv_malloc(kNumGMTFormatters * sizeof(MessageFormat*));
- if (fGMTFormatters) {
- for (int32_t i = 0; i < kNumGMTFormatters; i++) {
- const UnicodeString *hourPattern = NULL; //initialized it to avoid warning
- switch (i) {
- case kGMTNegativeHMS:
- hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HMS]);
- break;
- case kGMTNegativeHM:
- hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HM]);
- break;
- case kGMTPositiveHMS:
- hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HMS]);
- break;
- case kGMTPositiveHM:
- hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HM]);
- 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->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();
- }
- }
- }
+static inline void
+_appendSymbolWithMonthPattern(UnicodeString& dst, int32_t value, const UnicodeString* symbols, int32_t symbolsCount,
+ const UnicodeString* monthPattern, UErrorCode& status) {
+ U_ASSERT(0 <= value && value < symbolsCount);
+ if (0 <= value && value < symbolsCount) {
+ if (monthPattern == NULL) {
+ dst += symbols[value];
} else {
- status = U_MEMORY_ALLOCATION_ERROR;
+ Formattable monthName((const UnicodeString&)(symbols[value]));
+ MessageFormat::format(*monthPattern, &monthName, 1, dst, status);
}
}
- umtx_unlock(&LOCK);
}
+//----------------------------------------------------------------------
void
SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) {
if (U_FAILURE(status)) {
UBool moreToProcess = TRUE;
while (moreToProcess) {
- int32_t delimiterPosition = str.indexOf(ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE,start);
+ int32_t delimiterPosition = str.indexOf((UChar)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE,start);
if (delimiterPosition == -1) {
moreToProcess = FALSE;
len = str.length() - start;
len = delimiterPosition - start;
}
UnicodeString currentString(str,start,len);
- int32_t equalSignPosition = currentString.indexOf(ULOC_KEYWORD_ASSIGN_UNICODE,0);
+ int32_t equalSignPosition = currentString.indexOf((UChar)ULOC_KEYWORD_ASSIGN_UNICODE,0);
if (equalSignPosition == -1) { // Simple override string such as "hebrew"
nsName.setTo(currentString);
ovrField.setToBogus();
SimpleDateFormat::subFormat(UnicodeString &appendTo,
UChar ch,
int32_t count,
+ UDateFormatContextValue capitalizationContext,
+ int32_t fieldNum,
FieldPositionHandler& handler,
Calendar& cal,
UErrorCode& status) const
const int32_t maxIntCount = 10;
int32_t beginOffset = appendTo.length();
NumberFormat *currentNumberFormat;
+ DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType = DateFormatSymbols::kCapContextUsageOther;
- UBool isHebrewCalendar = !strcmp(cal.getType(),"hebrew");
+ UBool isHebrewCalendar = (uprv_strcmp(cal.getType(),"hebrew") == 0);
+ UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0);
// if the pattern character is unrecognized, signal an error and dump out
if (patternCharPtr == NULL)
{
- status = U_INVALID_FORMAT_ERROR;
+ if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
+ status = U_INVALID_FORMAT_ERROR;
+ }
return;
}
}
currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
+ UnicodeString hebr("hebr", 4, US_INV);
+
switch (patternCharIndex) {
// for any "G" symbol, write out the appropriate era string
// "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
case UDAT_ERA_FIELD:
- if (count == 5)
- _appendSymbol(appendTo, value, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount);
- else if (count == 4)
- _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount);
- else
- _appendSymbol(appendTo, value, fSymbols->fEras, fSymbols->fErasCount);
+ if (isChineseCalendar) {
+ zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, 9); // as in ICU4J
+ } else {
+ if (count == 5) {
+ _appendSymbol(appendTo, value, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageEraNarrow;
+ } else if (count == 4) {
+ _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageEraWide;
+ } else {
+ _appendSymbol(appendTo, value, fSymbols->fEras, fSymbols->fErasCount);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageEraAbbrev;
+ }
+ }
break;
- // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
+ case UDAT_YEAR_NAME_FIELD:
+ if (fSymbols->fShortYearNames != NULL && value <= fSymbols->fShortYearNamesCount) {
+ // the Calendar YEAR field runs 1 through 60 for cyclic years
+ _appendSymbol(appendTo, value - 1, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount);
+ break;
+ }
+ // else fall through to numeric year handling, do not break here
+
+ // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
// NEW: UTS#35:
//Year y yy yyy yyyy yyyyy
//AD 1 1 01 001 0001 00001
//AD 12345 12345 45 12345 12345 12345
case UDAT_YEAR_FIELD:
case UDAT_YEAR_WOY_FIELD:
+ if (fDateOverride.compare(hebr)==0 && value>5000 && value<6000) {
+ value-=5000;
+ }
if(count == 2)
zeroPaddingNumber(currentNumberFormat, appendTo, value, 2, 2);
else
zeroPaddingNumber(currentNumberFormat, appendTo, value, count, maxIntCount);
break;
- // for "MMMM", write out the whole month name, for "MMM", write out the month
- // abbreviation, for "M" or "MM", write out the month as a number with the
+ // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
+ // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
// appropriate number of digits
- // for "MMMMM", use the narrow form
+ // for "MMMMM"/"LLLLL", use the narrow form
case UDAT_MONTH_FIELD:
+ case UDAT_STANDALONE_MONTH_FIELD:
if ( isHebrewCalendar ) {
HebrewCalendar *hc = (HebrewCalendar*)&cal;
if (hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value == 6 && count >= 3 )
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)
- _appendSymbol(appendTo, value, fSymbols->fMonths,
- fSymbols->fMonthsCount);
- else if (count == 3)
- _appendSymbol(appendTo, value, fSymbols->fShortMonths,
- fSymbols->fShortMonthsCount);
- else
- zeroPaddingNumber(currentNumberFormat,appendTo, value + 1, count, maxIntCount);
- break;
-
- // for "LLLL", write out the whole month name, for "LLL", write out the month
- // abbreviation, for "L" or "LL", write out the month as a number with the
- // appropriate number of digits
- // for "LLLLL", use the narrow form
- case UDAT_STANDALONE_MONTH_FIELD:
- if (count == 5)
- _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowMonths,
- fSymbols->fStandaloneNarrowMonthsCount);
- else if (count == 4)
- _appendSymbol(appendTo, value, fSymbols->fStandaloneMonths,
- fSymbols->fStandaloneMonthsCount);
- else if (count == 3)
- _appendSymbol(appendTo, value, fSymbols->fStandaloneShortMonths,
- fSymbols->fStandaloneShortMonthsCount);
- else
- zeroPaddingNumber(currentNumberFormat,appendTo, value + 1, count, maxIntCount);
+ {
+ int32_t isLeapMonth = (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount)?
+ cal.get(UCAL_IS_LEAP_MONTH, status): 0;
+ // should consolidate the next section by using arrays of pointers & counts for the right symbols...
+ if (count == 5) {
+ if (patternCharIndex == UDAT_MONTH_FIELD) {
+ _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fNarrowMonths, fSymbols->fNarrowMonthsCount,
+ (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatNarrow]): NULL, status);
+ } else {
+ _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneNarrowMonths, fSymbols->fStandaloneNarrowMonthsCount,
+ (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow]): NULL, status);
+ }
+ capContextUsageType = DateFormatSymbols::kCapContextUsageMonthNarrow;
+ } else if (count == 4) {
+ if (patternCharIndex == UDAT_MONTH_FIELD) {
+ _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fMonths, fSymbols->fMonthsCount,
+ (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide]): NULL, status);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat;
+ } else {
+ _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount,
+ (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide]): NULL, status);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone;
+ }
+ } else if (count == 3) {
+ if (patternCharIndex == UDAT_MONTH_FIELD) {
+ _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fShortMonths, fSymbols->fShortMonthsCount,
+ (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev]): NULL, status);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat;
+ } else {
+ _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount,
+ (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev]): NULL, status);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone;
+ }
+ } else {
+ UnicodeString monthNumber;
+ zeroPaddingNumber(currentNumberFormat,monthNumber, value + 1, count, maxIntCount);
+ _appendSymbolWithMonthPattern(appendTo, 0, &monthNumber, 1,
+ (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric]): NULL, status);
+ }
+ }
break;
// for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
}
// 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)
+ capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow;
+ } else if (count == 4) {
_appendSymbol(appendTo, value, fSymbols->fWeekdays,
fSymbols->fWeekdaysCount);
- else
+ capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
+ } else {
_appendSymbol(appendTo, value, fSymbols->fShortWeekdays,
fSymbols->fShortWeekdaysCount);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
+ }
break;
// for "ccc", write out the abbreviated day-of-the-week name
if (U_FAILURE(status)) {
return;
}
- if (count == 5)
+ if (count == 5) {
_appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowWeekdays,
fSymbols->fStandaloneNarrowWeekdaysCount);
- else if (count == 4)
+ capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow;
+ } else if (count == 4) {
_appendSymbol(appendTo, value, fSymbols->fStandaloneWeekdays,
fSymbols->fStandaloneWeekdaysCount);
- else // count == 3
+ capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
+ } else { // count == 3
_appendSymbol(appendTo, value, fSymbols->fStandaloneShortWeekdays,
fSymbols->fStandaloneShortWeekdaysCount);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
+ }
break;
// for and "a" symbol, write out the whole AM/PM string
case UDAT_TIMEZONE_FIELD:
case UDAT_TIMEZONE_GENERIC_FIELD:
case UDAT_TIMEZONE_SPECIAL_FIELD:
+ case UDAT_TIMEZONE_RFC_FIELD: // 'Z' - TIMEZONE_RFC
{
UnicodeString zoneString;
- const ZoneStringFormat *zsf = fSymbols->getZoneStringFormat();
- if (zsf) {
- if (patternCharIndex == UDAT_TIMEZONE_FIELD) {
+ const TimeZone& tz = cal.getTimeZone();
+ UDate date = cal.getTime(status);
+ if (U_SUCCESS(status)) {
+ if (patternCharIndex == UDAT_TIMEZONE_RFC_FIELD) {
+ if (count < 4) {
+ // "Z"
+ tzFormat()->format(UTZFMT_STYLE_RFC822, tz, date, zoneString);
+ } else if (count == 5) {
+ // "ZZZZZ"
+ tzFormat()->format(UTZFMT_STYLE_ISO8601, tz, date, zoneString);
+ } else {
+ // "ZZ", "ZZZ", "ZZZZ"
+ tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString);
+ }
+ } else if (patternCharIndex == UDAT_TIMEZONE_FIELD) {
if (count < 4) {
// "z", "zz", "zzz"
- zsf->getSpecificShortString(cal, TRUE /*commonly used only*/,
- zoneString, status);
+ tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT, tz, date, zoneString);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
} else {
// "zzzz"
- zsf->getSpecificLongString(cal, zoneString, status);
+ tzFormat()->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, date, zoneString);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong;
}
} else if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
if (count == 1) {
// "v"
- zsf->getGenericShortString(cal, TRUE /*commonly used only*/,
- zoneString, status);
+ tzFormat()->format(UTZFMT_STYLE_GENERIC_SHORT, tz, date, zoneString);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
} else if (count == 4) {
// "vvvv"
- zsf->getGenericLongString(cal, zoneString, status);
+ tzFormat()->format(UTZFMT_STYLE_GENERIC_LONG, tz, date, zoneString);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong;
}
} else { // patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD
if (count == 1) {
// "V"
- zsf->getSpecificShortString(cal, FALSE /*ignore commonly used*/,
- zoneString, status);
+ tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT, tz, date, zoneString);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
} else if (count == 4) {
// "VVVV"
- zsf->getGenericLocationString(cal, zoneString, status);
+ tzFormat()->format(UTZFMT_STYLE_GENERIC_LOCATION, tz, date, zoneString);
+ capContextUsageType = DateFormatSymbols::kCapContextUsageZoneLong;
}
}
}
- if (zoneString.isEmpty()) {
- appendGMT(currentNumberFormat,appendTo, cal, status);
- } else {
- appendTo += zoneString;
- }
- }
- break;
-
- case UDAT_TIMEZONE_RFC_FIELD: // 'Z' - TIMEZONE_RFC
- if (count < 4) {
- // RFC822 format, must use ASCII digits
- value = (cal.get(UCAL_ZONE_OFFSET, status) + cal.get(UCAL_DST_OFFSET, status));
- formatRFC822TZ(appendTo, value);
- } else {
- // long form, localized GMT pattern
- appendGMT(currentNumberFormat,appendTo, cal, status);
+ appendTo += zoneString;
}
break;
zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
break;
}
+#if !UCONFIG_NO_BREAK_ITERATION
+ if (fieldNum == 0) {
+ // first field, check to see whether we need to titlecase it
+ UBool titlecase = FALSE;
+ switch (capitalizationContext) {
+ case UDAT_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
+ titlecase = TRUE;
+ break;
+ case UDAT_CAPITALIZATION_FOR_UI_LIST_OR_MENU:
+ titlecase = fSymbols->fCapitalization[capContextUsageType][0];
+ break;
+ case UDAT_CAPITALIZATION_FOR_STANDALONE:
+ titlecase = fSymbols->fCapitalization[capContextUsageType][1];
+ break;
+ default:
+ // titlecase = FALSE;
+ break;
+ }
+ if (titlecase) {
+ UnicodeString firstField(appendTo, beginOffset);
+ firstField.toTitle(NULL, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
+ appendTo.replaceBetween(beginOffset, appendTo.length(), firstField);
+ }
+ }
+#endif
handler.addAttribute(fgPatternIndexToDateFormatField[patternCharIndex], beginOffset, appendTo.length());
}
UBool lenient = isLenient();
// hack, reset tztype, cast away const
- ((SimpleDateFormat*)this)->tztype = TZTYPE_UNK;
+ ((SimpleDateFormat*)this)->tztype = UTZFMT_TIME_TYPE_UNKNOWN;
// For parsing abutting numeric fields. 'abutPat' is the
// offset into 'pattern' of the first of 2 or more abutting
UBool inQuote = FALSE;
const UnicodeString numericFormatChars(NUMERIC_FORMAT_CHARS);
+ MessageFormat * numericLeapMonthFormatter = NULL;
- TimeZone *backupTZ = NULL;
+ Calendar* calClone = 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)) {
+ calClone = fCalendar->clone();
+ if (calClone != NULL) {
+ calClone->setTime(cal.getTime(status),status);
+ if (U_FAILURE(status)) {
+ goto ExitParse;
+ }
+ calClone->setTimeZone(cal.getTimeZone());
+ workCal = calClone;
+ } else {
+ status = U_MEMORY_ALLOCATION_ERROR;
goto ExitParse;
}
- backupTZ = fCalendar->getTimeZone().clone();
- fCalendar->setTimeZone(cal.getTimeZone());
- workCal = fCalendar;
+ }
+
+ if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
+ numericLeapMonthFormatter = new MessageFormat(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric], fLocale, status);
+ if (numericLeapMonthFormatter == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ goto ExitParse;
+ } else if (U_FAILURE(status)) {
+ goto ExitParse; // this will delete numericLeapMonthFormatter
+ }
}
for (int32_t i=0; i<fPattern.length(); ++i) {
}
pos = subParse(text, pos, ch, count,
- TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i);
+ TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter);
// If the parse fails anywhere in the run, back up to the
// start of the run and retry.
// Handle non-numeric fields and non-abutting numeric
// fields.
- else {
+ 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);
+ FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter);
if (s == -pos-1) {
// era not present, in special cases allow this to continue
UChar ch = fPattern.charAt(i+1);
// check for whitespace
- if (uprv_isRuleWhiteSpace(ch)) {
+ if (PatternProps::isWhiteSpace(ch)) {
i++;
// Advance over run in pattern
while ((i+1)<fPattern.length() &&
- uprv_isRuleWhiteSpace(fPattern.charAt(i+1))) {
+ PatternProps::isWhiteSpace(fPattern.charAt(i+1))) {
++i;
}
}
// when the two-digit year is equal to the start year, and thus might fall at the
// front or the back of the default century. This only works because we adjust
// the year correctly to start with in other cases -- see subParse().
- if (ambiguousYear[0] || tztype != TZTYPE_UNK) // If this is true then the two-digit year == the default start year
+ if (ambiguousYear[0] || tztype != UTZFMT_TIME_TYPE_UNKNOWN) // If this is true then the two-digit year == the default start year
{
// We need a copy of the fields, and we need to avoid triggering a call to
// complete(), which will recalculate the fields. Since we can't access
delete copy;
}
- if (tztype != TZTYPE_UNK) {
+ if (tztype != UTZFMT_TIME_TYPE_UNKNOWN) {
copy = cal.clone();
// Check for failed cloning.
if (copy == NULL) {
// matches the rule used by the parsed time zone.
int32_t raw, dst;
if (btz != NULL) {
- if (tztype == TZTYPE_STD) {
+ if (tztype == UTZFMT_TIME_TYPE_STANDARD) {
btz->getOffsetFromLocal(localMillis,
BasicTimeZone::kStandard, BasicTimeZone::kStandard, raw, dst, status);
} else {
// Now, compare the results with parsed type, either standard or daylight saving time
int32_t resolvedSavings = dst;
- if (tztype == TZTYPE_STD) {
+ if (tztype == UTZFMT_TIME_TYPE_STANDARD) {
if (dst != 0) {
// Override DST_OFFSET = 0 in the result calendar
resolvedSavings = 0;
cal.setTime(workCal->getTime(status), status);
}
- // Restore the original time zone if required
- if (backupTZ != NULL) {
- fCalendar->adoptTimeZone(backupTZ);
+ if (numericLeapMonthFormatter != NULL) {
+ delete numericLeapMonthFormatter;
+ }
+ if (calClone != NULL) {
+ delete calClone;
}
// If any Calendar calls failed, we pretend that we
for (p = 0; p < literal.length() && t < text.length(); p += 1, t += 1) {
UBool needWhitespace = FALSE;
- while (p < literal.length() && uprv_isRuleWhiteSpace(literal.charAt(p))) {
+ while (p < literal.length() && PatternProps::isWhiteSpace(literal.charAt(p))) {
needWhitespace = TRUE;
p += 1;
}
while (t < text.length()) {
UChar tch = text.charAt(t);
- if (!u_isUWhiteSpace(tch) && !uprv_isRuleWhiteSpace(tch)) {
+ if (!u_isUWhiteSpace(tch) && !PatternProps::isWhiteSpace(tch)) {
break;
}
UCalendarDateFields field,
const UnicodeString* data,
int32_t dataCount,
+ const UnicodeString* monthPattern,
Calendar& cal) const
{
int32_t i = 0;
// We keep track of the longest match, and return that. Note that this
// unfortunately requires us to test all array elements.
int32_t bestMatchLength = 0, bestMatch = -1;
+ UnicodeString bestMatchName;
+ int32_t isLeapMonth = 0;
// {sfb} kludge to support case-insensitive comparison
// {markus 2002oct11} do not just use caseCompareBetween because we do not know
{
bestMatch = i;
bestMatchLength = length;
+ bestMatchName.setTo(data[i]);
+ isLeapMonth = 0;
+ }
+
+ if (monthPattern != NULL) {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString leapMonthName;
+ Formattable monthName((const UnicodeString&)(data[i]));
+ MessageFormat::format(*monthPattern, &monthName, 1, leapMonthName, status);
+ if (U_SUCCESS(status)) {
+ lcase.fastCopyFrom(leapMonthName).foldCase();
+ length = lcase.length();
+
+ if (length > bestMatchLength &&
+ lcaseText.compareBetween(0, length, lcase, 0, length) == 0)
+ {
+ bestMatch = i;
+ bestMatchLength = length;
+ bestMatchName.setTo(leapMonthName);
+ isLeapMonth = 1;
+ }
+ }
}
}
if (bestMatch >= 0)
cal.set(field,6);
}
else {
+ if (field == UCAL_YEAR) {
+ bestMatch++; // only get here for cyclic year names, which match 1-based years 1-60
+ }
cal.set(field, bestMatch);
}
+ if (monthPattern != NULL) {
+ cal.set(UCAL_IS_LEAP_MONTH, isLeapMonth);
+ }
// Once we have a match, we have to determine the length of the
// original source string. This will usually be == the length of
// the case folded string, but it may differ (e.g. sharp s).
- lcase.fastCopyFrom(data[bestMatch]).foldCase();
+ lcase.fastCopyFrom(bestMatchName).foldCase();
// Most of the time, the length will be the same as the length
// of the string from the locale data. Sometimes it will be
// adding a character at a time, until we have a match. We do
// this all in one loop, where we try 'len' first (at index
// i==0).
- int32_t len = data[bestMatch].length(); // 99+% of the time
+ int32_t len = bestMatchName.length(); // 99+% of the time
int32_t n = text.length() - start;
for (i=0; i<=n; ++i) {
int32_t j=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) const
+ int32_t patLoc, MessageFormat * numericLeapMonthFormatter) const
{
Formattable number;
int32_t value = 0;
UnicodeString temp;
UChar *patternCharPtr = u_strchr(DateFormatSymbols::getPatternUChars(), ch);
UBool lenient = isLenient();
+ int32_t tzParseOptions = (lenient)? UTZFMT_PARSE_OPTION_ALL_STYLES: UTZFMT_PARSE_OPTION_NONE;
UBool gotNumber = FALSE;
#if defined (U_DEBUG_CAL)
patternCharIndex = (UDateFormatField)(patternCharPtr - DateFormatSymbols::getPatternUChars());
currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
+ UnicodeString hebr("hebr", 4, US_INV);
+
+ if (numericLeapMonthFormatter != NULL) {
+ numericLeapMonthFormatter->setFormats((const Format **)¤tNumberFormat, 1);
+ }
+ UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0);
// If there are any spaces here, skip over them. If we hit the end
// of the string, then fail.
if (start >= text.length()) {
return -start;
}
-
UChar32 c = text.char32At(start);
- if (!u_isUWhiteSpace(c) /*||*/ && !uprv_isRuleWhiteSpace(c)) {
+ if (!u_isUWhiteSpace(c) /*||*/ && !PatternProps::isWhiteSpace(c)) {
break;
}
-
- start += UTF_CHAR_LENGTH(c);
+ start += U16_LENGTH(c);
}
-
pos.setIndex(start);
// 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 ||
- patternCharIndex == UDAT_HOUR_OF_DAY0_FIELD ||
- patternCharIndex == UDAT_HOUR1_FIELD ||
- patternCharIndex == UDAT_HOUR0_FIELD ||
- patternCharIndex == UDAT_DOW_LOCAL_FIELD ||
- patternCharIndex == UDAT_STANDALONE_DAY_FIELD ||
- patternCharIndex == UDAT_MONTH_FIELD ||
- patternCharIndex == UDAT_STANDALONE_MONTH_FIELD ||
- patternCharIndex == UDAT_QUARTER_FIELD ||
- patternCharIndex == UDAT_STANDALONE_QUARTER_FIELD ||
- patternCharIndex == UDAT_YEAR_FIELD ||
- patternCharIndex == UDAT_YEAR_WOY_FIELD ||
- patternCharIndex == UDAT_FRACTIONAL_SECOND_FIELD)
+ 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;
- if (obeyCount) {
- if ((start+count) > text.length()) {
- return -start;
+ UBool parsedNumericLeapMonth = FALSE;
+ if (numericLeapMonthFormatter != NULL && (patternCharIndex == UDAT_MONTH_FIELD || patternCharIndex == UDAT_STANDALONE_MONTH_FIELD)) {
+ int32_t argCount;
+ Formattable * args = numericLeapMonthFormatter->parse(text, pos, argCount);
+ if (args != NULL && 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);
}
-
- text.extractBetween(0, start + count, temp);
- src = &temp;
- } else {
- src = &text;
}
- parseInt(*src, number, pos, allowNegative,currentNumberFormat);
+ 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();
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;
+ if (!lenient) {
+ int32_t bias = gFieldRangeBias[patternCharIndex];
+ if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) {
+ return -start;
+ }
+ } else {
+ int32_t bias = gFieldRangeBiasLenient[patternCharIndex];
+ if (bias >= 0 && (value > cal.getMaximum(field) + bias)) {
+ return -start;
+ }
}
-
+
pos.setIndex(txtLoc);
}
}
break;
- case UDAT_DOW_LOCAL_FIELD:
- case UDAT_STANDALONE_DAY_FIELD:
- case UDAT_MONTH_FIELD:
- case UDAT_STANDALONE_MONTH_FIELD:
- case UDAT_QUARTER_FIELD:
- case UDAT_STANDALONE_QUARTER_FIELD:
- // in strict mode, these can only
- // be a number if count <= 2
- if (!lenient && gotNumber && count > 2) {
- // We have a string pattern in strict mode
- // but the input parsed as a number. Ignore
- // the fact that the input parsed as a number
- // and try to match it as a string. (Some
- // locales have numbers for the month names.)
- gotNumber = FALSE;
- pos.setIndex(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, cal);
+ ps = matchString(text, start, UCAL_ERA, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount, NULL, cal);
} else if (count == 4) {
- ps = matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, cal);
+ ps = matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, NULL, cal);
} else {
- ps = matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, cal);
+ ps = matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, NULL, cal);
}
// check return position, if it equals -start, then matchString error
// 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 ((pos.getIndex() - start) == 2
+ if (fDateOverride.compare(hebr)==0 && value < 1000) {
+ value += 5000;
+ } else if ((pos.getIndex() - start) == 2 && !isChineseCalendar
&& u_isdigit(text.charAt(start))
&& u_isdigit(text.charAt(start+1)))
{
case UDAT_YEAR_WOY_FIELD:
// Comment is the same as for UDAT_Year_FIELDs - look above
- if ((pos.getIndex() - start) == 2
+ if (fDateOverride.compare(hebr)==0 && value < 1000) {
+ value += 5000;
+ } else if ((pos.getIndex() - start) == 2
&& u_isdigit(text.charAt(start))
&& u_isdigit(text.charAt(start+1))
&& fHaveDefaultCentury )
cal.set(UCAL_YEAR_WOY, value);
return pos.getIndex();
+ case UDAT_YEAR_NAME_FIELD:
+ if (fSymbols->fShortYearNames != NULL) {
+ int32_t newStart = matchString(text, start, UCAL_YEAR, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount, NULL, cal);
+ if (newStart > 0) {
+ return newStart;
+ }
+ }
+ if (gotNumber && (lenient || 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
}
} else {
// Don't want to parse the month if it is a string
- // while pattern uses numeric style: M or MM.
+ // 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 or MMMM
- // Want to be able to parse both short and long forms.
- // Try count == 4 first:
- int32_t newStart = 0;
-
- if ((newStart = matchString(text, start, UCAL_MONTH,
- fSymbols->fMonths, fSymbols->fMonthsCount, cal)) > 0)
- return newStart;
- else // count == 4 failed, now try count == 3
- return matchString(text, start, UCAL_MONTH,
- fSymbols->fShortMonths, fSymbols->fShortMonthsCount, cal);
- }
-
- case UDAT_STANDALONE_MONTH_FIELD:
- if (gotNumber) // i.e., L or LL.
- {
- // 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., LLL or LLLL
+ // 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 = NULL;
+ UnicodeString * shortMonthPat = NULL;
+ if (fSymbols->fLeapMonthPatterns != NULL && 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 ((newStart = matchString(text, start, UCAL_MONTH,
- fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, cal)) > 0)
+ if (patternCharIndex==UDAT_MONTH_FIELD) {
+ newStart = matchString(text, start, UCAL_MONTH, fSymbols->fMonths, fSymbols->fMonthsCount, wideMonthPat, cal); // try MMMM
+ if (newStart > 0) {
+ return newStart;
+ }
+ newStart = matchString(text, start, UCAL_MONTH, fSymbols->fShortMonths, fSymbols->fShortMonthsCount, shortMonthPat, cal); // try MMM
+ } else {
+ newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, wideMonthPat, cal); // try LLLL
+ if (newStart > 0) {
+ return newStart;
+ }
+ newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, shortMonthPat, cal); // try LLL
+ }
+ if (newStart > 0 || !lenient) // currently we do not try to parse MMMMM/LLLLL: #8860
return newStart;
- else // count == 4 failed, now try count == 3
- return matchString(text, start, UCAL_MONTH,
- fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, cal);
+ // else we allowing parsing as number, below
}
+ break;
case UDAT_HOUR_OF_DAY1_FIELD:
// [We computed 'value' above.]
// Try count == 4 (EEEE) first:
int32_t newStart = 0;
if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
- fSymbols->fWeekdays, fSymbols->fWeekdaysCount, cal)) > 0)
+ fSymbols->fWeekdays, fSymbols->fWeekdaysCount, NULL, cal)) > 0)
return newStart;
// EEEE failed, now try EEE
else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
- fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, cal)) > 0)
+ fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, NULL, cal)) > 0)
return newStart;
// EEE failed, now try EEEEE
- else
- return matchString(text, start, UCAL_DAY_OF_WEEK,
- fSymbols->fNarrowWeekdays, fSymbols->fNarrowWeekdaysCount, cal);
+ else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
+ fSymbols->fNarrowWeekdays, fSymbols->fNarrowWeekdaysCount, NULL, cal)) > 0)
+ return newStart;
+ else if (!lenient || patternCharIndex == UDAT_DAY_OF_WEEK_FIELD)
+ return newStart;
+ // else we allowing parsing as number, below
}
+ break;
case UDAT_STANDALONE_DAY_FIELD:
{
// Try count == 4 (cccc) first:
int32_t newStart = 0;
if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
- fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, cal)) > 0)
+ fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, NULL, cal)) > 0)
+ return newStart;
+ else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
+ fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, NULL, cal)) > 0)
return newStart;
- else // cccc failed, now try ccc
- return matchString(text, start, UCAL_DAY_OF_WEEK,
- fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, cal);
+ else if (!lenient)
+ return newStart;
+ // else we allowing parsing as number, below
}
+ break;
case UDAT_AM_PM_FIELD:
- return matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, cal);
+ return matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, NULL, cal);
case UDAT_HOUR1_FIELD:
// [We computed 'value' above.]
case UDAT_HOUR0_FIELD:
cal.set(UCAL_HOUR, value);
return pos.getIndex();
-
+
case UDAT_QUARTER_FIELD:
if (gotNumber) // i.e., Q or QQ.
{
if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
fSymbols->fQuarters, fSymbols->fQuartersCount, cal)) > 0)
return newStart;
- else // count == 4 failed, now try count == 3
- return matchQuarterString(text, start, UCAL_MONTH,
- fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal);
+ else if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
+ fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal)) > 0)
+ return newStart;
+ else if (!lenient)
+ return newStart;
+ // else we allowing parsing as number, below
}
+ break;
case UDAT_STANDALONE_QUARTER_FIELD:
if (gotNumber) // i.e., q or qq.
if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount, cal)) > 0)
return newStart;
- else // count == 4 failed, now try count == 3
- return matchQuarterString(text, start, UCAL_MONTH,
- fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal);
+ else if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
+ fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal)) > 0)
+ return newStart;
+ else if (!lenient)
+ return newStart;
+ // else we allowing parsing as number, below
}
+ break;
case UDAT_TIMEZONE_FIELD:
- case UDAT_TIMEZONE_RFC_FIELD:
- case UDAT_TIMEZONE_GENERIC_FIELD:
- case UDAT_TIMEZONE_SPECIAL_FIELD:
{
- int32_t offset = 0;
- UBool parsed = FALSE;
-
- // Step 1
- // Check if this is a long GMT offset string (either localized or default)
- offset = parseGMT(text, pos);
- if (pos.getIndex() - start > 0) {
- parsed = TRUE;
- }
- if (!parsed) {
- // Step 2
- // Check if this is an RFC822 time zone offset.
- // ICU supports the standard RFC822 format [+|-]HHmm
- // and its extended form [+|-]HHmmSS.
- do {
- int32_t sign = 0;
- UChar signChar = text.charAt(start);
- if (signChar == (UChar)0x002B /* '+' */) {
- sign = 1;
- } else if (signChar == (UChar)0x002D /* '-' */) {
- sign = -1;
- } else {
- // Not an RFC822 offset string
- break;
- }
-
- // Parse digits
- int32_t orgPos = start + 1;
- pos.setIndex(orgPos);
- parseInt(text, number, 6, pos, FALSE,currentNumberFormat);
- int32_t numLen = pos.getIndex() - orgPos;
- if (numLen <= 0) {
- break;
- }
-
- // Followings are possible format (excluding sign char)
- // HHmmSS
- // HmmSS
- // HHmm
- // Hmm
- // HH
- // H
- int32_t val = number.getLong();
- int32_t hour = 0, min = 0, sec = 0;
- switch(numLen) {
- case 1: // H
- case 2: // HH
- hour = val;
- break;
- case 3: // Hmm
- case 4: // HHmm
- hour = val / 100;
- min = val % 100;
- break;
- case 5: // Hmmss
- case 6: // HHmmss
- hour = val / 10000;
- min = (val % 10000) / 100;
- sec = val % 100;
- break;
- }
- if (hour > 23 || min > 59 || sec > 59) {
- // Invalid value range
- break;
- }
- offset = (((hour * 60) + min) * 60 + sec) * 1000 * sign;
- parsed = TRUE;
- } while (FALSE);
-
- if (!parsed) {
- // Failed to parse. Reset the position.
- pos.setIndex(start);
- }
+ UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
+ UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_SPECIFIC_SHORT : UTZFMT_STYLE_SPECIFIC_LONG;
+ TimeZone *tz = tzFormat()->parse(style, text, pos, tzParseOptions, &tzTimeType);
+ if (tz != NULL) {
+ ((SimpleDateFormat*)this)->tztype = tzTimeType;
+ cal.adoptTimeZone(tz);
+ return pos.getIndex();
}
-
- 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);
- TimeZone *customTZ = new SimpleTimeZone(offset, tzID); // faster than TimeZone::createTimeZone
- cal.adoptTimeZone(customTZ);
-
+ }
+ break;
+ case UDAT_TIMEZONE_RFC_FIELD:
+ {
+ UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
+ UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_RFC822 : ((count == 5) ? UTZFMT_STYLE_ISO8601: UTZFMT_STYLE_LOCALIZED_GMT);
+ TimeZone *tz = tzFormat()->parse(style, text, pos, &tzTimeType);
+ if (tz != NULL) {
+ ((SimpleDateFormat*)this)->tztype = tzTimeType;
+ cal.adoptTimeZone(tz);
return pos.getIndex();
}
-
- // Step 3
- // At this point, check for named time zones by looking through
- // the locale data from the DateFormatZoneData strings.
- // Want to be able to parse both short and long forms.
- // optimize for calendar's current time zone
- const ZoneStringFormat *zsf = fSymbols->getZoneStringFormat();
- if (zsf) {
- UErrorCode status = U_ZERO_ERROR;
- const ZoneStringInfo *zsinfo = NULL;
- int32_t matchLen;
-
- switch (patternCharIndex) {
- case UDAT_TIMEZONE_FIELD: // 'z'
- if (count < 4) {
- zsinfo = zsf->findSpecificShort(text, start, matchLen, status);
- } else {
- zsinfo = zsf->findSpecificLong(text, start, matchLen, status);
- }
- break;
- case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
- if (count == 1) {
- zsinfo = zsf->findGenericShort(text, start, matchLen, status);
- } else if (count == 4) {
- zsinfo = zsf->findGenericLong(text, start, matchLen, status);
- }
- break;
- case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
- if (count == 1) {
- zsinfo = zsf->findSpecificShort(text, start, matchLen, status);
- } else if (count == 4) {
- zsinfo = zsf->findGenericLocation(text, start, matchLen, status);
- }
- break;
- default:
- break;
- }
-
- if (U_SUCCESS(status) && zsinfo != NULL) {
- if (zsinfo->isStandard()) {
- ((SimpleDateFormat*)this)->tztype = TZTYPE_STD;
- } else if (zsinfo->isDaylight()) {
- ((SimpleDateFormat*)this)->tztype = TZTYPE_DST;
- }
- UnicodeString tzid;
- zsinfo->getID(tzid);
-
- UnicodeString current;
- cal.getTimeZone().getID(current);
- if (tzid != current) {
- TimeZone *tz = TimeZone::createTimeZone(tzid);
- cal.adoptTimeZone(tz);
- }
- return start + matchLen;
- }
+ return -start;
+ }
+ case UDAT_TIMEZONE_GENERIC_FIELD:
+ {
+ UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
+ UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_GENERIC_SHORT : UTZFMT_STYLE_GENERIC_LONG;
+ TimeZone *tz = tzFormat()->parse(style, text, pos, tzParseOptions, &tzTimeType);
+ if (tz != NULL) {
+ ((SimpleDateFormat*)this)->tztype = tzTimeType;
+ cal.adoptTimeZone(tz);
+ return pos.getIndex();
}
- // 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"));
+ return -start;
+ }
+ case UDAT_TIMEZONE_SPECIAL_FIELD:
+ {
+ UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
+ UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_SPECIFIC_SHORT : UTZFMT_STYLE_GENERIC_LOCATION;
+ TimeZone *tz = tzFormat()->parse(style, text, pos, &tzTimeType);
+ if (tz != NULL) {
+ ((SimpleDateFormat*)this)->tztype = tzTimeType;
cal.adoptTimeZone(tz);
- return start + gmtLen;
+ return pos.getIndex();
}
-
- // complete failure
return -start;
}
default:
// Handle "generic" fields
- int32_t parseStart = pos.getIndex();
- const UnicodeString* src;
- if (obeyCount) {
- if ((start+count) > text.length()) {
+ // 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 (pos.getIndex() != parseStart) {
+ int32_t value = 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.
+
+ // Check the range of the value
+ if (!lenient) {
+ int32_t bias = gFieldRangeBias[patternCharIndex];
+ if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) {
return -start;
}
- text.extractBetween(0, start + count, temp);
- src = &temp;
} else {
- src = &text;
- }
- parseInt(*src, number, pos, allowNegative,currentNumberFormat);
- if (pos.getIndex() != parseStart) {
- int32_t value = number.getLong();
-
- // Check the range of the value
- int32_t bias = gFieldRangeBias[patternCharIndex];
-
- if (bias < 0 || (value >= cal.getMinimum(field) + bias && value <= cal.getMaximum(field) + bias)) {
- cal.set(field, value);
- return pos.getIndex();
+ int32_t bias = gFieldRangeBiasLenient[patternCharIndex];
+ if (bias >= 0 && (value > cal.getMaximum(field) + bias)) {
+ return -start;
}
}
- 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 (!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 {
+ cal.set(UCAL_MONTH, value - 1);
+ }
+ break;
+ case UDAT_STANDALONE_MONTH_FIELD:
+ cal.set(UCAL_MONTH, value - 1);
+ break;
+ case UDAT_DOW_LOCAL_FIELD:
+ case UDAT_STANDALONE_DAY_FIELD:
+ cal.set(UCAL_DOW_LOCAL, value);
+ break;
+ case UDAT_QUARTER_FIELD:
+ case UDAT_STANDALONE_QUARTER_FIELD:
+ cal.set(UCAL_MONTH, (value - 1) * 3);
+ break;
+ default:
+ cal.set(field, value);
+ break;
+ }
+ return pos.getIndex();
}
+ return -start;
}
/**
DecimalFormat* df = NULL;
if (!allowNegative && (df = dynamic_cast<DecimalFormat*>(fmt)) != NULL) {
df->getNegativePrefix(oldPrefix);
- df->setNegativePrefix(SUPPRESS_NEGATIVE_PREFIX);
+ df->setNegativePrefix(UnicodeString(TRUE, SUPPRESS_NEGATIVE_PREFIX, -1));
}
int32_t oldPos = pos.getIndex();
fmt->parse(text, number, pos);
SimpleDateFormat::toLocalizedPattern(UnicodeString& result,
UErrorCode& status) const
{
- translatePattern(fPattern, result, DateFormatSymbols::getPatternUChars(), fSymbols->fLocalPatternChars, status);
+ translatePattern(fPattern, result,
+ UnicodeString(DateFormatSymbols::getPatternUChars()),
+ fSymbols->fLocalPatternChars, status);
return result;
}
SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern,
UErrorCode &status)
{
- translatePattern(pattern, fPattern, fSymbols->fLocalPatternChars, DateFormatSymbols::getPatternUChars(), status);
+ translatePattern(pattern, fPattern,
+ fSymbols->fLocalPatternChars,
+ UnicodeString(DateFormatSymbols::getPatternUChars()), status);
}
//----------------------------------------------------------------------
fSymbols = new DateFormatSymbols(newFormatSymbols);
}
+//----------------------------------------------------------------------
+const TimeZoneFormat*
+SimpleDateFormat::getTimeZoneFormat(void) const {
+ return (const TimeZoneFormat*)tzFormat();
+}
+
+//----------------------------------------------------------------------
+void
+SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat* timeZoneFormatToAdopt)
+{
+ delete fTimeZoneFormat;
+ fTimeZoneFormat = timeZoneFormatToAdopt;
+}
+
+//----------------------------------------------------------------------
+void
+SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat& newTimeZoneFormat)
+{
+ delete fTimeZoneFormat;
+ fTimeZoneFormat = new TimeZoneFormat(newTimeZoneFormat);
+}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
+void SimpleDateFormat::setDefaultContext(UDateFormatContextType type,
+ UDateFormatContextValue value, UErrorCode& status)
+{
+ if (U_FAILURE(status))
+ return;
+ if (type != UDAT_CAPITALIZATION) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+ fDefaultCapitalizationContext = value;
+}
+
+
+//----------------------------------------------------------------------
+
+
+int32_t SimpleDateFormat::getDefaultContext(UDateFormatContextType type, UErrorCode& status) const
+{
+ if (U_FAILURE(status))
+ return 0;
+ if (type != UDAT_CAPITALIZATION) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+ return (int32_t)fDefaultCapitalizationContext;
+}
+
+
+//----------------------------------------------------------------------
+
+
UBool
SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field) const {
return isFieldUnitIgnored(fPattern, field);
for (int32_t i=0; i<affix.length(); ) {
UChar32 c = affix.char32At(i);
int32_t len = U16_LENGTH(c);
- if (uprv_isRuleWhiteSpace(c)) {
+ if (PatternProps::isWhiteSpace(c)) {
// We may have a pattern like: \u200F \u0020
// and input text like: \u200F \u0020
- // Note that U+200F and U+0020 are RuleWhiteSpace but only
+ // Note that U+200F and U+0020 are Pattern_White_Space but only
// U+0020 is UWhiteSpace. So we have to first do a direct
- // match of the run of RULE whitespace in the pattern,
+ // match of the run of Pattern_White_Space in the pattern,
// then match any extra characters.
UBool literalMatch = FALSE;
while (pos < input.length() &&
}
c = affix.char32At(i);
len = U16_LENGTH(c);
- if (!uprv_isRuleWhiteSpace(c)) {
+ if (!PatternProps::isWhiteSpace(c)) {
break;
}
}
// Advance over run in pattern
- i = skipRuleWhiteSpace(affix, i);
+ i = skipPatternWhiteSpace(affix, i);
// Advance over run in input text
// Must see at least one white space char in input,
//----------------------------------------------------------------------
int32_t
-SimpleDateFormat::skipRuleWhiteSpace(const UnicodeString& text, int32_t pos) const {
- while (pos < text.length()) {
- UChar32 c = text.char32At(pos);
- if (!uprv_isRuleWhiteSpace(c)) {
- break;
- }
- pos += U16_LENGTH(c);
- }
- return pos;
+SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) const {
+ const UChar* s = text.getBuffer();
+ return (int32_t)(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s);
}
//----------------------------------------------------------------------
return pos;
}
+//----------------------------------------------------------------------
+
+// Lazy TimeZoneFormat instantiation, semantically const.
+TimeZoneFormat *
+SimpleDateFormat::tzFormat() const {
+ if (fTimeZoneFormat == NULL) {
+ umtx_lock(&LOCK);
+ {
+ if (fTimeZoneFormat == NULL) {
+ UErrorCode status = U_ZERO_ERROR;
+ TimeZoneFormat *tzfmt = TimeZoneFormat::createInstance(fLocale, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ const_cast<SimpleDateFormat *>(this)->fTimeZoneFormat = tzfmt;
+ }
+ }
+ umtx_unlock(&LOCK);
+ }
+ return fTimeZoneFormat;
+}
+
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */