home *** CD-ROM | disk | FTP | other *** search
- /*
- ********************************************************************************
- * *
- * COPYRIGHT: *
- * (C) Copyright Taligent, Inc., 1997 *
- * (C) Copyright International Business Machines Corporation, 1997-1998 *
- * Copyright (C) 1999 Alan Liu and others. All rights reserved. *
- * Licensed Material - Program-Property of IBM - All Rights Reserved. *
- * US Government Users Restricted Rights - Use, duplication, or disclosure *
- * restricted by GSA ADP Schedule Contract with IBM Corp. *
- * *
- ********************************************************************************
- *
- * File SMPDTFMT.CPP
- *
- * Modification History:
- *
- * Date Name Description
- * 02/19/97 aliu Converted from java.
- * 03/31/97 aliu Modified extensively to work with 50 locales.
- * 04/01/97 aliu Added support for centuries.
- * 07/09/97 helena Made ParsePosition into a class.
- * 07/21/98 stephen Added initializeDefaultCentury.
- * Removed getZoneIndex (added in DateFormatSymbols)
- * Removed subParseLong
- * Removed chk
- * 02/22/99 stephen Removed character literals for EBCDIC safety
- * 10/14/99 aliu Updated 2-digit year parsing so that only "00" thru
- * "99" are recognized. {j28 4182066}
- ********************************************************************************
- */
-
- #include "smpdtfmt.h"
- #include "dtfmtsym.h"
- #include "resbund.h"
- #include "msgfmt.h"
- #include "calendar.h"
- #include "gregocal.h"
- #include "timezone.h"
- #include "decimfmt.h"
- #include "dcfmtsym.h"
- #include "mutex.h"
- #include <float.h>
- #ifdef _DEBUG
- #include <iostream.h>
- #endif
-
- // *****************************************************************************
- // class SimpleDateFormat
- // *****************************************************************************
-
- // For time zones that have no names, use strings GMT+minutes and
- // GMT-minutes. For instance, in France the time zone is GMT+60.
- // Also accepted are GMT+H:MM or GMT-H:MM.
- const UnicodeString SimpleDateFormat::fgGmt("GMT");
- const UnicodeString SimpleDateFormat::fgGmtPlus("GMT+");
- const UnicodeString SimpleDateFormat::fgGmtMinus("GMT-");
-
- // This is a pattern-of-last-resort used when we can't load a usable pattern out
- // of a resource.
- const UnicodeString SimpleDateFormat::fgDefaultPattern("yyMMdd hh:mm a");
-
- /**
- * These are the tags we expect to see in normal resource bundle files associated
- * with a locale.
- */
- const UnicodeString SimpleDateFormat::fgErasTag("Eras");
- const UnicodeString SimpleDateFormat::fgMonthNamesTag("MonthNames");
- const UnicodeString SimpleDateFormat::fgMonthAbbreviationsTag("MonthAbbreviations");
- const UnicodeString SimpleDateFormat::fgDayNamesTag("DayNames");
- const UnicodeString SimpleDateFormat::fgDayAbbreviationsTag("DayAbbreviations");
- const UnicodeString SimpleDateFormat::fgAmPmMarkersTag("AmPmMarkers");
- const UnicodeString SimpleDateFormat::fgDateTimePatternsTag("DateTimePatterns");
-
- /**
- * These are the tags we expect to see in time zone data resource bundle files
- * associated with a locale.
- */
- const UnicodeString SimpleDateFormat::fgZoneStringsTag("zoneStrings");
- const UnicodeString SimpleDateFormat::fgLocalPatternCharsTag("localPatternChars");
-
- char SimpleDateFormat::fgClassID = 0; // Value is irrelevant
-
- /**
- * This value of defaultCenturyStart indicates that the system default is to be
- * used.
- */
- const UDate SimpleDateFormat::fgSystemDefaultCentury = DBL_MIN;
- const int32_t SimpleDateFormat::fgSystemDefaultCenturyYear = -1;
-
- UDate SimpleDateFormat::fgSystemDefaultCenturyStart = SimpleDateFormat::fgSystemDefaultCentury;
- int32_t SimpleDateFormat::fgSystemDefaultCenturyStartYear = SimpleDateFormat::fgSystemDefaultCenturyYear;
-
- //----------------------------------------------------------------------
-
- SimpleDateFormat::~SimpleDateFormat()
- {
- delete fSymbols;
- }
-
- //----------------------------------------------------------------------
-
- SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
- : fSymbols(NULL),
- fDefaultCenturyStart(fgSystemDefaultCentury),
- fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
- {
- construct(kShort, (EStyle) (kShort + kDateOffset), Locale::getDefault(), status);
- }
-
- //----------------------------------------------------------------------
-
- SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
- UErrorCode &status)
- : fPattern(pattern),
- fSymbols(new DateFormatSymbols(status)),
- fDefaultCenturyStart(fgSystemDefaultCentury),
- fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
- {
- initialize(Locale::getDefault(), status);
- }
-
- //----------------------------------------------------------------------
-
- SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
- const Locale& locale,
- UErrorCode& status)
- : fPattern(pattern),
- fSymbols(new DateFormatSymbols(locale, status)),
- fDefaultCenturyStart(fgSystemDefaultCentury),
- fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
- {
- initialize(locale, status);
- }
-
- //----------------------------------------------------------------------
-
- SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
- DateFormatSymbols* symbolsToAdopt,
- UErrorCode& status)
- : fPattern(pattern),
- fSymbols(symbolsToAdopt),
- fDefaultCenturyStart(fgSystemDefaultCentury),
- fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
- {
- initialize(Locale::getDefault(), status);
- }
-
- //----------------------------------------------------------------------
-
- SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
- const DateFormatSymbols& symbols,
- UErrorCode& status)
- : fPattern(pattern),
- fSymbols(new DateFormatSymbols(symbols)),
- fDefaultCenturyStart(fgSystemDefaultCentury),
- fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
- {
- initialize(Locale::getDefault(), status);
- }
-
- //----------------------------------------------------------------------
-
- // Not for public consumption; used by DateFormat
- SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
- EStyle dateStyle,
- const Locale& locale,
- UErrorCode& status)
- : fSymbols(NULL),
- fDefaultCenturyStart(fgSystemDefaultCentury),
- fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
- {
- construct(timeStyle, dateStyle, locale, status);
- }
-
- //----------------------------------------------------------------------
-
- /**
- * Not for public consumption; used by DateFormat. This constructor
- * never fails. If the resource data is not available, it uses the
- * the last resort symbols.
- */
- SimpleDateFormat::SimpleDateFormat(const Locale& locale,
- UErrorCode& status)
- : fPattern(fgDefaultPattern),
- fSymbols(NULL),
- fDefaultCenturyStart(fgSystemDefaultCentury),
- fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
- {
- if (U_FAILURE(status)) return;
- fSymbols = new DateFormatSymbols(locale, status);
- if (U_FAILURE(status))
- {
- status = U_ZERO_ERROR;
- delete fSymbols;
- // This constructor doesn't fail; it uses last resort data
- fSymbols = new DateFormatSymbols(status);
- }
- initialize(locale, status);
- }
-
- //----------------------------------------------------------------------
-
- SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
- : DateFormat(other),
- fSymbols(NULL),
- fDefaultCenturyStart(fgSystemDefaultCentury),
- fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
- {
- *this = other;
- }
-
- //----------------------------------------------------------------------
-
- SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
- {
- DateFormat::operator=(other);
-
- delete fSymbols;
- fSymbols = NULL;
-
- if (other.fSymbols)
- fSymbols = new DateFormatSymbols(*other.fSymbols);
-
- fDefaultCenturyStart = other.fDefaultCenturyStart;
- fDefaultCenturyStartYear = other.fDefaultCenturyStartYear;
-
- fPattern = other.fPattern;
-
- return *this;
- }
-
- //----------------------------------------------------------------------
-
- Format*
- SimpleDateFormat::clone() const
- {
- return new SimpleDateFormat(*this);
- }
-
- //----------------------------------------------------------------------
-
- bool_t
- SimpleDateFormat::operator==(const Format& other) const
- {
- if (DateFormat::operator==(other) &&
- other.getDynamicClassID() == getStaticClassID())
- {
- SimpleDateFormat* that = (SimpleDateFormat*)&other;
- return (fPattern == that->fPattern &&
- fSymbols != NULL && // Check for pathological object
- that->fSymbols != NULL && // Check for pathological object
- *fSymbols == *that->fSymbols &&
- fDefaultCenturyStart == that->fDefaultCenturyStart);
- }
- return FALSE;
- }
-
- //----------------------------------------------------------------------
-
- void SimpleDateFormat::construct(EStyle timeStyle,
- EStyle dateStyle,
- const Locale& locale,
- UErrorCode& status)
- {
- // called by several constructors to load pattern data from the resources
-
- if (U_FAILURE(status)) return;
-
- // load up the DateTimePatters resource from the appropriate locale (throw
- // an error if for some weird reason the resource is malformed)
- ResourceBundle resources(Locale::getDataDirectory(), locale, status);
- int32_t dtCount;
- const UnicodeString *dateTimePatterns = resources.getStringArray(fgDateTimePatternsTag, dtCount, status);
- if (U_FAILURE(status)) return;
- if (dtCount <= kDateTime)
- {
- status = U_INVALID_FORMAT_ERROR;
- return;
- }
-
- // create a symbols object from the locale
- fSymbols = new DateFormatSymbols(locale, status);
-
- UnicodeString str;
-
- // Move dateStyle from the range [0, 3] to [4, 7] if necessary
- //if (dateStyle >= 0 && dateStyle < DATE_OFFSET) dateStyle = (EStyle)(dateStyle + DATE_OFFSET);
-
- // if the pattern should include both date and time information, use the date/time
- // pattern string as a guide to tell use how to glue together the appropriate date
- // and time pattern strings. The actual gluing-together is handled by a convenience
- // method on MessageFormat.
- if ((timeStyle != kNone) &&
- (dateStyle != kNone))
- {
- // Object[] dateTimeArgs = {
- // dateTimePatterns[timeStyle], dateTimePatterns[dateStyle]
- // };
- // pattern = MessageFormat.format(dateTimePatterns[8], dateTimeArgs);
-
- Formattable *timeDateArray = new Formattable[2];
- timeDateArray[0].setString(dateTimePatterns[timeStyle]);
- timeDateArray[1].setString(dateTimePatterns[dateStyle]);
-
- MessageFormat::format(dateTimePatterns[kDateTime], timeDateArray, 2, fPattern, status);
- delete [] timeDateArray;
- }
-
- // if the pattern includes just time data or just date date, load the appropriate
- // pattern string from the resources
- else if (timeStyle != kNone) fPattern = dateTimePatterns[timeStyle];
- else if (dateStyle != kNone) fPattern = dateTimePatterns[dateStyle];
-
- // and if it includes _neither_, that's an error
- else status = U_INVALID_FORMAT_ERROR;
-
- // finally, finish initializing by creating a Calendar and a NumberFormat
- initialize(locale, status);
- }
-
- //----------------------------------------------------------------------
-
- void
- SimpleDateFormat::initialize(const Locale& locale,
- UErrorCode& status)
- {
- if (U_FAILURE(status)) return;
-
- // {sfb} should this be here?
- if (fSymbols->fZoneStringsColCount < 1)
- {
- status = U_INVALID_FORMAT_ERROR; // Check for bogus locale data
- return;
- }
-
- // We don't need to check that the row count is >= 1, since all 2d arrays have at
- // least one row
- fCalendar = Calendar::createInstance(TimeZone::createDefault(), locale, status);
- fNumberFormat = NumberFormat::createInstance(locale, status);
- if (fNumberFormat != NULL && U_SUCCESS(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
-
- fNumberFormat->setGroupingUsed(FALSE);
- if (fNumberFormat->getDynamicClassID() == DecimalFormat::getStaticClassID())
- ((DecimalFormat*)fNumberFormat)->setDecimalSeparatorAlwaysShown(FALSE);
- fNumberFormat->setParseIntegerOnly(TRUE);
- fNumberFormat->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
-
- initializeDefaultCentury();
- }
- else if (U_SUCCESS(status))
- {
- status = U_MISSING_RESOURCE_ERROR;
- }
- }
-
- /* Initialize the fields we use to disambiguate ambiguous years. Separate
- * so we can call it from readObject().
- */
- void SimpleDateFormat::initializeDefaultCentury()
- {
- fDefaultCenturyStart = internalGetDefaultCenturyStart();
- fDefaultCenturyStartYear = internalGetDefaultCenturyStartYear();
-
- UErrorCode status = U_ZERO_ERROR;
- fCalendar->setTime(fDefaultCenturyStart, status);
- // {sfb} throw away error
- }
-
- /* 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)
- {
- if(U_FAILURE(status))
- return;
-
- fCalendar->setTime(startDate, status);
- if(U_SUCCESS(status)) {
- fDefaultCenturyStart = startDate;
- fDefaultCenturyStartYear = fCalendar->get(Calendar::YEAR, status);
- }
- }
-
- //----------------------------------------------------------------------
-
- UnicodeString&
- SimpleDateFormat::format(UDate date, UnicodeString& toAppendTo, FieldPosition& pos) const
- {
- UErrorCode status = U_ZERO_ERROR;
- pos.setBeginIndex(0);
- pos.setEndIndex(0);
-
- // load up our Calendar with the date/time we're formatting (the subroutines of this
- // function pick it up from there, since they need it anyway to split the value
- // into fields)
- fCalendar->setTime(date, status);
-
- bool_t inQuote = FALSE;
- UChar prevCh = 0;
- int32_t count = 0;
- UnicodeString str;
-
- // loop through the pattern string character by character
- for (int32_t i = 0; i < fPattern.size() && 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) {
- toAppendTo += subFormat(str, prevCh, count, toAppendTo.size(), pos, status);
- count = 0;
- }
- if (ch == 0x0027 /*'\''*/) {
- // Consecutive single quotes are a single quote literal,
- // either outside of quotes or between quotes
- if ((i+1) < fPattern.size() && fPattern[i+1] == 0x0027 /*'\''*/) {
- toAppendTo += 0x0027 /*'\''*/;
- ++i;
- } else {
- inQuote = ! inQuote;
- }
- }
- 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
- prevCh = ch;
- ++count;
- }
- else {
- // Append quoted characters and unquoted non-pattern characters
- toAppendTo += ch;
- }
- }
-
- // Format the last item in the pattern, if any
- if (count > 0) {
- toAppendTo += subFormat(str, prevCh, count, toAppendTo.size(), pos, 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);
- }
-
- return toAppendTo;
- }
-
- UnicodeString&
- SimpleDateFormat::format(const Formattable& obj,
- UnicodeString& toAppendTo,
- 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, toAppendTo, pos, status);
- }
-
- //----------------------------------------------------------------------
-
- // Map index into pattern character string to Calendar field number.
- const Calendar::EDateFields
- SimpleDateFormat::fgPatternIndexToCalendarField[] =
- {
- Calendar::ERA, Calendar::YEAR, Calendar::MONTH, Calendar::DATE,
- Calendar::HOUR_OF_DAY, Calendar::HOUR_OF_DAY, Calendar::MINUTE,
- Calendar::SECOND, Calendar::MILLISECOND, Calendar::DAY_OF_WEEK,
- Calendar::DAY_OF_YEAR, Calendar::DAY_OF_WEEK_IN_MONTH,
- Calendar::WEEK_OF_YEAR, Calendar::WEEK_OF_MONTH,
- Calendar::AM_PM, Calendar::HOUR, Calendar::HOUR, Calendar::ZONE_OFFSET
- };
-
- // Map index into pattern character string to DateFormat field number
- const DateFormat::EField
- SimpleDateFormat::fgPatternIndexToDateFormatField[] = {
- DateFormat::kEraField, DateFormat::kYearField, DateFormat::kMonthField,
- DateFormat::kDateField, DateFormat::kHourOfDay1Field,
- DateFormat::kHourOfDay0Field, DateFormat::kMinuteField,
- DateFormat::kSecondField, DateFormat::kMillisecondField,
- DateFormat::kDayOfWeekField, DateFormat::kDayOfYearField,
- DateFormat::kDayOfWeekInMonthField, DateFormat::kWeekOfYearField,
- DateFormat::kWeekOfMonthField, DateFormat::kAmPmField,
- DateFormat::kHour1Field, DateFormat::kHour0Field,
- DateFormat::kTimezoneField,
- };
-
-
- //----------------------------------------------------------------------
-
- UnicodeString&
- SimpleDateFormat::subFormat(UnicodeString& result,
- UChar ch,
- int32_t count,
- int32_t beginOffset,
- FieldPosition& pos,
- UErrorCode& status) const
- {
- // this function gets called by format() to produce the appropriate substitution
- // text for an individual pattern symbol (e.g., "HH" or "yyyy")
-
- EField patternCharIndex = (EField) -1;
- int32_t maxIntCount = 10;
- UnicodeString str; // Scratch
- result.remove();
-
- // if the pattern character is unrecognized, signal an error and dump out
- if ((patternCharIndex = (EField)DateFormatSymbols::fgPatternChars.indexOf(ch)) == (EField)-1)
- {
- status = U_INVALID_FORMAT_ERROR;
- return result;
- }
-
- Calendar::EDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
- int32_t value = fCalendar->get(field, status);
- if (U_FAILURE(status)) return result;
-
- switch (patternCharIndex) {
-
- // for any "G" symbol, write out the appropriate era string
- case kEraField:
- result = fSymbols->fEras[value];
- break;
-
- // for "yyyy", write out the whole year; for "yy", write out the last 2 digits
- case kYearField:
- if (count >= 4)
- zeroPaddingNumber(result, value, 4, maxIntCount);
- else
- zeroPaddingNumber(result, value, 2, 2);
- 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
- case kMonthField:
- if (count >= 4)
- result = fSymbols->fMonths[value];
- else if (count == 3)
- result = fSymbols->fShortMonths[value];
- else
- zeroPaddingNumber(result, value + 1, count, maxIntCount);
- break;
-
- // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
- case kHourOfDay1Field:
- if (value == 0)
- zeroPaddingNumber(result, fCalendar->getMaximum(Calendar::HOUR_OF_DAY) + 1, count, maxIntCount);
- else
- zeroPaddingNumber(result, value, count, maxIntCount);
- break;
-
- // for "SS" and "S", we want to truncate digits so that you still see the MOST
- // significant digits rather than the LEAST (as is the case with the year)
- case kMillisecondField:
- if (count > 3)
- count = 3;
- else if (count == 2)
- value = value / 10;
- else if (count == 1)
- value = value / 100;
- zeroPaddingNumber(result, value, count, maxIntCount);
- break;
-
- // for "EEEE", write out the day-of-the-week name; otherwise, use the abbreviation
- case kDayOfWeekField:
- if (count >= 4)
- result = fSymbols->fWeekdays[value];
- else
- result = fSymbols->fShortWeekdays[value];
- break;
-
- // for and "a" symbol, write out the whole AM/PM string
- case kAmPmField:
- result = fSymbols->fAmPms[value];
- break;
-
- // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
- // as "12"
- case kHour1Field:
- if (value == 0)
- zeroPaddingNumber(result, fCalendar->getLeastMaximum(Calendar::HOUR) + 1, count, maxIntCount);
- else
- zeroPaddingNumber(result, value, count, maxIntCount);
- break;
-
- // for the "z" symbols, we have to check our time zone data first. If we have a
- // localized name for the time zone, then "zzzz" is the whole name and anything
- // shorter is the abbreviation (we also have to check for daylight savings time
- // since the name will be different). 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 kTimezoneField: {
- int32_t zoneIndex = fSymbols->getZoneIndex(fCalendar->getTimeZone().getID(str));
- if (zoneIndex == -1) {
- UnicodeString zoneString;
-
- value = fCalendar->get(Calendar::ZONE_OFFSET, status) +
- fCalendar->get(Calendar::DST_OFFSET, status);
-
- if (value < 0) {
- zoneString += fgGmtMinus;
- value = -value; // suppress the '-' sign for text display.
- }
- else
- zoneString += fgGmtPlus;
-
- zoneString += zeroPaddingNumber(str, (int32_t)(value/U_MILLIS_PER_HOUR), 2, 2);
- zoneString += 0x003A /*':'*/;
- zoneString += zeroPaddingNumber(str, (int32_t)((value%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE), 2, 2);
-
- result = zoneString;
- }
- else if (fCalendar->get(Calendar::DST_OFFSET, status) != 0) {
- if (count >= 4)
- result = fSymbols->fZoneStrings[zoneIndex][3];
- else
- result = fSymbols->fZoneStrings[zoneIndex][4];
- }
- else {
- if (count >= 4)
- result = fSymbols->fZoneStrings[zoneIndex][1];
- else
- result = fSymbols->fZoneStrings[zoneIndex][2];
- }
- }
- break;
-
- // all of the other pattern symbols can be formatted as simple numbers with
- // appropriate zero padding
- default:
- // case kDateField:
- // case kHourOfDay0Field:
- // case kMinuteField:
- // case kSecondField:
- // case kDayOfYearField:
- // case kDayOfWeekInMonthField:
- // case kWeekOfYearField:
- // case kWeekOfMonthField:
- // case kHour0Field:
- zeroPaddingNumber(result, 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.getField() == fgPatternIndexToDateFormatField[patternCharIndex]) {
- if (pos.getBeginIndex() == 0 && pos.getEndIndex() == 0) {
- pos.setBeginIndex(beginOffset);
- pos.setEndIndex(beginOffset + result.size());
- }
- }
-
- return result;
- }
-
- //----------------------------------------------------------------------
-
- UnicodeString&
- SimpleDateFormat::zeroPaddingNumber(UnicodeString& result, int32_t value, int32_t minDigits, int32_t maxDigits) const
- {
- result.remove();
- fNumberFormat->setMinimumIntegerDigits(minDigits);
- fNumberFormat->setMaximumIntegerDigits(maxDigits);
- return fNumberFormat->format(value, result);
- }
-
- //----------------------------------------------------------------------
-
- // {sfb} removed
- /*
- // this function will dump output to the console on a debug build when there's a parse error
- #ifdef _DEBUG
- void chk(ParsePosition& val, UChar ch, ParsePosition& start, int32_t count)
- {
- if (val.getIndex() < 0)
- {
- cout << "[Parse failure on '" << (char)ch << "' x " << dec << count << " @ " << start.getIndex() << ']';
- }
- }
- #else
- inline void chk(ParsePosition& val, UChar ch, ParsePosition& start, int32_t count)
- {
- }
- #endif
-
- inline Date
- parseFailureResult(ParsePosition& pos, ParsePosition& oldStart, ParsePosition& failurePos)
- {
- // Note: The C++ version currently supports the notion of returning zero
- // with a non-zero parse position, but only if this format is lenient.
- // The returned position in this case is the first un-parseable character.
- // This is useful, but is not present in the Java version, and causes a
- // DateFormat test to fail.
-
- // For now, I am removing this function. It can be restored later.
-
- // if (!isLenient()) pos = oldStart;
- // else { pos = failurePos.getIndex(); if (pos.getIndex() < 0) pos = -pos.getIndex(); };
- pos = oldStart;
- return 0;
- }
- */
-
- UDate
- SimpleDateFormat::parse(const UnicodeString& text, ParsePosition& pos) const
- {
- int32_t start = pos.getIndex();
- int32_t oldStart = start;
- bool_t ambiguousYear[] = { FALSE };
-
- fCalendar->clear();
-
- bool_t inQuote = FALSE;
- UChar prevCh = 0;
- int32_t count = 0;
- int32_t interQuoteCount = 1; // Number of chars between quotes
-
- // loop through the pattern string character by character, using it to control how
- // we match characters in the input
- for (int32_t i = 0; i < fPattern.size();++i) {
- UChar ch = fPattern[i];
-
- // if we're inside a quoted string, match characters exactly until we hit
- // another single quote (two single quotes in a row match one single quote
- // in the input)
- if (inQuote)
- {
- if (ch == 0x0027 /*'\''*/)
- {
- // ends with 2nd single quote
- inQuote = FALSE;
- // two consecutive quotes outside a quote means we have
- // a quote literal we need to match.
- if (count == 0)
- {
- if(start > text.size() || ch != text[start])
- {
- pos.setIndex(oldStart);
- pos.setErrorIndex(start);
- // {sfb} what is the correct Date for failure?
- return 0;
- }
- ++start;
- }
- count = 0;
- interQuoteCount = 0;
- }
- else
- {
- // pattern uses text following from 1st single quote.
- if (start >= text.size() || ch != text[start]) {
- // Check for cases like: 'at' in pattern vs "xt"
- // in time text, where 'a' doesn't match with 'x'.
- // If fail to match, return null.
- pos.setIndex(oldStart); // left unchanged
- pos.setErrorIndex(start);
- // {sfb} what is correct Date for failure?
- return 0;
- }
- ++count;
- ++start;
- }
- }
-
- // if we're not inside a quoted string...
- else {
-
- // ...a quote mark puts us into a quoted string (and we parse any pending
- // pattern symbols)
- if (ch == 0x0027 /*'\''*/) {
- inQuote = TRUE;
- if (count > 0)
- {
- int32_t startOffset = start;
- start = subParse(text, start, prevCh, count, FALSE, ambiguousYear);
- if ( start < 0 ) {
- pos.setErrorIndex(startOffset);
- pos.setIndex(oldStart);
- // {sfb} correct Date
- return 0;
- }
- count = 0;
- }
-
- if (interQuoteCount == 0)
- {
- // This indicates two consecutive quotes inside a quote,
- // for example, 'o''clock'. We need to parse this as
- // representing a single quote within the quote.
- int32_t startOffset = start;
- if (start >= text.size() || ch != text[start])
- {
- pos.setErrorIndex(startOffset);
- pos.setIndex(oldStart);
- // {sfb} correct Date
- return 0;
- }
- ++start;
- count = 1; // Make it look like we never left
- }
- }
-
- // if we're on a letter, collect copies of the same letter to determine
- // the whole parse symbol. when we hit a different character, parse the
- // input based on the resulting symbol
- else if ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
- || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))
- {
- // ch is a date-time pattern
- if (ch != prevCh && count > 0) // e.g., yyyyMMdd
- {
- int32_t startOffset = start;
- // This is the only case where we pass in 'true' for
- // obeyCount. That's because the next field directly
- // abuts this one, so we have to use the count to know when
- // to stop parsing. [LIU]
- start = subParse(text, start, prevCh, count, TRUE, ambiguousYear);
- if (start < 0) {
- pos.setErrorIndex(startOffset);
- pos.setIndex(oldStart);
- // {sfb} correct Date
- return 0;
- }
- prevCh = ch;
- count = 1;
- }
- else {
- if (ch != prevCh)
- prevCh = ch;
- count++;
- }
- }
-
- // if we're on a non-letter, parse based on any pending pattern symbols
- else if (count > 0)
- {
- // handle cases like: MM-dd-yy, HH:mm:ss, or yyyy MM dd,
- // where ch = '-', ':', or ' ', repectively.
- int32_t startOffset = start;
- start = subParse( text, start, prevCh, count, FALSE, ambiguousYear);
- if ( start < 0 ) {
- pos.setErrorIndex(startOffset);
- pos.setIndex(oldStart);
- // {sfb} correct Date?
- return 0;
- }
- if (start >= text.size() || ch != text[start]) {
- // handle cases like: 'MMMM dd' in pattern vs. "janx20"
- // in time text, where ' ' doesn't match with 'x'.
- pos.setErrorIndex(start);
- pos.setIndex(oldStart);
- // {sfb} correct Date?
- return 0;
- }
- start++;
- count = 0;
- prevCh = 0;
- }
-
- // otherwise, match characters exactly
- else
- {
- if (start >= text.size() || ch != text[start]) {
- // handle cases like: 'MMMM dd' in pattern vs.
- // "jan,,,20" in time text, where " " doesn't
- // match with ",,,".
- pos.setErrorIndex(start);
- pos.setIndex(oldStart);
- // {sfb} correct Date?
- return 0;
- }
- start++;
- }
-
- ++interQuoteCount;
- }
- }
-
- // if we still have a pending pattern symbol after we're done looping through
- // characters in the pattern string, parse the input based on the final pending
- // pattern symbol
- if (count > 0)
- {
- int32_t startOffset = start;
- start = subParse(text, start, prevCh, count, FALSE, ambiguousYear);
- if ( start < 0 ) {
- pos.setIndex(oldStart);
- pos.setErrorIndex(startOffset);
- // {sfb} correct Date?>
- return 0;
- }
- }
-
- // At this point the fields of Calendar have been set. Calendar
- // will fill in default values for missing fields when the time
- // is computed.
-
- pos.setIndex(start);
-
- // This part is a problem: When we call parsedDate.after, we compute the time.
- // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
- // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
- // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
- // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
- // on that day. It is therefore parsed out to fields as 3:30 am. Then we
- // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
- // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
- /*
- UDate parsedDate = calendar.getTime();
- if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
- calendar.add(Calendar.YEAR, 100);
- parsedDate = calendar.getTime();
- }
- */
- // Because of the above condition, save off the fields in case we need to readjust.
- // The procedure we use here is not particularly efficient, but there is no other
- // way to do this given the API restrictions present in Calendar. We minimize
- // inefficiency by only performing this computation when it might apply, that is,
- // 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().
- UErrorCode status = U_ZERO_ERROR;
- UDate parsedDate;
- if (ambiguousYear[0]) // 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
- // the fields[] array in Calendar, we clone the entire object. This will
- // stop working if Calendar.clone() is ever rewritten to call complete().
- Calendar *savedCalendar = fCalendar->clone();
- parsedDate = fCalendar->getTime(status);
- // {sfb} check internalGetDefaultCenturyStart
- if (parsedDate < internalGetDefaultCenturyStart())
- {
- // We can't use add here because that does a complete() first.
- savedCalendar->set(Calendar::YEAR, internalGetDefaultCenturyStartYear() + 100);
- parsedDate = savedCalendar->getTime(status);
- }
- delete savedCalendar;
- }
- else parsedDate = fCalendar->getTime(status);
-
- // 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)) {
- pos.setErrorIndex(start);
- pos.setIndex(oldStart);
- return 0;
- }
-
- return parsedDate;
- }
-
- UDate
- SimpleDateFormat::parse(const UnicodeString& text, UErrorCode& status) const
- {
- // redefined here because the other parse() function hides this function's
- // ounterpart on DateFormat
- return DateFormat::parse(text, status);
- }
- //----------------------------------------------------------------------
-
- int32_t SimpleDateFormat::matchString(const UnicodeString& text,
- int32_t start,
- Calendar::EDateFields field,
- const UnicodeString* data,
- int32_t dataCount) const
- {
- int32_t i = 0;
- int32_t count = dataCount;
-
- if (field == Calendar::DAY_OF_WEEK) i = 1;
-
- // There may be multiple strings in the data[] array which begin with
- // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
- // 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;
-
- // {sfb} kludge to support case-insensitive comparison
- UnicodeString lcaseText(text);
- lcaseText.toLower();
-
- for (; i < count; ++i)
- {
- int32_t length = data[i].size();
- // Always compare if we have no match yet; otherwise only compare
- // against potentially better matches (longer strings).
-
- UnicodeString lcase(data[i]);
- lcase.toLower();
-
- if (length > bestMatchLength && (lcaseText.compareBetween(start, start + length, lcase, 0, length)) == 0)
- {
- bestMatch = i;
- bestMatchLength = length;
- }
- }
- if (bestMatch >= 0)
- {
- fCalendar->set(field, bestMatch);
- return start + bestMatchLength;
- }
-
- return -start;
- }
-
- //----------------------------------------------------------------------
-
- void
- SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status)
- {
- parseAmbiguousDatesAsAfter(d, status);
- }
-
- /**
- * Parse the given text, at the given position, as a numeric value, using
- * this objects fNumberFormat. Return the corresponding long value in the
- * fill-in parameter 'value'. If the parse fails, this method leaves pos
- * unchanged and returns FALSE; otherwise it advances pos and
- * returns TRUE.
- */
- // {sfb} removed
- /*
- bool_t
- SimpleDateFormat::subParseLong(const UnicodeString& text, ParsePosition& pos, int32_t& value) const
- {
- Formattable parseResult;
- ParsePosition posSave = pos;
- fNumberFormat->parse(text, parseResult, pos);
- if (pos != posSave && parseResult.getType() == Formattable::kLong)
- {
- value = parseResult.getLong();
- return TRUE;
- }
- pos = posSave;
- return FALSE;
- }
- */
-
- /**
- * Private member function that converts the parsed date strings into
- * timeFields. Returns -start (for ParsePosition) if failed.
- * @param text the time text to be parsed.
- * @param start where to start parsing.
- * @param ch the pattern character for the date field text to be parsed.
- * @param count the count of a pattern character.
- * @return the new start position if matching succeeded; a negative number
- * indicating matching failure, otherwise.
- */
- int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count,
- bool_t obeyCount, bool_t ambiguousYear[]) const
- {
- UErrorCode status = U_ZERO_ERROR;
- Formattable number;
- int32_t value = 0;
- int32_t i;
- ParsePosition pos(0);
- int32_t patternCharIndex = -1;
-
- if ((patternCharIndex = DateFormatSymbols::fgPatternChars.indexOf(ch)) == -1)
- return -start;
-
- pos.setIndex(start);
-
- Calendar::EDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
-
- // If there are any spaces here, skip over them. If we hit the end
- // of the string, then fail.
- for (;;) {
- if (pos.getIndex() >= text.size())
- return -start;
- UChar c = text[pos.getIndex()];
- if (c != 0x0020 /*' '*/ && c != 0x0009 /*'\t'*/)
- break;
- pos.setIndex(pos.getIndex() + 1);
- }
-
- // 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 == kHourOfDay1Field /*HOUR_OF_DAY1_FIELD*/ ||
- patternCharIndex == kHour1Field /*HOUR1_FIELD*/ ||
- (patternCharIndex == kMonthField /*MONTH_FIELD*/ && count <= 2) ||
- patternCharIndex == kYearField /*YEAR*/)
- {
- int32_t parseStart = pos.getIndex(); // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
- // It would be good to unify this with the obeyCount logic below,
- // but that's going to be difficult.
- if (obeyCount)
- {
- if ((start+count) > text.size())
- return -start;
- UnicodeString temp;
- text.extractBetween(0, start + count, temp);
- fNumberFormat->parse(temp, number, pos);
- }
- else
- fNumberFormat->parse(text, number, pos);
- if (pos.getIndex() == parseStart)
- // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
- return -start;
- value = number.getLong();
- }
-
- switch (patternCharIndex) {
- case kEraField:
- return matchString(text, start, Calendar::ERA, fSymbols->fEras, fSymbols->fErasCount);
- case kYearField:
- // If there are 3 or more YEAR pattern characters, this indicates
- // that the year value is to be treated literally, without any
- // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
- // we made adjustments to place the 2-digit year in the proper
- // century, for parsed strings from "00" to "99". Any other string
- // is treated literally: "2250", "-1", "1", "002".
- if (count <= 2 && (pos.getIndex() - start) == 2
- && Unicode::isDigit(text.charAt(start))
- && Unicode::isDigit(text.charAt(start+1)))
- {
- // Assume for example that the defaultCenturyStart is 6/18/1903.
- // This means that two-digit years will be forced into the range
- // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
- // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
- // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
- // other fields specify a date before 6/18, or 1903 if they specify a
- // date afterwards. As a result, 03 is an ambiguous year. All other
- // two-digit years are unambiguous.
- int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
- ambiguousYear[0] = (value == ambiguousTwoDigitYear);
- value += (fDefaultCenturyStartYear/100)*100 +
- (value < ambiguousTwoDigitYear ? 100 : 0);
- }
- fCalendar->set(Calendar::YEAR, value);
- return pos.getIndex();
- case kMonthField:
- if (count <= 2) // 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.]
- fCalendar->set(Calendar::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, Calendar::MONTH,
- fSymbols->fMonths, fSymbols->fMonthsCount)) > 0)
- return newStart;
- else // count == 4 failed, now try count == 3
- return matchString(text, start, Calendar::MONTH,
- fSymbols->fShortMonths, fSymbols->fShortMonthsCount);
- }
- case kHourOfDay1Field:
- // [We computed 'value' above.]
- if (value == fCalendar->getMaximum(Calendar::HOUR_OF_DAY) + 1)
- value = 0;
- fCalendar->set(Calendar::HOUR_OF_DAY, value);
- return pos.getIndex();
- case kDayOfWeekField:
- {
- // Want to be able to parse both short and long forms.
- // Try count == 4 (DDDD) first:
- int32_t newStart = 0;
- if ((newStart = matchString(text, start, Calendar::DAY_OF_WEEK,
- fSymbols->fWeekdays, fSymbols->fWeekdaysCount)) > 0)
- return newStart;
- else // DDDD failed, now try DDD
- return matchString(text, start, Calendar::DAY_OF_WEEK,
- fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount);
- }
- case kAmPmField:
- return matchString(text, start, Calendar::AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount);
- case kHour1Field:
- // [We computed 'value' above.]
- if (value == fCalendar->getLeastMaximum(Calendar::HOUR)+1)
- value = 0;
- fCalendar->set(Calendar::HOUR, value);
- return pos.getIndex();
- case kTimezoneField:
- {
- // First try to parse generic forms such as GMT-07:00. Do this first
- // in case localized DateFormatZoneData contains the string "GMT"
- // for a zone; in that case, we don't want to match the first three
- // characters of GMT+/-HH:MM etc.
- int32_t sign = 0;
- int32_t offset;
-
- // For time zones that have no known names, look for strings
- // of the form:
- // GMT[+-]hours:minutes or
- // GMT[+-]hhmm or
- // GMT.
-
- // {sfb} kludge for case-insensitive compare
- UnicodeString lcaseText(text);
- lcaseText.toLower();
- UnicodeString lcaseGMT(fgGmt);
- lcaseGMT.toLower();
-
- if ((text.size() - start) > fgGmt.size() &&
- (lcaseText.compare(start, lcaseGMT.size(), lcaseGMT, 0, lcaseGMT.size())) == 0)
- {
- fCalendar->set(Calendar::DST_OFFSET, 0);
-
- pos.setIndex(start + fgGmt.size());
-
- if( text[pos.getIndex()] == 0x002B /*'+'*/ )
- sign = 1;
- else if( text[pos.getIndex()] == 0x002D /*'-'*/ )
- sign = -1;
- else {
- fCalendar->set(Calendar::ZONE_OFFSET, 0 );
- return pos.getIndex();
- }
-
- // Look for hours:minutes or hhmm.
- pos.setIndex(pos.getIndex() + 1);
- // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
- int32_t parseStart = pos.getIndex();
- Formattable tzNumber;
- fNumberFormat->parse(text, tzNumber, pos);
- if( pos.getIndex() == parseStart) {
- // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
- return -start;
- }
- if( text[pos.getIndex()] == 0x003A /*':'*/ ) {
- // This is the hours:minutes case
- offset = tzNumber.getLong() * 60;
- pos.setIndex(pos.getIndex() + 1);
- // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
- parseStart = pos.getIndex();
- fNumberFormat->parse(text, tzNumber, pos);
- if( pos.getIndex() == parseStart) {
- // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
- return -start;
- }
- offset += tzNumber.getLong();
- }
- else {
- // This is the hhmm case.
- offset = tzNumber.getLong();
- if( offset < 24 )
- offset *= 60;
- else
- offset = offset % 100 + offset / 100 * 60;
- }
-
- // Fall through for final processing below of 'offset' and 'sign'.
- }
- else {
- // 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.
- for (i = 0; i < fSymbols->fZoneStringsRowCount; i++)
- {
- // Checking long and short zones [1 & 2],
- // and long and short daylight [3 & 4].
- int32_t j = 1;
-
- // {sfb} kludge for case-insensitive compare
- UnicodeString s1(text);
- s1.toLower();
-
- for (; j <= 4; ++j)
- {
- UnicodeString s2(fSymbols->fZoneStrings[i][j]);
- s2.toLower();
-
- if ((s1.compare(start, s2.size(), s2, 0, s2.size())) == 0)
- break;
- }
- if (j <= 4)
- {
- TimeZone *tz = TimeZone::createTimeZone(fSymbols->fZoneStrings[i][0]);
- fCalendar->set(Calendar::ZONE_OFFSET, tz->getRawOffset());
- // Must call set() with something -- TODO -- Fix this to
- // use the correct DST SAVINGS for the zone.
- delete tz;
- fCalendar->set(Calendar::DST_OFFSET, j >= 3 ? U_MILLIS_PER_HOUR : 0);
- return (start + fSymbols->fZoneStrings[i][j].size());
- }
- }
-
- // As a last resort, look for numeric timezones of the form
- // [+-]hhmm as specified by RFC 822. This code is actually
- // a little more permissive than RFC 822. It will try to do
- // its best with numbers that aren't strictly 4 digits long.
- UErrorCode status = U_ZERO_ERROR;
- DecimalFormat *fmt = new DecimalFormat("+####;-####", status);
- if(U_FAILURE(status))
- return -start;
- fmt->setParseIntegerOnly(TRUE);
- // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
- int32_t parseStart = pos.getIndex();
- Formattable tzNumber;
- fmt->parse( text, tzNumber, pos );
- if( pos.getIndex() == parseStart) {
- // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
- return -start; // Wasn't actually a number.
- }
- offset = tzNumber.getLong();
- sign = 1;
- if( offset < 0 ) {
- sign = -1;
- offset = -offset;
- }
- if( offset < 24 )
- offset = offset * 60;
- else
- offset = offset % 100 + offset / 100 * 60;
-
- // Fall through for final processing below of 'offset' and 'sign'.
- }
-
- // Do the final processing for both of the above cases. We only
- // arrive here if the form GMT+/-... or an RFC 822 form was seen.
- if (sign != 0)
- {
- offset *= U_MILLIS_PER_MINUTE * sign;
-
- if (fCalendar->getTimeZone().useDaylightTime())
- {
- fCalendar->set(Calendar::DST_OFFSET, U_MILLIS_PER_HOUR);
- offset -= U_MILLIS_PER_HOUR;
- }
- fCalendar->set(Calendar::ZONE_OFFSET, offset);
-
- return pos.getIndex();
- }
-
- // All efforts to parse a zone failed.
- return -start;
- }
- default:
- // case 3: // 'd' - DATE
- // case 5: // 'H' - HOUR_OF_DAY:0-based. eg, 23:59 + 1 hour =>> 00:59
- // case 6: // 'm' - MINUTE
- // case 7: // 's' - SECOND
- // case 8: // 'S' - MILLISECOND
- // case 10: // 'D' - DAY_OF_YEAR
- // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH
- // case 12: // 'w' - WEEK_OF_YEAR
- // case 13: // 'W' - WEEK_OF_MONTH
- // case 16: // 'K' - HOUR: 0-based. eg, 11PM + 1 hour =>> 0 AM
-
- // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
- int32_t parseStart = pos.getIndex();
- // Handle "generic" fields
- if (obeyCount)
- {
- if ((start+count) > text.size())
- return -start;
- UnicodeString s;
- // {sfb} old code had extract, make sure it works
- text.extractBetween(0, start + count, s);
- fNumberFormat->parse(s, number, pos);
- }
- else
- fNumberFormat->parse(text, number, pos);
- if (pos.getIndex() != parseStart) {
- // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
- fCalendar->set(field, number.getLong());
- return pos.getIndex();
- }
- return -start;
- }
- }
-
- //----------------------------------------------------------------------
-
- void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern,
- UnicodeString& translatedPattern,
- const UnicodeString& from,
- const UnicodeString& to,
- UErrorCode& status)
- {
- // run through the pattern and convert any pattern symbols from the version
- // in "from" to the corresponding character ion "to". This code takes
- // quoted strings into account (it doesn't try to translate them), and it signals
- // an error if a particular "pattern character" doesn't appear in "from".
- // Depending on the values of "from" and "to" this can convert from generic
- // to localized patterns or localized to generic.
- if (U_FAILURE(status))
- return;
-
- translatedPattern.remove();
- bool_t inQuote = FALSE;
- for (UTextOffset i = 0; i < originalPattern.size(); ++i) {
- UChar c = originalPattern[i];
- if (inQuote) {
- if (c == 0x0027 /*'\''*/)
- inQuote = FALSE;
- }
- else {
- if (c == 0x0027 /*'\''*/)
- inQuote = TRUE;
- else if ((c >= 0x0061 /*'a'*/ && c <= 0x007A) /*'z'*/
- || (c >= 0x0041 /*'A'*/ && c <= 0x005A /*'Z'*/)) {
- UTextOffset ci = from.indexOf(c);
- if (ci == -1) {
- status = U_INVALID_FORMAT_ERROR;
- return;
- }
- c = to[ci];
- }
- }
- translatedPattern += c;
- }
- if (inQuote) {
- status = U_INVALID_FORMAT_ERROR;
- return;
- }
- }
-
- //----------------------------------------------------------------------
-
- UnicodeString&
- SimpleDateFormat::toPattern(UnicodeString& result) const
- {
- result = fPattern;
- return result;
- }
-
- //----------------------------------------------------------------------
-
- UnicodeString&
- SimpleDateFormat::toLocalizedPattern(UnicodeString& result,
- UErrorCode& status) const
- {
- translatePattern(fPattern, result, DateFormatSymbols::fgPatternChars, fSymbols->fLocalPatternChars, status);
- return result;
- }
-
- //----------------------------------------------------------------------
-
- void
- SimpleDateFormat::applyPattern(const UnicodeString& pattern)
- {
- fPattern = pattern;
- }
-
- //----------------------------------------------------------------------
-
- void
- SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern,
- UErrorCode &status)
- {
- translatePattern(pattern, fPattern, fSymbols->fLocalPatternChars, DateFormatSymbols::fgPatternChars, status);
- }
-
- //----------------------------------------------------------------------
-
- const DateFormatSymbols*
- SimpleDateFormat::getDateFormatSymbols() const
- {
- return fSymbols;
- }
-
- //----------------------------------------------------------------------
-
- void
- SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols* newFormatSymbols)
- {
- delete fSymbols;
- fSymbols = newFormatSymbols;
- }
-
- //----------------------------------------------------------------------
- void
- SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols)
- {
- delete fSymbols;
- fSymbols = new DateFormatSymbols(newFormatSymbols);
- }
-
-
- //----------------------------------------------------------------------
-
- // {sfb} removed
- /*int32_t
- SimpleDateFormat::getZoneIndex(const UnicodeString& ID) const
- {
- // this function searches a time zone list for a time zone with the specified
- // ID. It'll either return an apprpriate row number or -1 if the ID wasn't
- // found.
- int32_t index, col;
-
- for (col=0; col<=4 && col<fSymbols->fZoneStringsColCount; col+=2)
- {
- for (index = 0; index < fSymbols->fZoneStringsRowCount; index++)
- {
- if (fSymbols->fZoneStrings[index][col] == ID) return index;
- }
- }
-
- return - 1;
- }*/
-
- //----------------------------------------------------------------------
-
- UDate
- SimpleDateFormat::internalGetDefaultCenturyStart() const
- {
- // lazy-evaluate systemDefaultCenturyStart
- if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
- initializeSystemDefaultCentury();
-
- // use defaultCenturyStart unless it's the flag value;
- // then use systemDefaultCenturyStart
- return (fDefaultCenturyStart == fgSystemDefaultCentury) ?
- fgSystemDefaultCenturyStart : fDefaultCenturyStart;
- }
-
- int32_t
- SimpleDateFormat::internalGetDefaultCenturyStartYear() const
- {
- // lazy-evaluate systemDefaultCenturyStartYear
- if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
- initializeSystemDefaultCentury();
-
- // use defaultCenturyStart unless it's the flag value;
- // then use systemDefaultCenturyStartYear
- //return (fDefaultCenturyStart == fgSystemDefaultCentury) ?
- return (fDefaultCenturyStartYear == fgSystemDefaultCenturyYear) ?
- fgSystemDefaultCenturyStartYear : fDefaultCenturyStartYear;
- }
-
- void
- SimpleDateFormat::initializeSystemDefaultCentury()
- {
- // initialize systemDefaultCentury and systemDefaultCenturyYear based
- // on the current time. They'll be set to 80 years before
- // the current time.
- // No point in locking as it should be idempotent.
- if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
- {
- UErrorCode status = U_ZERO_ERROR;
- Calendar *calendar = Calendar::createInstance(status);
- if (calendar != NULL && U_SUCCESS(status))
- {
- calendar->setTime(Calendar::getNow(), status);
- calendar->add(Calendar::YEAR, -80, status);
- fgSystemDefaultCenturyStart = calendar->getTime(status);
- fgSystemDefaultCenturyStartYear = calendar->get(Calendar::YEAR, status);
- delete calendar;
- }
- // We have no recourse upon failure unless we want to propagate the failure
- // out.
- }
- }
-
- //eof
-