/*
*******************************************************************************
-* Copyright (C) 1997-2009, International Business Machines Corporation and *
+* Copyright (C) 1997-2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
#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"
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[] =
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.
}
}
uprv_free(fGMTFormatters);
+
+ }
+ if (fNumberFormatters) {
+ uprv_free(fNumberFormatters);
+ }
+
+ while (fOverrideList) {
+ NSOverride *cur = fOverrideList;
+ fOverrideList = cur->next;
+ delete cur->nf;
+ uprv_free(cur);
}
}
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();
: 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);
+
}
//----------------------------------------------------------------------
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();
//----------------------------------------------------------------------
+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();
: 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();
UErrorCode& status)
: fLocale(locale),
fSymbols(NULL),
- fGMTFormatters(NULL)
+ fGMTFormatters(NULL),
+ fNumberFormatters(NULL),
+ fOverrideList(NULL)
{
construct(timeStyle, dateStyle, fLocale, status);
if(U_SUCCESS(status)) {
: 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);
}
}
+ fDateOverride.setToBogus();
+ fTimeOverride.setToBogus();
+
initialize(fLocale, status);
if(U_SUCCESS(status)) {
initializeDefaultCentury();
SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
: DateFormat(other),
fSymbols(NULL),
- fGMTFormatters(NULL)
+ fGMTFormatters(NULL),
+ fNumberFormatters(NULL),
+ fOverrideList(NULL)
{
*this = other;
}
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)
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
// 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) {
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) {
}
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;
// 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<DecimalFormat*>(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))
{
/* 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();
/* 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;
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
-
+
fCalendar->setTime(startDate, status);
if(U_SUCCESS(status)) {
fHaveDefaultCentury = TRUE;
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) {
} 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
// 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;
}
//----------------------------------------------------------------------
/*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
};
//---------------------------------------------------------------------
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)) {
// 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());
// 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());
}
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.
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 */) {
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) {
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
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
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;
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<DecimalFormat*>(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 ; i<kDateFieldsCount; i++ ) {
+ fNumberFormatters[kDateFields[i]] = nf;
+ }
+ if (type==kOvrStrDate) {
+ break;
+ }
+ }
+ case kOvrStrTime : {
+ for ( int8_t i=0 ; i<kTimeFieldsCount; i++ ) {
+ fNumberFormatters[kTimeFields[i]] = nf;
+ }
+ break;
+ }
+ }
+ } else {
+ UChar ch = ovrField.charAt(0);
+ UChar *patternCharPtr = u_strchr(DateFormatSymbols::getPatternUChars(), ch);
+ UDateFormatField patternCharIndex;
+
+ // if the pattern character is unrecognized, signal an error and bail out
+ if (patternCharPtr == NULL) {
+ status = U_INVALID_FORMAT_ERROR;
+ return;
+ }
+ patternCharIndex = (UDateFormatField)(patternCharPtr - DateFormatSymbols::getPatternUChars());
+
+ // Set the number formatter in the table
+ fNumberFormatters[patternCharIndex] = nf;
+ }
+
+ start = delimiterPosition + 1;
+ }
+}
//---------------------------------------------------------------------
void
SimpleDateFormat::subFormat(UnicodeString &appendTo,
UChar ch,
int32_t count,
- FieldPosition& pos,
+ FieldPositionHandler& handler,
Calendar& cal,
UErrorCode& status) const
{
UDateFormatField patternCharIndex;
const int32_t maxIntCount = 10;
int32_t beginOffset = appendTo.length();
+ NumberFormat *currentNumberFormat;
+
+ UBool isHebrewCalendar = !strcmp(cal.getType(),"hebrew");
// if the pattern character is unrecognized, signal an error and dump out
if (patternCharPtr == NULL)
return;
}
+ currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
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:
//AD 123 123 23 123 0123 00123
//AD 1234 1234 34 1234 1234 01234
//AD 12345 12345 45 12345 12345 12345
- case UDAT_YEAR_FIELD:
+ case UDAT_YEAR_FIELD:
case UDAT_YEAR_WOY_FIELD:
if(count == 2)
- zeroPaddingNumber(appendTo, value, 2, 2);
- else
- zeroPaddingNumber(appendTo, value, count, maxIntCount);
- break;
+ 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
// appropriate number of digits
// for "MMMMM", use the narrow form
case UDAT_MONTH_FIELD:
- if (count == 5)
+ if ( isHebrewCalendar ) {
+ HebrewCalendar *hc = (HebrewCalendar*)&cal;
+ if (hc->isLeapYear(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
// 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;
// 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,
}
// 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
// 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,
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
// 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
// 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();
}
}
if (zoneString.isEmpty()) {
- appendGMT(appendTo, cal, status);
+ appendGMT(currentNumberFormat,appendTo, cal, status);
} else {
appendTo += zoneString;
}
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
}
}
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
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; i<fPattern.length(); ++i) {
UChar ch = fPattern.charAt(i);
if (fieldPat == abutPat) {
count -= abutPass++;
if (count == 0) {
- parsePos.setIndex(start);
- parsePos.setErrorIndex(pos);
- return;
+ status = U_PARSE_ERROR;
+ goto ExitParse;
}
}
pos = subParse(text, pos, ch, count,
- TRUE, FALSE, ambiguousYear, cal);
+ TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i);
// If the parse fails anywhere in the run, back up to the
// start of the run and retry.
// fields.
else {
int32_t s = subParse(text, pos, ch, count,
- FALSE, TRUE, ambiguousYear, cal);
-
- if (s <= 0) {
+ FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i);
+
+ if (s == -pos-1) {
+ // era not present, in special cases allow this to continue
+ s++;
+
+ if (i+1 < fPattern.length()) {
+ // move to next pattern character
+ UChar ch = fPattern.charAt(i+1);
+
+ // check for whitespace
+ if (uprv_isRuleWhiteSpace(ch)) {
+ i++;
+ // Advance over run in pattern
+ while ((i+1)<fPattern.length() &&
+ uprv_isRuleWhiteSpace(fPattern.charAt(i+1))) {
+ ++i;
+ }
+ }
+ }
+ }
+ else if (s <= 0) {
status = U_PARSE_ERROR;
goto ExitParse;
}
// quoted characters and non-alphabetic unquoted
// characters.
else {
-
+
abutPat = -1; // End of any abutting fields
if (! matchLiterals(fPattern, i, text, pos, lenient)) {
copy = cal.clone();
// Check for failed cloning.
if (copy == NULL) {
- status = U_MEMORY_ALLOCATION_ERROR;
- goto ExitParse;
+ status = U_MEMORY_ALLOCATION_ERROR;
+ goto ExitParse;
}
UDate parsedDate = copy->getTime(status);
// {sfb} check internalGetDefaultCenturyStart
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<const OlsonTimeZone *>(&tz) != NULL
+ || dynamic_cast<const SimpleTimeZone *>(&tz) != NULL
+ || dynamic_cast<const RuleBasedTimeZone *>(&tz) != NULL
+ || dynamic_cast<const VTimeZone *>(&tz) != NULL) {
btz = (BasicTimeZone*)&tz;
}
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);
}
}
lcase.fastCopyFrom(data[i]).foldCase();
int32_t length = lcase.length();
-
+
if (length > bestMatchLength &&
lcaseText.compareBetween(0, length, lcase, 0, length) == 0)
{
}
}
}
-
+
return -start;
}
t += 1;
}
}
-
+
for (p = 0; p < literal.length() && t < text.length(); p += 1, t += 1) {
UBool needWhitespace = FALSE;
lcase.fastCopyFrom(data[i]).foldCase();
int32_t length = lcase.length();
-
+
if (length > bestMatchLength &&
lcaseText.compareBetween(0, length, lcase, 0, length) == 0)
{
}
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
}
}
}
-
+
return -start;
}
* 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();
}
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
}
UChar32 c = text.char32At(start);
-
- if (!u_isUWhiteSpace(c)) {
+ if (!u_isUWhiteSpace(c) /*||*/ && !uprv_isRuleWhiteSpace(c)) {
break;
}
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
// 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
}
}
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:
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
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();
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.
{
// 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;
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);
zsinfo = zsf->findGenericLocation(text, start, matchLen, status);
}
break;
+ default:
+ break;
}
if (U_SUCCESS(status) && zsinfo != NULL) {
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;
}
} else {
src = &text;
}
- parseInt(*src, number, pos, allowNegative);
+ parseInt(*src, number, pos, allowNegative,currentNumberFormat);
if (pos.getIndex() != parseStart) {
int32_t value = number.getLong();
return pos.getIndex();
}
}
-
+
return -start;
}
}
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);
}
/**
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<DecimalFormat*>(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);
}
// 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) {
{
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)
UBool
-SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern,
+SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern,
UCalendarDateFields field) {
int32_t fieldLevel = fgCalendarFieldToLevel[field];
int32_t level;
} 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;
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<DecimalFormat*>(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<affix.length(); ) {
+ UChar32 c = affix.char32At(i);
+ int32_t len = U16_LENGTH(c);
+ if (uprv_isRuleWhiteSpace(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
+ // U+0020 is UWhiteSpace. So we have to first do a direct
+ // match of the run of RULE whitespace in the pattern,
+ // then match any extra characters.
+ UBool literalMatch = FALSE;
+ while (pos < input.length() &&
+ input.char32At(pos) == c) {
+ literalMatch = TRUE;
+ i += len;
+ pos += len;
+ if (i == affix.length()) {
+ break;
+ }
+ c = affix.char32At(i);
+ len = U16_LENGTH(c);
+ if (!uprv_isRuleWhiteSpace(c)) {
+ break;
+ }
+ }
+
+ // Advance over run in pattern
+ i = skipRuleWhiteSpace(affix, i);
+
+ // Advance over run in input text
+ // Must see at least one white space char in input,
+ // unless we've already matched some characters literally.
+ int32_t s = pos;
+ pos = skipUWhiteSpace(input, pos);
+ if (pos == s && !literalMatch) {
+ return -1;
+ }
+
+ // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
+ // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
+ // is also in the affix.
+ i = skipUWhiteSpace(affix, i);
+ } else {
+ if (pos < input.length() &&
+ input.char32At(pos) == c) {
+ i += len;
+ pos += len;
+ } else {
+ return -1;
+ }
+ }
+ }
+ return pos - start;
+}
+
+//----------------------------------------------------------------------
+
+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;
+}
+
+//----------------------------------------------------------------------
+
+int32_t
+SimpleDateFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) const {
+ while (pos < text.length()) {
+ UChar32 c = text.char32At(pos);
+ if (!u_isUWhiteSpace(c)) {
+ break;
+ }
+ pos += U16_LENGTH(c);
+ }
+ return pos;
+}
U_NAMESPACE_END