home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Programming / ICU / src / icu / source / i18n / smpdtfmt.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-19  |  58.2 KB  |  1,570 lines

  1. /*
  2. ********************************************************************************
  3. *                                                                              *
  4. * COPYRIGHT:                                                                   *
  5. *   (C) Copyright Taligent, Inc.,  1997                                        *
  6. *   (C) Copyright International Business Machines Corporation,  1997-1998      *
  7. *   Copyright (C) 1999 Alan Liu and others. All rights reserved.               *
  8. *   Licensed Material - Program-Property of IBM - All Rights Reserved.         *
  9. *   US Government Users Restricted Rights - Use, duplication, or disclosure    *
  10. *   restricted by GSA ADP Schedule Contract with IBM Corp.                     *
  11. *                                                                              *
  12. ********************************************************************************
  13. *
  14. * File SMPDTFMT.CPP
  15. *
  16. * Modification History:
  17. *
  18. *   Date        Name        Description
  19. *   02/19/97    aliu        Converted from java.
  20. *   03/31/97    aliu        Modified extensively to work with 50 locales.
  21. *   04/01/97    aliu        Added support for centuries.
  22. *   07/09/97    helena      Made ParsePosition into a class.
  23. *   07/21/98    stephen     Added initializeDefaultCentury.
  24. *                             Removed getZoneIndex (added in DateFormatSymbols)
  25. *                             Removed subParseLong
  26. *                             Removed chk
  27. *  02/22/99     stephen     Removed character literals for EBCDIC safety
  28. *   10/14/99    aliu        Updated 2-digit year parsing so that only "00" thru
  29. *                           "99" are recognized. {j28 4182066}
  30. ********************************************************************************
  31. */
  32.  
  33. #include "smpdtfmt.h"
  34. #include "dtfmtsym.h"
  35. #include "resbund.h"
  36. #include "msgfmt.h"
  37. #include "calendar.h"
  38. #include "gregocal.h"
  39. #include "timezone.h"
  40. #include "decimfmt.h"
  41. #include "dcfmtsym.h"
  42. #include "mutex.h"
  43. #include <float.h>
  44. #ifdef _DEBUG
  45. #include <iostream.h>
  46. #endif
  47.  
  48. // *****************************************************************************
  49. // class SimpleDateFormat
  50. // *****************************************************************************
  51.  
  52. // For time zones that have no names, use strings GMT+minutes and
  53. // GMT-minutes. For instance, in France the time zone is GMT+60.
  54. // Also accepted are GMT+H:MM or GMT-H:MM.
  55. const UnicodeString     SimpleDateFormat::fgGmt("GMT");
  56. const UnicodeString     SimpleDateFormat::fgGmtPlus("GMT+");
  57. const UnicodeString     SimpleDateFormat::fgGmtMinus("GMT-");
  58.  
  59. // This is a pattern-of-last-resort used when we can't load a usable pattern out
  60. // of a resource.
  61. const UnicodeString     SimpleDateFormat::fgDefaultPattern("yyMMdd hh:mm a");
  62.  
  63. /**
  64.  * These are the tags we expect to see in normal resource bundle files associated
  65.  * with a locale.
  66.  */
  67. const UnicodeString     SimpleDateFormat::fgErasTag("Eras");
  68. const UnicodeString     SimpleDateFormat::fgMonthNamesTag("MonthNames");
  69. const UnicodeString     SimpleDateFormat::fgMonthAbbreviationsTag("MonthAbbreviations");
  70. const UnicodeString     SimpleDateFormat::fgDayNamesTag("DayNames");
  71. const UnicodeString     SimpleDateFormat::fgDayAbbreviationsTag("DayAbbreviations");
  72. const UnicodeString     SimpleDateFormat::fgAmPmMarkersTag("AmPmMarkers");
  73. const UnicodeString     SimpleDateFormat::fgDateTimePatternsTag("DateTimePatterns");
  74.  
  75. /**
  76.  * These are the tags we expect to see in time zone data resource bundle files
  77.  * associated with a locale.
  78.  */
  79. const UnicodeString     SimpleDateFormat::fgZoneStringsTag("zoneStrings");
  80. const UnicodeString     SimpleDateFormat::fgLocalPatternCharsTag("localPatternChars");
  81.  
  82. char                    SimpleDateFormat::fgClassID = 0; // Value is irrelevant
  83.  
  84. /**
  85.  * This value of defaultCenturyStart indicates that the system default is to be
  86.  * used.
  87.  */
  88. const UDate              SimpleDateFormat::fgSystemDefaultCentury        = DBL_MIN;
  89. const int32_t            SimpleDateFormat::fgSystemDefaultCenturyYear    = -1;
  90.  
  91. UDate                    SimpleDateFormat::fgSystemDefaultCenturyStart        = SimpleDateFormat::fgSystemDefaultCentury;
  92. int32_t                 SimpleDateFormat::fgSystemDefaultCenturyStartYear    = SimpleDateFormat::fgSystemDefaultCenturyYear;
  93.  
  94. //----------------------------------------------------------------------
  95.  
  96. SimpleDateFormat::~SimpleDateFormat()
  97. {
  98.     delete fSymbols;
  99. }
  100.  
  101. //----------------------------------------------------------------------
  102.  
  103. SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
  104. :   fSymbols(NULL),
  105.     fDefaultCenturyStart(fgSystemDefaultCentury),
  106.     fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
  107. {
  108.     construct(kShort, (EStyle) (kShort + kDateOffset), Locale::getDefault(), status);
  109. }
  110.  
  111. //----------------------------------------------------------------------
  112.  
  113. SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
  114.                                    UErrorCode &status)
  115. :   fPattern(pattern),
  116.     fSymbols(new DateFormatSymbols(status)),
  117.     fDefaultCenturyStart(fgSystemDefaultCentury),
  118.     fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
  119. {
  120.     initialize(Locale::getDefault(), status);
  121. }
  122.  
  123. //----------------------------------------------------------------------
  124.  
  125. SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
  126.                                    const Locale& locale,
  127.                                    UErrorCode& status)
  128. :   fPattern(pattern),
  129.     fSymbols(new DateFormatSymbols(locale, status)),
  130.     fDefaultCenturyStart(fgSystemDefaultCentury),
  131.     fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
  132. {
  133.     initialize(locale, status);
  134. }
  135.  
  136. //----------------------------------------------------------------------
  137.  
  138. SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
  139.                                    DateFormatSymbols* symbolsToAdopt,
  140.                                    UErrorCode& status)
  141. :   fPattern(pattern),
  142.     fSymbols(symbolsToAdopt),
  143.     fDefaultCenturyStart(fgSystemDefaultCentury),
  144.     fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
  145. {
  146.     initialize(Locale::getDefault(), status);
  147. }
  148.  
  149. //----------------------------------------------------------------------
  150.  
  151. SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
  152.                                    const DateFormatSymbols& symbols,
  153.                                    UErrorCode& status)
  154. :   fPattern(pattern),
  155.     fSymbols(new DateFormatSymbols(symbols)),
  156.     fDefaultCenturyStart(fgSystemDefaultCentury),
  157.     fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
  158. {
  159.     initialize(Locale::getDefault(), status);
  160. }
  161.  
  162. //----------------------------------------------------------------------
  163.  
  164. // Not for public consumption; used by DateFormat
  165. SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
  166.                                    EStyle dateStyle,
  167.                                    const Locale& locale,
  168.                                    UErrorCode& status)
  169. :   fSymbols(NULL),
  170.     fDefaultCenturyStart(fgSystemDefaultCentury),
  171.     fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
  172. {
  173.     construct(timeStyle, dateStyle, locale, status);
  174. }
  175.  
  176. //----------------------------------------------------------------------
  177.  
  178. /**
  179.  * Not for public consumption; used by DateFormat.  This constructor
  180.  * never fails.  If the resource data is not available, it uses the
  181.  * the last resort symbols.
  182.  */
  183. SimpleDateFormat::SimpleDateFormat(const Locale& locale,
  184.                                    UErrorCode& status)
  185. :   fPattern(fgDefaultPattern),
  186.     fSymbols(NULL),
  187.     fDefaultCenturyStart(fgSystemDefaultCentury),
  188.     fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
  189. {
  190.     if (U_FAILURE(status)) return;
  191.     fSymbols = new DateFormatSymbols(locale, status);
  192.     if (U_FAILURE(status))
  193.     {
  194.         status = U_ZERO_ERROR;
  195.         delete fSymbols;
  196.         // This constructor doesn't fail; it uses last resort data
  197.         fSymbols = new DateFormatSymbols(status);
  198.     }
  199.     initialize(locale, status);
  200. }
  201.  
  202. //----------------------------------------------------------------------
  203.  
  204. SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
  205. :   DateFormat(other),
  206.     fSymbols(NULL),
  207.     fDefaultCenturyStart(fgSystemDefaultCentury),
  208.     fDefaultCenturyStartYear(fgSystemDefaultCenturyYear)
  209. {
  210.     *this = other;
  211. }
  212.  
  213. //----------------------------------------------------------------------
  214.  
  215. SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
  216. {
  217.     DateFormat::operator=(other);
  218.  
  219.     delete fSymbols;
  220.     fSymbols = NULL;
  221.  
  222.     if (other.fSymbols)
  223.         fSymbols = new DateFormatSymbols(*other.fSymbols);
  224.  
  225.     fDefaultCenturyStart         = other.fDefaultCenturyStart;
  226.     fDefaultCenturyStartYear     = other.fDefaultCenturyStartYear;
  227.  
  228.     fPattern = other.fPattern;
  229.  
  230.     return *this;
  231. }
  232.  
  233. //----------------------------------------------------------------------
  234.  
  235. Format*
  236. SimpleDateFormat::clone() const
  237. {
  238.     return new SimpleDateFormat(*this);
  239. }
  240.  
  241. //----------------------------------------------------------------------
  242.  
  243. bool_t
  244. SimpleDateFormat::operator==(const Format& other) const
  245. {
  246.     if (DateFormat::operator==(other) &&
  247.         other.getDynamicClassID() == getStaticClassID())
  248.     {
  249.         SimpleDateFormat* that = (SimpleDateFormat*)&other;
  250.         return     (fPattern             == that->fPattern &&
  251.                 fSymbols             != NULL && // Check for pathological object
  252.                 that->fSymbols         != NULL && // Check for pathological object
  253.                 *fSymbols             == *that->fSymbols &&
  254.                 fDefaultCenturyStart == that->fDefaultCenturyStart);
  255.     }
  256.     return FALSE;
  257. }
  258.  
  259. //----------------------------------------------------------------------
  260.  
  261. void SimpleDateFormat::construct(EStyle timeStyle,
  262.                                  EStyle dateStyle,
  263.                                  const Locale& locale,
  264.                                  UErrorCode& status)
  265. {
  266.     // called by several constructors to load pattern data from the resources
  267.  
  268.     if (U_FAILURE(status)) return;
  269.  
  270.     // load up the DateTimePatters resource from the appropriate locale (throw
  271.     // an error if for some weird reason the resource is malformed)
  272.     ResourceBundle resources(Locale::getDataDirectory(), locale, status);
  273.     int32_t dtCount;
  274.     const UnicodeString *dateTimePatterns = resources.getStringArray(fgDateTimePatternsTag, dtCount, status);
  275.     if (U_FAILURE(status)) return;
  276.     if (dtCount <= kDateTime)
  277.     {
  278.         status = U_INVALID_FORMAT_ERROR;
  279.         return;
  280.     }
  281.  
  282.     // create a symbols object from the locale
  283.     fSymbols = new DateFormatSymbols(locale, status);
  284.  
  285.     UnicodeString str;
  286.  
  287.     // Move dateStyle from the range [0, 3] to [4, 7] if necessary
  288.     //if (dateStyle >= 0 && dateStyle < DATE_OFFSET) dateStyle = (EStyle)(dateStyle + DATE_OFFSET);
  289.  
  290.     // if the pattern should include both date and time information, use the date/time
  291.     // pattern string as a guide to tell use how to glue together the appropriate date
  292.     // and time pattern strings.  The actual gluing-together is handled by a convenience
  293.     // method on MessageFormat.
  294.     if ((timeStyle != kNone) &&
  295.         (dateStyle != kNone))
  296.     {
  297.         //  Object[] dateTimeArgs = {
  298.         //     dateTimePatterns[timeStyle], dateTimePatterns[dateStyle]
  299.         //  };
  300.         //  pattern = MessageFormat.format(dateTimePatterns[8], dateTimeArgs);
  301.  
  302.         Formattable *timeDateArray = new Formattable[2];
  303.         timeDateArray[0].setString(dateTimePatterns[timeStyle]);
  304.         timeDateArray[1].setString(dateTimePatterns[dateStyle]);
  305.  
  306.         MessageFormat::format(dateTimePatterns[kDateTime], timeDateArray, 2, fPattern, status);
  307.         delete [] timeDateArray;
  308.     }
  309.     
  310.     // if the pattern includes just time data or just date date, load the appropriate
  311.     // pattern string from the resources
  312.     else if (timeStyle != kNone) fPattern = dateTimePatterns[timeStyle];
  313.     else if (dateStyle != kNone) fPattern = dateTimePatterns[dateStyle];
  314.     
  315.     // and if it includes _neither_, that's an error
  316.     else status = U_INVALID_FORMAT_ERROR;
  317.  
  318.     // finally, finish initializing by creating a Calendar and a NumberFormat
  319.     initialize(locale, status);
  320. }
  321.  
  322. //----------------------------------------------------------------------
  323.  
  324. void
  325. SimpleDateFormat::initialize(const Locale& locale,
  326.                              UErrorCode& status)
  327. {
  328.     if (U_FAILURE(status)) return;
  329.  
  330.     // {sfb} should this be here?
  331.     if (fSymbols->fZoneStringsColCount < 1)
  332.     {
  333.         status = U_INVALID_FORMAT_ERROR; // Check for bogus locale data
  334.         return;
  335.     }
  336.  
  337.     // We don't need to check that the row count is >= 1, since all 2d arrays have at
  338.     // least one row
  339.     fCalendar = Calendar::createInstance(TimeZone::createDefault(), locale, status);
  340.     fNumberFormat = NumberFormat::createInstance(locale, status);
  341.     if (fNumberFormat != NULL && U_SUCCESS(status))
  342.     {
  343.         // no matter what the locale's default number format looked like, we want
  344.         // to modify it so that it doesn't use thousands separators, doesn't always
  345.         // show the decimal point, and recognizes integers only when parsing
  346.  
  347.         fNumberFormat->setGroupingUsed(FALSE);
  348.         if (fNumberFormat->getDynamicClassID() == DecimalFormat::getStaticClassID())
  349.             ((DecimalFormat*)fNumberFormat)->setDecimalSeparatorAlwaysShown(FALSE);
  350.         fNumberFormat->setParseIntegerOnly(TRUE);
  351.         fNumberFormat->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
  352.  
  353.         initializeDefaultCentury();
  354.     }
  355.     else if (U_SUCCESS(status))
  356.     {
  357.         status = U_MISSING_RESOURCE_ERROR;
  358.     }
  359. }
  360.  
  361. /* Initialize the fields we use to disambiguate ambiguous years. Separate
  362.  * so we can call it from readObject().
  363.  */
  364. void SimpleDateFormat::initializeDefaultCentury() 
  365. {
  366.     fDefaultCenturyStart        = internalGetDefaultCenturyStart();
  367.     fDefaultCenturyStartYear    = internalGetDefaultCenturyStartYear();
  368.  
  369.     UErrorCode status = U_ZERO_ERROR;
  370.     fCalendar->setTime(fDefaultCenturyStart, status);
  371.     // {sfb} throw away error
  372. }
  373.  
  374. /* Define one-century window into which to disambiguate dates using
  375.  * two-digit years. Make public in JDK 1.2.
  376.  */
  377. void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status) 
  378. {
  379.     if(U_FAILURE(status))
  380.         return;
  381.         
  382.     fCalendar->setTime(startDate, status);
  383.     if(U_SUCCESS(status)) {
  384.         fDefaultCenturyStart = startDate;
  385.         fDefaultCenturyStartYear = fCalendar->get(Calendar::YEAR, status);
  386.     }
  387. }
  388.     
  389. //----------------------------------------------------------------------
  390.  
  391. UnicodeString&
  392. SimpleDateFormat::format(UDate date, UnicodeString& toAppendTo, FieldPosition& pos) const
  393. {
  394.     UErrorCode status = U_ZERO_ERROR;
  395.     pos.setBeginIndex(0);
  396.     pos.setEndIndex(0);
  397.  
  398.     // load up our Calendar with the date/time we're formatting (the subroutines of this
  399.     // function pick it up from there, since they need it anyway to split the value
  400.     // into fields)
  401.     fCalendar->setTime(date, status);
  402.  
  403.     bool_t inQuote = FALSE;
  404.     UChar prevCh = 0;
  405.     int32_t count = 0;
  406.     UnicodeString str;
  407.     
  408.     // loop through the pattern string character by character
  409.     for (int32_t i = 0; i < fPattern.size() && U_SUCCESS(status); ++i) {
  410.         UChar ch = fPattern[i];
  411.         
  412.         // Use subFormat() to format a repeated pattern character
  413.         // when a different pattern or non-pattern character is seen
  414.         if (ch != prevCh && count > 0) {
  415.             toAppendTo += subFormat(str, prevCh, count, toAppendTo.size(), pos, status);
  416.             count = 0;
  417.         }
  418.         if (ch == 0x0027 /*'\''*/) {
  419.             // Consecutive single quotes are a single quote literal,
  420.             // either outside of quotes or between quotes
  421.             if ((i+1) < fPattern.size() && fPattern[i+1] == 0x0027 /*'\''*/) {
  422.                 toAppendTo += 0x0027 /*'\''*/;
  423.                 ++i;
  424.             } else {
  425.                 inQuote = ! inQuote;
  426.             }
  427.         } 
  428.         else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) 
  429.                     || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
  430.             // ch is a date-time pattern character to be interpreted
  431.             // by subFormat(); count the number of times it is repeated
  432.             prevCh = ch;
  433.             ++count;
  434.         }
  435.         else {
  436.             // Append quoted characters and unquoted non-pattern characters
  437.             toAppendTo += ch;
  438.         }
  439.     }
  440.  
  441.     // Format the last item in the pattern, if any
  442.     if (count > 0) {
  443.         toAppendTo += subFormat(str, prevCh, count, toAppendTo.size(), pos, status);
  444.     }
  445.  
  446.     // and if something failed (e.g., an invalid format character), reset our FieldPosition
  447.     // to (0, 0) to show that
  448.     // {sfb} look at this later- are these being set correctly?
  449.     if (U_FAILURE(status)) {
  450.         pos.setBeginIndex(0);
  451.         pos.setEndIndex(0);
  452.     }
  453.     
  454.     return toAppendTo;
  455. }
  456.  
  457. UnicodeString&
  458. SimpleDateFormat::format(const Formattable& obj, 
  459.                          UnicodeString& toAppendTo, 
  460.                          FieldPosition& pos,
  461.                          UErrorCode& status) const
  462. {
  463.     // this is just here to get around the hiding problem
  464.     // (the previous format() override would hide the version of
  465.     // format() on DateFormat that this function correspond to, so we
  466.     // have to redefine it here)
  467.     return DateFormat::format(obj, toAppendTo, pos, status);
  468. }
  469.  
  470. //----------------------------------------------------------------------
  471.  
  472. // Map index into pattern character string to Calendar field number.
  473. const Calendar::EDateFields
  474. SimpleDateFormat::fgPatternIndexToCalendarField[] =
  475. {
  476.     Calendar::ERA, Calendar::YEAR, Calendar::MONTH, Calendar::DATE, 
  477.     Calendar::HOUR_OF_DAY, Calendar::HOUR_OF_DAY, Calendar::MINUTE, 
  478.     Calendar::SECOND, Calendar::MILLISECOND, Calendar::DAY_OF_WEEK,
  479.     Calendar::DAY_OF_YEAR, Calendar::DAY_OF_WEEK_IN_MONTH, 
  480.     Calendar::WEEK_OF_YEAR, Calendar::WEEK_OF_MONTH, 
  481.     Calendar::AM_PM, Calendar::HOUR, Calendar::HOUR, Calendar::ZONE_OFFSET
  482. };
  483.  
  484. // Map index into pattern character string to DateFormat field number
  485. const DateFormat::EField
  486. SimpleDateFormat::fgPatternIndexToDateFormatField[] = {
  487.     DateFormat::kEraField, DateFormat::kYearField, DateFormat::kMonthField,
  488.     DateFormat::kDateField, DateFormat::kHourOfDay1Field,
  489.     DateFormat::kHourOfDay0Field, DateFormat::kMinuteField,
  490.     DateFormat::kSecondField, DateFormat::kMillisecondField,
  491.     DateFormat::kDayOfWeekField, DateFormat::kDayOfYearField,
  492.     DateFormat::kDayOfWeekInMonthField, DateFormat::kWeekOfYearField,
  493.     DateFormat::kWeekOfMonthField, DateFormat::kAmPmField,
  494.     DateFormat::kHour1Field, DateFormat::kHour0Field,
  495.     DateFormat::kTimezoneField,
  496. };
  497.  
  498.  
  499. //----------------------------------------------------------------------
  500.  
  501. UnicodeString&
  502. SimpleDateFormat::subFormat(UnicodeString& result,
  503.                             UChar ch,
  504.                             int32_t count,
  505.                             int32_t beginOffset,
  506.                             FieldPosition& pos,
  507.                             UErrorCode& status) const
  508. {
  509.     // this function gets called by format() to produce the appropriate substitution
  510.     // text for an individual pattern symbol (e.g., "HH" or "yyyy")
  511.  
  512.     EField patternCharIndex = (EField) -1;
  513.     int32_t maxIntCount = 10;
  514.     UnicodeString str; // Scratch
  515.     result.remove();
  516.  
  517.     // if the pattern character is unrecognized, signal an error and dump out
  518.     if ((patternCharIndex = (EField)DateFormatSymbols::fgPatternChars.indexOf(ch)) == (EField)-1)
  519.     {
  520.         status = U_INVALID_FORMAT_ERROR;
  521.         return result;
  522.     }
  523.  
  524.     Calendar::EDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
  525.     int32_t value = fCalendar->get(field, status);
  526.     if (U_FAILURE(status)) return result;
  527.  
  528.     switch (patternCharIndex) {
  529.     
  530.     // for any "G" symbol, write out the appropriate era string
  531.     case kEraField:
  532.         result = fSymbols->fEras[value];
  533.         break;
  534.  
  535.     // for "yyyy", write out the whole year; for "yy", write out the last 2 digits
  536.     case kYearField:
  537.         if (count >= 4) 
  538.             zeroPaddingNumber(result, value, 4, maxIntCount);
  539.         else 
  540.             zeroPaddingNumber(result, value, 2, 2);
  541.         break;
  542.  
  543.     // for "MMMM", write out the whole month name, for "MMM", write out the month
  544.     // abbreviation, for "M" or "MM", write out the month as a number with the
  545.     // appropriate number of digits
  546.     case kMonthField:
  547.         if (count >= 4) 
  548.             result = fSymbols->fMonths[value];
  549.         else if (count == 3) 
  550.             result = fSymbols->fShortMonths[value];
  551.         else 
  552.             zeroPaddingNumber(result, value + 1, count, maxIntCount);
  553.         break;
  554.  
  555.     // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
  556.     case kHourOfDay1Field:
  557.         if (value == 0) 
  558.             zeroPaddingNumber(result, fCalendar->getMaximum(Calendar::HOUR_OF_DAY) + 1, count, maxIntCount);
  559.         else 
  560.             zeroPaddingNumber(result, value, count, maxIntCount);
  561.         break;
  562.  
  563.     // for "SS" and "S", we want to truncate digits so that you still see the MOST
  564.     // significant digits rather than the LEAST (as is the case with the year)
  565.     case kMillisecondField:
  566.         if (count > 3) 
  567.             count = 3;
  568.         else if (count == 2) 
  569.             value = value / 10;
  570.         else if (count == 1) 
  571.             value = value / 100;
  572.         zeroPaddingNumber(result, value, count, maxIntCount);
  573.         break;
  574.  
  575.     // for "EEEE", write out the day-of-the-week name; otherwise, use the abbreviation
  576.     case kDayOfWeekField:
  577.         if (count >= 4) 
  578.             result = fSymbols->fWeekdays[value];
  579.         else 
  580.             result = fSymbols->fShortWeekdays[value];
  581.         break;
  582.  
  583.     // for and "a" symbol, write out the whole AM/PM string
  584.     case kAmPmField:
  585.         result = fSymbols->fAmPms[value];
  586.         break;
  587.  
  588.     // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
  589.     // as "12"
  590.     case kHour1Field:
  591.         if (value == 0) 
  592.             zeroPaddingNumber(result, fCalendar->getLeastMaximum(Calendar::HOUR) + 1, count, maxIntCount);
  593.         else 
  594.             zeroPaddingNumber(result, value, count, maxIntCount);
  595.         break;
  596.  
  597.     // for the "z" symbols, we have to check our time zone data first.  If we have a
  598.     // localized name for the time zone, then "zzzz" is the whole name and anything
  599.     // shorter is the abbreviation (we also have to check for daylight savings time
  600.     // since the name will be different).  If we don't have a localized time zone name,
  601.     // then the time zone shows up as "GMT+hh:mm" or "GMT-hh:mm" (where "hh:mm" is the
  602.     // offset from GMT) regardless of how many z's were in the pattern symbol
  603.     case kTimezoneField: {
  604.         int32_t zoneIndex = fSymbols->getZoneIndex(fCalendar->getTimeZone().getID(str));
  605.         if (zoneIndex == -1) {
  606.             UnicodeString zoneString;
  607.  
  608.             value = fCalendar->get(Calendar::ZONE_OFFSET, status) +
  609.                     fCalendar->get(Calendar::DST_OFFSET, status);
  610.  
  611.             if (value < 0) {
  612.                 zoneString += fgGmtMinus;
  613.                 value = -value; // suppress the '-' sign for text display.
  614.             }
  615.             else
  616.                 zoneString += fgGmtPlus;
  617.             
  618.             zoneString += zeroPaddingNumber(str, (int32_t)(value/U_MILLIS_PER_HOUR), 2, 2);
  619.             zoneString += 0x003A /*':'*/;
  620.             zoneString += zeroPaddingNumber(str, (int32_t)((value%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE), 2, 2);
  621.             
  622.             result = zoneString;
  623.         }
  624.         else if (fCalendar->get(Calendar::DST_OFFSET, status) != 0) {
  625.             if (count >= 4) 
  626.                 result = fSymbols->fZoneStrings[zoneIndex][3];
  627.             else 
  628.                 result = fSymbols->fZoneStrings[zoneIndex][4];
  629.         }
  630.         else {
  631.             if (count >= 4) 
  632.                 result = fSymbols->fZoneStrings[zoneIndex][1];
  633.             else 
  634.                 result = fSymbols->fZoneStrings[zoneIndex][2];
  635.         }
  636.         }
  637.         break;
  638.     
  639.     // all of the other pattern symbols can be formatted as simple numbers with
  640.     // appropriate zero padding
  641.     default:
  642.     // case kDateField:
  643.     // case kHourOfDay0Field:
  644.     // case kMinuteField:
  645.     // case kSecondField:
  646.     // case kDayOfYearField:
  647.     // case kDayOfWeekInMonthField:
  648.     // case kWeekOfYearField:
  649.     // case kWeekOfMonthField:
  650.     // case kHour0Field:
  651.         zeroPaddingNumber(result, value, count, maxIntCount);
  652.         break;
  653.     }
  654.  
  655.     // if the field we're formatting is the one the FieldPosition says it's interested
  656.     // in, fill in the FieldPosition with this field's positions
  657.     if (pos.getField() == fgPatternIndexToDateFormatField[patternCharIndex]) {
  658.         if (pos.getBeginIndex() == 0 && pos.getEndIndex() == 0) {
  659.             pos.setBeginIndex(beginOffset);
  660.             pos.setEndIndex(beginOffset + result.size());
  661.         }
  662.     }
  663.     
  664.     return result;
  665. }
  666.  
  667. //----------------------------------------------------------------------
  668.  
  669. UnicodeString&
  670. SimpleDateFormat::zeroPaddingNumber(UnicodeString& result, int32_t value, int32_t minDigits, int32_t maxDigits) const
  671. {
  672.     result.remove();
  673.     fNumberFormat->setMinimumIntegerDigits(minDigits);
  674.     fNumberFormat->setMaximumIntegerDigits(maxDigits);
  675.     return fNumberFormat->format(value, result);
  676. }
  677.  
  678. //----------------------------------------------------------------------
  679.  
  680. // {sfb} removed
  681. /*
  682. // this function will dump output to the console on a debug build when there's a parse error
  683. #ifdef _DEBUG
  684. void chk(ParsePosition& val, UChar ch, ParsePosition& start, int32_t count)
  685. {
  686.     if (val.getIndex() < 0)
  687.     {
  688.         cout << "[Parse failure on '" << (char)ch << "' x " << dec << count << " @ " << start.getIndex() << ']';
  689.     }
  690. }
  691. #else
  692. inline void chk(ParsePosition& val, UChar ch, ParsePosition& start, int32_t count)
  693. {
  694. }
  695. #endif
  696.  
  697. inline Date
  698. parseFailureResult(ParsePosition& pos, ParsePosition& oldStart, ParsePosition& failurePos)
  699. {
  700.     // Note: The C++ version currently supports the notion of returning zero
  701.     // with a non-zero parse position, but only if this format is lenient.
  702.     // The returned position in this case is the first un-parseable character.
  703.     // This is useful, but is not present in the Java version, and causes a
  704.     // DateFormat test to fail.
  705.     
  706.     // For now, I am removing this function.  It can be restored later.
  707.  
  708.     // if (!isLenient()) pos = oldStart;
  709.     // else { pos = failurePos.getIndex(); if (pos.getIndex() < 0) pos = -pos.getIndex(); };
  710.     pos = oldStart;
  711.     return 0;
  712. }
  713. */
  714.  
  715. UDate
  716. SimpleDateFormat::parse(const UnicodeString& text, ParsePosition& pos) const
  717. {
  718.     int32_t start = pos.getIndex();
  719.     int32_t oldStart = start;
  720.     bool_t ambiguousYear[] = { FALSE };
  721.  
  722.     fCalendar->clear();
  723.  
  724.     bool_t inQuote = FALSE;
  725.     UChar prevCh = 0;
  726.     int32_t count = 0;
  727.     int32_t interQuoteCount = 1; // Number of chars between quotes
  728.  
  729.     // loop through the pattern string character by character, using it to control how
  730.     // we match characters in the input
  731.     for (int32_t i = 0; i < fPattern.size();++i) {
  732.         UChar ch = fPattern[i];
  733.         
  734.         // if we're inside a quoted string, match characters exactly until we hit
  735.         // another single quote (two single quotes in a row match one single quote
  736.         // in the input)
  737.         if (inQuote)
  738.         {
  739.             if (ch == 0x0027 /*'\''*/)
  740.             {
  741.                 // ends with 2nd single quote
  742.                 inQuote = FALSE;
  743.                 // two consecutive quotes outside a quote means we have
  744.                 // a quote literal we need to match.
  745.                 if (count == 0)
  746.                 {
  747.                     if(start > text.size() || ch != text[start])
  748.                         {
  749.                             pos.setIndex(oldStart);
  750.                             pos.setErrorIndex(start);
  751.                             // {sfb} what is the correct Date for failure?
  752.                             return 0;
  753.                         }
  754.                         ++start;
  755.                 }
  756.                 count = 0;
  757.                 interQuoteCount = 0;
  758.             }
  759.             else
  760.                 {
  761.                     // pattern uses text following from 1st single quote.
  762.                     if (start >= text.size() || ch != text[start]) {
  763.                         // Check for cases like: 'at' in pattern vs "xt"
  764.                         // in time text, where 'a' doesn't match with 'x'.
  765.                         // If fail to match, return null.
  766.                         pos.setIndex(oldStart); // left unchanged
  767.                         pos.setErrorIndex(start);
  768.                         // {sfb} what is correct Date for failure?
  769.                         return 0;
  770.                     }
  771.                     ++count;
  772.                     ++start;
  773.                 }
  774.         }
  775.  
  776.         // if we're not inside a quoted string...
  777.         else {
  778.             
  779.             // ...a quote mark puts us into a quoted string (and we parse any pending
  780.             // pattern symbols)
  781.             if (ch == 0x0027 /*'\''*/) {
  782.                 inQuote = TRUE;
  783.                 if (count > 0) 
  784.                 {
  785.                     int32_t startOffset = start;
  786.                     start = subParse(text, start, prevCh, count, FALSE, ambiguousYear);
  787.                     if ( start < 0 ) {
  788.                         pos.setErrorIndex(startOffset);
  789.                         pos.setIndex(oldStart);
  790.                         // {sfb} correct Date
  791.                         return 0;
  792.                     }
  793.                     count = 0;
  794.                 }
  795.  
  796.                     if (interQuoteCount == 0)
  797.                     {
  798.                         // This indicates two consecutive quotes inside a quote,
  799.                         // for example, 'o''clock'.  We need to parse this as
  800.                         // representing a single quote within the quote.
  801.                         int32_t startOffset = start;
  802.                         if (start >= text.size() ||  ch != text[start])
  803.                         {
  804.                             pos.setErrorIndex(startOffset);
  805.                             pos.setIndex(oldStart);
  806.                             // {sfb} correct Date
  807.                             return 0;
  808.                         }
  809.                         ++start;
  810.                         count = 1; // Make it look like we never left
  811.                     }
  812.             }
  813.             
  814.             // if we're on a letter, collect copies of the same letter to determine
  815.             // the whole parse symbol.  when we hit a different character, parse the
  816.             // input based on the resulting symbol
  817.         else if ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) 
  818.              || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))
  819.           {
  820.                 // ch is a date-time pattern
  821.                 if (ch != prevCh && count > 0) // e.g., yyyyMMdd
  822.                 {
  823.                     int32_t startOffset = start;
  824.                     // This is the only case where we pass in 'true' for
  825.                     // obeyCount.  That's because the next field directly
  826.                     // abuts this one, so we have to use the count to know when
  827.                     // to stop parsing. [LIU]
  828.                     start = subParse(text, start, prevCh, count, TRUE, ambiguousYear);
  829.                     if (start < 0) {
  830.                         pos.setErrorIndex(startOffset);
  831.                         pos.setIndex(oldStart);
  832.                         // {sfb} correct Date
  833.                         return 0;
  834.                     }
  835.                     prevCh = ch;
  836.                     count = 1;
  837.                 }
  838.                 else {
  839.                     if (ch != prevCh) 
  840.                         prevCh = ch;
  841.                     count++;
  842.                 }
  843.             }
  844.  
  845.             // if we're on a non-letter, parse based on any pending pattern symbols
  846.             else if (count > 0) 
  847.             {
  848.                 // handle cases like: MM-dd-yy, HH:mm:ss, or yyyy MM dd,
  849.                 // where ch = '-', ':', or ' ', repectively.
  850.                 int32_t startOffset = start;
  851.                 start = subParse( text, start, prevCh, count, FALSE, ambiguousYear);
  852.                 if ( start < 0 ) {
  853.                     pos.setErrorIndex(startOffset);
  854.                     pos.setIndex(oldStart);
  855.                     // {sfb} correct Date?
  856.                     return 0;
  857.                 }
  858.                 if (start >= text.size() || ch != text[start]) {
  859.                     // handle cases like: 'MMMM dd' in pattern vs. "janx20"
  860.                     // in time text, where ' ' doesn't match with 'x'.
  861.                     pos.setErrorIndex(start);
  862.                     pos.setIndex(oldStart);
  863.                     // {sfb} correct Date?
  864.                     return 0;
  865.                 }
  866.                 start++;
  867.                 count = 0;
  868.                 prevCh = 0;
  869.             }
  870.  
  871.             // otherwise, match characters exactly
  872.             else 
  873.             {
  874.                 if (start >= text.size() || ch != text[start]) {
  875.                     // handle cases like: 'MMMM   dd' in pattern vs.
  876.                     // "jan,,,20" in time text, where "   " doesn't
  877.                     // match with ",,,".
  878.                     pos.setErrorIndex(start);
  879.                     pos.setIndex(oldStart);
  880.                     // {sfb} correct Date?
  881.                     return 0;
  882.                 }
  883.                 start++;
  884.             }
  885.  
  886.             ++interQuoteCount;
  887.         }
  888.     }
  889.  
  890.     // if we still have a pending pattern symbol after we're done looping through
  891.     // characters in the pattern string, parse the input based on the final pending
  892.     // pattern symbol
  893.     if (count > 0) 
  894.     {
  895.         int32_t startOffset = start;
  896.         start = subParse(text, start, prevCh, count, FALSE, ambiguousYear);
  897.         if ( start < 0 ) {
  898.             pos.setIndex(oldStart);
  899.             pos.setErrorIndex(startOffset);
  900.             // {sfb} correct Date?>
  901.             return 0;
  902.         }
  903.     }
  904.  
  905.     // At this point the fields of Calendar have been set.  Calendar
  906.     // will fill in default values for missing fields when the time
  907.     // is computed.
  908.  
  909.     pos.setIndex(start);
  910.  
  911.     // This part is a problem:  When we call parsedDate.after, we compute the time.
  912.     // Take the date April 3 2004 at 2:30 am.  When this is first set up, the year
  913.     // will be wrong if we're parsing a 2-digit year pattern.  It will be 1904.
  914.     // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day.  2:30 am
  915.     // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
  916.     // on that day.  It is therefore parsed out to fields as 3:30 am.  Then we
  917.     // add 100 years, and get April 3 2004 at 3:30 am.  Note that April 3 2004 is
  918.     // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
  919.     /*
  920.         UDate parsedDate = calendar.getTime();
  921.         if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
  922.             calendar.add(Calendar.YEAR, 100);
  923.             parsedDate = calendar.getTime();
  924.         }
  925.     */
  926.     // Because of the above condition, save off the fields in case we need to readjust.
  927.     // The procedure we use here is not particularly efficient, but there is no other
  928.     // way to do this given the API restrictions present in Calendar.  We minimize
  929.     // inefficiency by only performing this computation when it might apply, that is,
  930.     // when the two-digit year is equal to the start year, and thus might fall at the
  931.     // front or the back of the default century.  This only works because we adjust
  932.     // the year correctly to start with in other cases -- see subParse().
  933.     UErrorCode status = U_ZERO_ERROR;
  934.     UDate parsedDate;
  935.     if (ambiguousYear[0]) // If this is true then the two-digit year == the default start year
  936.     {
  937.         // We need a copy of the fields, and we need to avoid triggering a call to
  938.         // complete(), which will recalculate the fields.  Since we can't access
  939.         // the fields[] array in Calendar, we clone the entire object.  This will
  940.         // stop working if Calendar.clone() is ever rewritten to call complete().
  941.         Calendar *savedCalendar = fCalendar->clone();
  942.         parsedDate = fCalendar->getTime(status);
  943.         // {sfb} check internalGetDefaultCenturyStart
  944.         if (parsedDate < internalGetDefaultCenturyStart())
  945.         {
  946.             // We can't use add here because that does a complete() first.
  947.             savedCalendar->set(Calendar::YEAR, internalGetDefaultCenturyStartYear() + 100);
  948.             parsedDate = savedCalendar->getTime(status);
  949.         }
  950.         delete savedCalendar;
  951.     }
  952.     else parsedDate = fCalendar->getTime(status);
  953.  
  954.     // If any Calendar calls failed, we pretend that we
  955.     // couldn't parse the string, when in reality this isn't quite accurate--
  956.     // we did parse it; the Calendar calls just failed.
  957.     if (U_FAILURE(status)) { 
  958.         pos.setErrorIndex(start);
  959.         pos.setIndex(oldStart); 
  960.         return 0; 
  961.     }
  962.  
  963.     return parsedDate;
  964. }
  965.  
  966. UDate
  967. SimpleDateFormat::parse(const UnicodeString& text, UErrorCode& status) const
  968. {
  969.     // redefined here because the other parse() function hides this function's
  970.     // ounterpart on DateFormat
  971.     return DateFormat::parse(text, status);
  972. }
  973. //----------------------------------------------------------------------
  974.  
  975. int32_t SimpleDateFormat::matchString(const UnicodeString& text,
  976.                               int32_t start,
  977.                               Calendar::EDateFields field,
  978.                               const UnicodeString* data,
  979.                               int32_t dataCount) const
  980. {
  981.     int32_t i = 0;
  982.     int32_t count = dataCount;
  983.  
  984.     if (field == Calendar::DAY_OF_WEEK) i = 1;
  985.  
  986.     // There may be multiple strings in the data[] array which begin with
  987.     // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
  988.     // We keep track of the longest match, and return that.  Note that this
  989.     // unfortunately requires us to test all array elements.
  990.     int32_t bestMatchLength = 0, bestMatch = -1;
  991.  
  992.     // {sfb} kludge to support case-insensitive comparison
  993.     UnicodeString lcaseText(text);
  994.     lcaseText.toLower();
  995.  
  996.     for (; i < count; ++i)
  997.     {
  998.         int32_t length = data[i].size();
  999.         // Always compare if we have no match yet; otherwise only compare
  1000.         // against potentially better matches (longer strings).
  1001.  
  1002.         UnicodeString lcase(data[i]);
  1003.         lcase.toLower();
  1004.                     
  1005.         if (length > bestMatchLength && (lcaseText.compareBetween(start, start + length, lcase, 0, length)) == 0)
  1006.         {
  1007.             bestMatch = i;
  1008.             bestMatchLength = length;
  1009.         }
  1010.     }
  1011.     if (bestMatch >= 0)
  1012.     {
  1013.         fCalendar->set(field, bestMatch);
  1014.         return start + bestMatchLength;
  1015.     }
  1016.     
  1017.     return -start;
  1018. }
  1019.  
  1020. //----------------------------------------------------------------------
  1021.  
  1022. void
  1023. SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status)
  1024. {
  1025.     parseAmbiguousDatesAsAfter(d, status);
  1026. }
  1027.  
  1028. /**
  1029.  * Parse the given text, at the given position, as a numeric value, using
  1030.  * this objects fNumberFormat. Return the corresponding long value in the
  1031.  * fill-in parameter 'value'. If the parse fails, this method leaves pos
  1032.  * unchanged and returns FALSE; otherwise it advances pos and
  1033.  * returns TRUE.
  1034.  */
  1035. // {sfb} removed
  1036. /*
  1037. bool_t
  1038. SimpleDateFormat::subParseLong(const UnicodeString& text, ParsePosition& pos, int32_t& value) const
  1039. {
  1040.     Formattable parseResult;
  1041.     ParsePosition posSave = pos;
  1042.     fNumberFormat->parse(text, parseResult, pos);
  1043.     if (pos != posSave && parseResult.getType() == Formattable::kLong)
  1044.     {
  1045.         value = parseResult.getLong();
  1046.         return TRUE;
  1047.     }
  1048.     pos = posSave;
  1049.     return FALSE;
  1050. }
  1051. */
  1052.  
  1053. /**
  1054.  * Private member function that converts the parsed date strings into
  1055.  * timeFields. Returns -start (for ParsePosition) if failed.
  1056.  * @param text the time text to be parsed.
  1057.  * @param start where to start parsing.
  1058.  * @param ch the pattern character for the date field text to be parsed.
  1059.  * @param count the count of a pattern character.
  1060.  * @return the new start position if matching succeeded; a negative number
  1061.  * indicating matching failure, otherwise.
  1062.  */
  1063. int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count,
  1064.                            bool_t obeyCount, bool_t ambiguousYear[]) const
  1065. {
  1066.     UErrorCode status = U_ZERO_ERROR;
  1067.     Formattable number;
  1068.     int32_t value = 0;
  1069.     int32_t i;
  1070.     ParsePosition pos(0);
  1071.     int32_t patternCharIndex = -1;
  1072.     
  1073.     if ((patternCharIndex = DateFormatSymbols::fgPatternChars.indexOf(ch)) == -1) 
  1074.         return -start;
  1075.     
  1076.     pos.setIndex(start);
  1077.  
  1078.     Calendar::EDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
  1079.  
  1080.     // If there are any spaces here, skip over them.  If we hit the end
  1081.     // of the string, then fail.
  1082.     for (;;) {
  1083.         if (pos.getIndex() >= text.size()) 
  1084.             return -start;
  1085.         UChar c = text[pos.getIndex()];
  1086.         if (c != 0x0020 /*' '*/ && c != 0x0009 /*'\t'*/) 
  1087.             break;
  1088.         pos.setIndex(pos.getIndex() + 1);
  1089.     }
  1090.  
  1091.     // We handle a few special cases here where we need to parse
  1092.     // a number value.  We handle further, more generic cases below.  We need
  1093.     // to handle some of them here because some fields require extra processing on
  1094.     // the parsed value.
  1095.     if (patternCharIndex == kHourOfDay1Field /*HOUR_OF_DAY1_FIELD*/ ||
  1096.         patternCharIndex == kHour1Field /*HOUR1_FIELD*/ ||
  1097.         (patternCharIndex == kMonthField /*MONTH_FIELD*/ && count <= 2) ||
  1098.         patternCharIndex == kYearField /*YEAR*/)
  1099.     {
  1100.         int32_t parseStart = pos.getIndex(); // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
  1101.         // It would be good to unify this with the obeyCount logic below,
  1102.         // but that's going to be difficult.
  1103.         if (obeyCount)
  1104.         {
  1105.             if ((start+count) > text.size()) 
  1106.                 return -start;
  1107.             UnicodeString temp;
  1108.             text.extractBetween(0, start + count, temp);
  1109.             fNumberFormat->parse(temp, number, pos);
  1110.         }
  1111.         else 
  1112.             fNumberFormat->parse(text, number, pos);
  1113.         if (pos.getIndex() == parseStart)
  1114.             // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
  1115.             return -start;
  1116.         value = number.getLong();
  1117.     }
  1118.  
  1119.     switch (patternCharIndex) {
  1120.     case kEraField:
  1121.         return matchString(text, start, Calendar::ERA, fSymbols->fEras, fSymbols->fErasCount);
  1122.     case kYearField:
  1123.         // If there are 3 or more YEAR pattern characters, this indicates
  1124.         // that the year value is to be treated literally, without any
  1125.         // two-digit year adjustments (e.g., from "01" to 2001).  Otherwise
  1126.         // we made adjustments to place the 2-digit year in the proper
  1127.         // century, for parsed strings from "00" to "99".  Any other string
  1128.         // is treated literally:  "2250", "-1", "1", "002".
  1129.         if (count <= 2 && (pos.getIndex() - start) == 2
  1130.             && Unicode::isDigit(text.charAt(start))
  1131.             && Unicode::isDigit(text.charAt(start+1)))
  1132.         {
  1133.             // Assume for example that the defaultCenturyStart is 6/18/1903.
  1134.             // This means that two-digit years will be forced into the range
  1135.             // 6/18/1903 to 6/17/2003.  As a result, years 00, 01, and 02
  1136.             // correspond to 2000, 2001, and 2002.  Years 04, 05, etc. correspond
  1137.             // to 1904, 1905, etc.  If the year is 03, then it is 2003 if the
  1138.             // other fields specify a date before 6/18, or 1903 if they specify a
  1139.             // date afterwards.  As a result, 03 is an ambiguous year.  All other
  1140.             // two-digit years are unambiguous.
  1141.             int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
  1142.             ambiguousYear[0] = (value == ambiguousTwoDigitYear);
  1143.             value += (fDefaultCenturyStartYear/100)*100 +
  1144.                 (value < ambiguousTwoDigitYear ? 100 : 0);
  1145.         }
  1146.         fCalendar->set(Calendar::YEAR, value);
  1147.         return pos.getIndex();
  1148.     case kMonthField:
  1149.         if (count <= 2) // i.e., M or MM.
  1150.         {
  1151.             // Don't want to parse the month if it is a string
  1152.             // while pattern uses numeric style: M or MM.
  1153.             // [We computed 'value' above.]
  1154.             fCalendar->set(Calendar::MONTH, value - 1);
  1155.             return pos.getIndex();
  1156.         }
  1157.         else
  1158.         {
  1159.             // count >= 3 // i.e., MMM or MMMM
  1160.             // Want to be able to parse both short and long forms.
  1161.             // Try count == 4 first:
  1162.             int32_t newStart = 0;
  1163.             if ((newStart = matchString(text, start, Calendar::MONTH,
  1164.                                       fSymbols->fMonths, fSymbols->fMonthsCount)) > 0)
  1165.                 return newStart;
  1166.             else // count == 4 failed, now try count == 3
  1167.                 return matchString(text, start, Calendar::MONTH,
  1168.                                    fSymbols->fShortMonths, fSymbols->fShortMonthsCount);
  1169.         }
  1170.     case kHourOfDay1Field:
  1171.         // [We computed 'value' above.]
  1172.         if (value == fCalendar->getMaximum(Calendar::HOUR_OF_DAY) + 1) 
  1173.             value = 0;
  1174.         fCalendar->set(Calendar::HOUR_OF_DAY, value);
  1175.         return pos.getIndex();
  1176.     case kDayOfWeekField:
  1177.         {
  1178.             // Want to be able to parse both short and long forms.
  1179.             // Try count == 4 (DDDD) first:
  1180.             int32_t newStart = 0;
  1181.             if ((newStart = matchString(text, start, Calendar::DAY_OF_WEEK,
  1182.                                       fSymbols->fWeekdays, fSymbols->fWeekdaysCount)) > 0)
  1183.                 return newStart;
  1184.             else // DDDD failed, now try DDD
  1185.                 return matchString(text, start, Calendar::DAY_OF_WEEK,
  1186.                                    fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount);
  1187.         }
  1188.     case kAmPmField:
  1189.         return matchString(text, start, Calendar::AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount);
  1190.     case kHour1Field:
  1191.         // [We computed 'value' above.]
  1192.         if (value == fCalendar->getLeastMaximum(Calendar::HOUR)+1) 
  1193.             value = 0;
  1194.         fCalendar->set(Calendar::HOUR, value);
  1195.         return pos.getIndex();
  1196.     case kTimezoneField:
  1197.         {
  1198.         // First try to parse generic forms such as GMT-07:00. Do this first
  1199.         // in case localized DateFormatZoneData contains the string "GMT"
  1200.         // for a zone; in that case, we don't want to match the first three
  1201.         // characters of GMT+/-HH:MM etc.
  1202.         int32_t sign = 0;
  1203.         int32_t offset;
  1204.  
  1205.         // For time zones that have no known names, look for strings
  1206.         // of the form:
  1207.         //    GMT[+-]hours:minutes or
  1208.         //    GMT[+-]hhmm or
  1209.         //    GMT.
  1210.         
  1211.         // {sfb} kludge for case-insensitive compare
  1212.         UnicodeString lcaseText(text);
  1213.         lcaseText.toLower();
  1214.         UnicodeString lcaseGMT(fgGmt);
  1215.         lcaseGMT.toLower();
  1216.         
  1217.         if ((text.size() - start) > fgGmt.size() &&
  1218.             (lcaseText.compare(start, lcaseGMT.size(), lcaseGMT, 0, lcaseGMT.size())) == 0)
  1219.         {
  1220.             fCalendar->set(Calendar::DST_OFFSET, 0);
  1221.  
  1222.             pos.setIndex(start + fgGmt.size());
  1223.  
  1224.             if( text[pos.getIndex()] == 0x002B /*'+'*/ )
  1225.                 sign = 1;
  1226.             else if( text[pos.getIndex()] == 0x002D /*'-'*/ )
  1227.                 sign = -1;
  1228.             else {
  1229.                 fCalendar->set(Calendar::ZONE_OFFSET, 0 );
  1230.                 return pos.getIndex();
  1231.             }
  1232.  
  1233.             // Look for hours:minutes or hhmm.
  1234.             pos.setIndex(pos.getIndex() + 1);
  1235.             // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
  1236.             int32_t parseStart = pos.getIndex();
  1237.             Formattable tzNumber;
  1238.             fNumberFormat->parse(text, tzNumber, pos);
  1239.             if( pos.getIndex() == parseStart) {
  1240.                 // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
  1241.                 return -start;
  1242.             }
  1243.             if( text[pos.getIndex()] == 0x003A /*':'*/ ) {
  1244.                 // This is the hours:minutes case
  1245.                 offset = tzNumber.getLong() * 60;
  1246.                 pos.setIndex(pos.getIndex() + 1);
  1247.                 // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
  1248.                 parseStart = pos.getIndex();
  1249.                 fNumberFormat->parse(text, tzNumber, pos);
  1250.                 if( pos.getIndex() == parseStart) {
  1251.                     // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
  1252.                     return -start;
  1253.                 }
  1254.                 offset += tzNumber.getLong();
  1255.             }
  1256.             else {
  1257.                 // This is the hhmm case.
  1258.                 offset = tzNumber.getLong();
  1259.                 if( offset < 24 )
  1260.                     offset *= 60;
  1261.                 else
  1262.                     offset = offset % 100 + offset / 100 * 60;
  1263.             }
  1264.  
  1265.             // Fall through for final processing below of 'offset' and 'sign'.
  1266.         }
  1267.         else {
  1268.             // At this point, check for named time zones by looking through
  1269.             // the locale data from the DateFormatZoneData strings.
  1270.             // Want to be able to parse both short and long forms.
  1271.             for (i = 0; i < fSymbols->fZoneStringsRowCount; i++)
  1272.             {
  1273.                 // Checking long and short zones [1 & 2],
  1274.                 // and long and short daylight [3 & 4].
  1275.                 int32_t j = 1;
  1276.                 
  1277.                 // {sfb} kludge for case-insensitive compare
  1278.                 UnicodeString s1(text);
  1279.                 s1.toLower();
  1280.                 
  1281.                 for (; j <= 4; ++j)
  1282.                 {
  1283.                     UnicodeString s2(fSymbols->fZoneStrings[i][j]);
  1284.                     s2.toLower();
  1285.                 
  1286.                     if ((s1.compare(start, s2.size(), s2, 0, s2.size())) == 0)
  1287.                         break;
  1288.                 }
  1289.                 if (j <= 4)
  1290.                 {
  1291.                     TimeZone *tz = TimeZone::createTimeZone(fSymbols->fZoneStrings[i][0]);
  1292.                     fCalendar->set(Calendar::ZONE_OFFSET, tz->getRawOffset());
  1293.                     // Must call set() with something -- TODO -- Fix this to
  1294.                     // use the correct DST SAVINGS for the zone.
  1295.                     delete tz;
  1296.                     fCalendar->set(Calendar::DST_OFFSET, j >= 3 ? U_MILLIS_PER_HOUR : 0);
  1297.                     return (start + fSymbols->fZoneStrings[i][j].size());
  1298.                 }
  1299.             }
  1300.  
  1301.             // As a last resort, look for numeric timezones of the form
  1302.             // [+-]hhmm as specified by RFC 822.  This code is actually
  1303.             // a little more permissive than RFC 822.  It will try to do
  1304.             // its best with numbers that aren't strictly 4 digits long.
  1305.             UErrorCode status = U_ZERO_ERROR;
  1306.             DecimalFormat *fmt = new DecimalFormat("+####;-####", status);
  1307.             if(U_FAILURE(status))
  1308.                 return -start;
  1309.             fmt->setParseIntegerOnly(TRUE);
  1310.             // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
  1311.             int32_t parseStart = pos.getIndex();
  1312.             Formattable tzNumber;
  1313.             fmt->parse( text, tzNumber, pos );
  1314.             if( pos.getIndex() == parseStart) {
  1315.                 // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
  1316.                 return -start;   // Wasn't actually a number.
  1317.             }
  1318.             offset = tzNumber.getLong();
  1319.             sign = 1;
  1320.             if( offset < 0 ) {
  1321.                 sign = -1;
  1322.                 offset = -offset;
  1323.             }
  1324.             if( offset < 24 )
  1325.                 offset = offset * 60;
  1326.             else
  1327.                 offset = offset % 100 + offset / 100 * 60;
  1328.  
  1329.             // Fall through for final processing below of 'offset' and 'sign'.
  1330.         }
  1331.  
  1332.         // Do the final processing for both of the above cases.  We only
  1333.         // arrive here if the form GMT+/-... or an RFC 822 form was seen.
  1334.         if (sign != 0)
  1335.         {
  1336.             offset *= U_MILLIS_PER_MINUTE * sign;
  1337.  
  1338.             if (fCalendar->getTimeZone().useDaylightTime())
  1339.             {
  1340.                 fCalendar->set(Calendar::DST_OFFSET, U_MILLIS_PER_HOUR);
  1341.                 offset -= U_MILLIS_PER_HOUR;
  1342.             }
  1343.             fCalendar->set(Calendar::ZONE_OFFSET, offset);
  1344.  
  1345.             return pos.getIndex();
  1346.         }
  1347.  
  1348.         // All efforts to parse a zone failed.
  1349.         return -start;
  1350.         }
  1351.     default:
  1352.     // case 3: // 'd' - DATE
  1353.     // case 5: // 'H' - HOUR_OF_DAY:0-based.  eg, 23:59 + 1 hour =>> 00:59
  1354.     // case 6: // 'm' - MINUTE
  1355.     // case 7: // 's' - SECOND
  1356.     // case 8: // 'S' - MILLISECOND
  1357.     // case 10: // 'D' - DAY_OF_YEAR
  1358.     // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH
  1359.     // case 12: // 'w' - WEEK_OF_YEAR
  1360.     // case 13: // 'W' - WEEK_OF_MONTH
  1361.     // case 16: // 'K' - HOUR: 0-based.  eg, 11PM + 1 hour =>> 0 AM
  1362.  
  1363.         // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
  1364.         int32_t parseStart = pos.getIndex();
  1365.         // Handle "generic" fields
  1366.         if (obeyCount)
  1367.         {
  1368.             if ((start+count) > text.size()) 
  1369.                 return -start;
  1370.             UnicodeString s;
  1371.             // {sfb} old code had extract, make sure it works
  1372.             text.extractBetween(0, start + count, s);
  1373.             fNumberFormat->parse(s, number, pos);
  1374.         }
  1375.         else 
  1376.             fNumberFormat->parse(text, number, pos);
  1377.         if (pos.getIndex() != parseStart) {
  1378.             // WORK AROUND BUG IN NUMBER FORMAT IN 1.2B3
  1379.             fCalendar->set(field, number.getLong());
  1380.             return pos.getIndex();
  1381.         }
  1382.         return -start;
  1383.     }
  1384. }
  1385.  
  1386. //----------------------------------------------------------------------
  1387.  
  1388. void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern,
  1389.                                         UnicodeString& translatedPattern,
  1390.                                         const UnicodeString& from,
  1391.                                         const UnicodeString& to,
  1392.                                         UErrorCode& status)
  1393. {
  1394.   // run through the pattern and convert any pattern symbols from the version
  1395.   // in "from" to the corresponding character ion "to".  This code takes
  1396.   // quoted strings into account (it doesn't try to translate them), and it signals
  1397.   // an error if a particular "pattern character" doesn't appear in "from".
  1398.   // Depending on the values of "from" and "to" this can convert from generic
  1399.   // to localized patterns or localized to generic.
  1400.   if (U_FAILURE(status)) 
  1401.     return;
  1402.   
  1403.   translatedPattern.remove();
  1404.   bool_t inQuote = FALSE;
  1405.   for (UTextOffset i = 0; i < originalPattern.size(); ++i) {
  1406.     UChar c = originalPattern[i];
  1407.     if (inQuote) {
  1408.       if (c == 0x0027 /*'\''*/) 
  1409.     inQuote = FALSE;
  1410.     }
  1411.     else {
  1412.       if (c == 0x0027 /*'\''*/) 
  1413.     inQuote = TRUE;
  1414.       else if ((c >= 0x0061 /*'a'*/ && c <= 0x007A) /*'z'*/ 
  1415.            || (c >= 0x0041 /*'A'*/ && c <= 0x005A /*'Z'*/)) {
  1416.     UTextOffset ci = from.indexOf(c);
  1417.     if (ci == -1) {
  1418.       status = U_INVALID_FORMAT_ERROR;
  1419.       return;
  1420.     }
  1421.     c = to[ci];
  1422.       }
  1423.     }
  1424.     translatedPattern += c;
  1425.   }
  1426.   if (inQuote) {
  1427.     status = U_INVALID_FORMAT_ERROR;
  1428.     return;
  1429.   }
  1430. }
  1431.  
  1432. //----------------------------------------------------------------------
  1433.  
  1434. UnicodeString&
  1435. SimpleDateFormat::toPattern(UnicodeString& result) const
  1436. {
  1437.     result = fPattern;
  1438.     return result;
  1439. }
  1440.  
  1441. //----------------------------------------------------------------------
  1442.  
  1443. UnicodeString&
  1444. SimpleDateFormat::toLocalizedPattern(UnicodeString& result,
  1445.                                      UErrorCode& status) const
  1446. {
  1447.     translatePattern(fPattern, result, DateFormatSymbols::fgPatternChars, fSymbols->fLocalPatternChars, status);
  1448.     return result;
  1449. }
  1450.  
  1451. //----------------------------------------------------------------------
  1452.  
  1453. void
  1454. SimpleDateFormat::applyPattern(const UnicodeString& pattern)
  1455. {
  1456.     fPattern = pattern;
  1457. }
  1458.  
  1459. //----------------------------------------------------------------------
  1460.  
  1461. void
  1462. SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern,
  1463.                                         UErrorCode &status)
  1464. {
  1465.     translatePattern(pattern, fPattern, fSymbols->fLocalPatternChars, DateFormatSymbols::fgPatternChars, status);
  1466. }
  1467.  
  1468. //----------------------------------------------------------------------
  1469.  
  1470. const DateFormatSymbols*
  1471. SimpleDateFormat::getDateFormatSymbols() const
  1472. {
  1473.     return fSymbols;
  1474. }
  1475.  
  1476. //----------------------------------------------------------------------
  1477.  
  1478. void
  1479. SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols* newFormatSymbols)
  1480. {
  1481.     delete fSymbols;
  1482.     fSymbols = newFormatSymbols;
  1483. }
  1484.  
  1485. //----------------------------------------------------------------------
  1486. void
  1487. SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols)
  1488. {
  1489.     delete fSymbols;
  1490.     fSymbols = new DateFormatSymbols(newFormatSymbols);
  1491. }
  1492.  
  1493.  
  1494. //----------------------------------------------------------------------
  1495.  
  1496. // {sfb} removed
  1497. /*int32_t
  1498. SimpleDateFormat::getZoneIndex(const UnicodeString& ID) const
  1499. {
  1500.     // this function searches a time zone list for a time zone with the specified
  1501.     // ID.  It'll either return an apprpriate row number or -1 if the ID wasn't
  1502.     // found.
  1503.     int32_t index, col;
  1504.  
  1505.     for (col=0; col<=4 && col<fSymbols->fZoneStringsColCount; col+=2)
  1506.     {
  1507.         for (index = 0; index < fSymbols->fZoneStringsRowCount; index++)
  1508.         {
  1509.             if (fSymbols->fZoneStrings[index][col] == ID) return index;
  1510.         }
  1511.     }
  1512.  
  1513.     return - 1;
  1514. }*/
  1515.  
  1516. //----------------------------------------------------------------------
  1517.  
  1518. UDate
  1519. SimpleDateFormat::internalGetDefaultCenturyStart() const
  1520. {
  1521.     // lazy-evaluate systemDefaultCenturyStart
  1522.     if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
  1523.         initializeSystemDefaultCentury();
  1524.  
  1525.     // use defaultCenturyStart unless it's the flag value;
  1526.     // then use systemDefaultCenturyStart
  1527.     return (fDefaultCenturyStart == fgSystemDefaultCentury) ?
  1528.         fgSystemDefaultCenturyStart : fDefaultCenturyStart;
  1529. }
  1530.  
  1531. int32_t
  1532. SimpleDateFormat::internalGetDefaultCenturyStartYear() const
  1533. {
  1534.     // lazy-evaluate systemDefaultCenturyStartYear
  1535.     if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
  1536.         initializeSystemDefaultCentury();
  1537.  
  1538.     // use defaultCenturyStart unless it's the flag value;
  1539.     // then use systemDefaultCenturyStartYear
  1540.     //return (fDefaultCenturyStart == fgSystemDefaultCentury) ?
  1541.     return (fDefaultCenturyStartYear == fgSystemDefaultCenturyYear) ?
  1542.         fgSystemDefaultCenturyStartYear : fDefaultCenturyStartYear;
  1543. }
  1544.  
  1545. void
  1546. SimpleDateFormat::initializeSystemDefaultCentury()
  1547. {
  1548.     // initialize systemDefaultCentury and systemDefaultCenturyYear based
  1549.     // on the current time.  They'll be set to 80 years before
  1550.     // the current time.
  1551.     // No point in locking as it should be idempotent.
  1552.     if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
  1553.     {
  1554.         UErrorCode status = U_ZERO_ERROR;
  1555.         Calendar *calendar = Calendar::createInstance(status);
  1556.         if (calendar != NULL && U_SUCCESS(status))
  1557.         {
  1558.             calendar->setTime(Calendar::getNow(), status);
  1559.             calendar->add(Calendar::YEAR, -80, status);
  1560.             fgSystemDefaultCenturyStart = calendar->getTime(status);
  1561.             fgSystemDefaultCenturyStartYear = calendar->get(Calendar::YEAR, status);
  1562.             delete calendar;
  1563.         }
  1564.         // We have no recourse upon failure unless we want to propagate the failure
  1565.         // out.
  1566.     }
  1567. }
  1568.  
  1569. //eof
  1570.