intl/icu/source/i18n/smpdtfmt.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/intl/icu/source/i18n/smpdtfmt.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3554 @@
     1.4 +/*
     1.5 +*******************************************************************************
     1.6 +* Copyright (C) 1997-2013, International Business Machines Corporation and    *
     1.7 +* others. All Rights Reserved.                                                *
     1.8 +*******************************************************************************
     1.9 +*
    1.10 +* File SMPDTFMT.CPP
    1.11 +*
    1.12 +* Modification History:
    1.13 +*
    1.14 +*   Date        Name        Description
    1.15 +*   02/19/97    aliu        Converted from java.
    1.16 +*   03/31/97    aliu        Modified extensively to work with 50 locales.
    1.17 +*   04/01/97    aliu        Added support for centuries.
    1.18 +*   07/09/97    helena      Made ParsePosition into a class.
    1.19 +*   07/21/98    stephen     Added initializeDefaultCentury.
    1.20 +*                             Removed getZoneIndex (added in DateFormatSymbols)
    1.21 +*                             Removed subParseLong
    1.22 +*                             Removed chk
    1.23 +*  02/22/99     stephen     Removed character literals for EBCDIC safety
    1.24 +*   10/14/99    aliu        Updated 2-digit year parsing so that only "00" thru
    1.25 +*                           "99" are recognized. {j28 4182066}
    1.26 +*   11/15/99    weiv        Added support for week of year/day of week format
    1.27 +********************************************************************************
    1.28 +*/
    1.29 +
    1.30 +#define ZID_KEY_MAX 128
    1.31 +
    1.32 +#include "unicode/utypes.h"
    1.33 +
    1.34 +#if !UCONFIG_NO_FORMATTING
    1.35 +
    1.36 +#include "unicode/smpdtfmt.h"
    1.37 +#include "unicode/dtfmtsym.h"
    1.38 +#include "unicode/ures.h"
    1.39 +#include "unicode/msgfmt.h"
    1.40 +#include "unicode/calendar.h"
    1.41 +#include "unicode/gregocal.h"
    1.42 +#include "unicode/timezone.h"
    1.43 +#include "unicode/decimfmt.h"
    1.44 +#include "unicode/dcfmtsym.h"
    1.45 +#include "unicode/uchar.h"
    1.46 +#include "unicode/uniset.h"
    1.47 +#include "unicode/ustring.h"
    1.48 +#include "unicode/basictz.h"
    1.49 +#include "unicode/simpletz.h"
    1.50 +#include "unicode/rbtz.h"
    1.51 +#include "unicode/tzfmt.h"
    1.52 +#include "unicode/utf16.h"
    1.53 +#include "unicode/vtzone.h"
    1.54 +#include "unicode/udisplaycontext.h"
    1.55 +#include "olsontz.h"
    1.56 +#include "patternprops.h"
    1.57 +#include "fphdlimp.h"
    1.58 +#include "gregoimp.h"
    1.59 +#include "hebrwcal.h"
    1.60 +#include "cstring.h"
    1.61 +#include "uassert.h"
    1.62 +#include "cmemory.h"
    1.63 +#include "umutex.h"
    1.64 +#include <float.h>
    1.65 +#include "smpdtfst.h"
    1.66 +
    1.67 +#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
    1.68 +#include <stdio.h>
    1.69 +#endif
    1.70 +
    1.71 +// *****************************************************************************
    1.72 +// class SimpleDateFormat
    1.73 +// *****************************************************************************
    1.74 +
    1.75 +U_NAMESPACE_BEGIN
    1.76 +
    1.77 +static const UChar PATTERN_CHAR_BASE = 0x40;
    1.78 +
    1.79 +/**
    1.80 + * Last-resort string to use for "GMT" when constructing time zone strings.
    1.81 + */
    1.82 +// For time zones that have no names, use strings GMT+minutes and
    1.83 +// GMT-minutes. For instance, in France the time zone is GMT+60.
    1.84 +// Also accepted are GMT+H:MM or GMT-H:MM.
    1.85 +// Currently not being used
    1.86 +//static const UChar gGmt[]      = {0x0047, 0x004D, 0x0054, 0x0000};         // "GMT"
    1.87 +//static const UChar gGmtPlus[]  = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
    1.88 +//static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
    1.89 +//static const UChar gDefGmtPat[]       = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
    1.90 +//static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
    1.91 +//static const UChar gDefGmtNegHmPat[]  = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
    1.92 +//static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
    1.93 +//static const UChar gDefGmtPosHmPat[]  = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
    1.94 +//static const UChar gUt[]       = {0x0055, 0x0054, 0x0000};  // "UT"
    1.95 +//static const UChar gUtc[]      = {0x0055, 0x0054, 0x0043, 0x0000};  // "UT"
    1.96 +
    1.97 +typedef enum GmtPatSize {
    1.98 +    kGmtLen = 3,
    1.99 +    kGmtPatLen = 6,
   1.100 +    kNegHmsLen = 9,
   1.101 +    kNegHmLen = 6,
   1.102 +    kPosHmsLen = 9,
   1.103 +    kPosHmLen = 6,
   1.104 +    kUtLen = 2,
   1.105 +    kUtcLen = 3
   1.106 +} GmtPatSize;
   1.107 +
   1.108 +// Stuff needed for numbering system overrides
   1.109 +
   1.110 +typedef enum OvrStrType {
   1.111 +    kOvrStrDate = 0,
   1.112 +    kOvrStrTime = 1,
   1.113 +    kOvrStrBoth = 2
   1.114 +} OvrStrType;
   1.115 +
   1.116 +static const UDateFormatField kDateFields[] = {
   1.117 +    UDAT_YEAR_FIELD,
   1.118 +    UDAT_MONTH_FIELD,
   1.119 +    UDAT_DATE_FIELD,
   1.120 +    UDAT_DAY_OF_YEAR_FIELD,
   1.121 +    UDAT_DAY_OF_WEEK_IN_MONTH_FIELD,
   1.122 +    UDAT_WEEK_OF_YEAR_FIELD,
   1.123 +    UDAT_WEEK_OF_MONTH_FIELD,
   1.124 +    UDAT_YEAR_WOY_FIELD,
   1.125 +    UDAT_EXTENDED_YEAR_FIELD,
   1.126 +    UDAT_JULIAN_DAY_FIELD,
   1.127 +    UDAT_STANDALONE_DAY_FIELD,
   1.128 +    UDAT_STANDALONE_MONTH_FIELD,
   1.129 +    UDAT_QUARTER_FIELD,
   1.130 +    UDAT_STANDALONE_QUARTER_FIELD,
   1.131 +    UDAT_YEAR_NAME_FIELD };
   1.132 +static const int8_t kDateFieldsCount = 15;
   1.133 +
   1.134 +static const UDateFormatField kTimeFields[] = {
   1.135 +    UDAT_HOUR_OF_DAY1_FIELD,
   1.136 +    UDAT_HOUR_OF_DAY0_FIELD,
   1.137 +    UDAT_MINUTE_FIELD,
   1.138 +    UDAT_SECOND_FIELD,
   1.139 +    UDAT_FRACTIONAL_SECOND_FIELD,
   1.140 +    UDAT_HOUR1_FIELD,
   1.141 +    UDAT_HOUR0_FIELD,
   1.142 +    UDAT_MILLISECONDS_IN_DAY_FIELD,
   1.143 +    UDAT_TIMEZONE_RFC_FIELD,
   1.144 +    UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD };
   1.145 +static const int8_t kTimeFieldsCount = 10;
   1.146 +
   1.147 +
   1.148 +// This is a pattern-of-last-resort used when we can't load a usable pattern out
   1.149 +// of a resource.
   1.150 +static const UChar gDefaultPattern[] =
   1.151 +{
   1.152 +    0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
   1.153 +};  /* "yyyyMMdd hh:mm a" */
   1.154 +
   1.155 +// This prefix is designed to NEVER MATCH real text, in order to
   1.156 +// suppress the parsing of negative numbers.  Adjust as needed (if
   1.157 +// this becomes valid Unicode).
   1.158 +static const UChar SUPPRESS_NEGATIVE_PREFIX[] = {0xAB00, 0};
   1.159 +
   1.160 +/**
   1.161 + * These are the tags we expect to see in normal resource bundle files associated
   1.162 + * with a locale.
   1.163 + */
   1.164 +static const char gDateTimePatternsTag[]="DateTimePatterns";
   1.165 +
   1.166 +//static const UChar gEtcUTC[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC"
   1.167 +static const UChar QUOTE = 0x27; // Single quote
   1.168 +
   1.169 +/*
   1.170 + * The field range check bias for each UDateFormatField.
   1.171 + * The bias is added to the minimum and maximum values
   1.172 + * before they are compared to the parsed number.
   1.173 + * For example, the calendar stores zero-based month numbers
   1.174 + * but the parsed month numbers start at 1, so the bias is 1.
   1.175 + *
   1.176 + * A value of -1 means that the value is not checked.
   1.177 + */
   1.178 +static const int32_t gFieldRangeBias[] = {
   1.179 +    -1,  // 'G' - UDAT_ERA_FIELD
   1.180 +    -1,  // 'y' - UDAT_YEAR_FIELD
   1.181 +     1,  // 'M' - UDAT_MONTH_FIELD
   1.182 +     0,  // 'd' - UDAT_DATE_FIELD
   1.183 +    -1,  // 'k' - UDAT_HOUR_OF_DAY1_FIELD
   1.184 +    -1,  // 'H' - UDAT_HOUR_OF_DAY0_FIELD
   1.185 +     0,  // 'm' - UDAT_MINUTE_FIELD
   1.186 +     0,  // 's' - UDAT_SEOND_FIELD
   1.187 +    -1,  // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
   1.188 +    -1,  // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
   1.189 +    -1,  // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
   1.190 +    -1,  // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
   1.191 +    -1,  // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
   1.192 +    -1,  // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
   1.193 +    -1,  // 'a' - UDAT_AM_PM_FIELD
   1.194 +    -1,  // 'h' - UDAT_HOUR1_FIELD
   1.195 +    -1,  // 'K' - UDAT_HOUR0_FIELD
   1.196 +    -1,  // 'z' - UDAT_TIMEZONE_FIELD
   1.197 +    -1,  // 'Y' - UDAT_YEAR_WOY_FIELD
   1.198 +    -1,  // 'e' - UDAT_DOW_LOCAL_FIELD
   1.199 +    -1,  // 'u' - UDAT_EXTENDED_YEAR_FIELD
   1.200 +    -1,  // 'g' - UDAT_JULIAN_DAY_FIELD
   1.201 +    -1,  // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
   1.202 +    -1,  // 'Z' - UDAT_TIMEZONE_RFC_FIELD
   1.203 +    -1,  // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
   1.204 +     0,  // 'c' - UDAT_STANDALONE_DAY_FIELD
   1.205 +     1,  // 'L' - UDAT_STANDALONE_MONTH_FIELD
   1.206 +    -1,  // 'Q' - UDAT_QUARTER_FIELD (1-4?)
   1.207 +    -1,  // 'q' - UDAT_STANDALONE_QUARTER_FIELD
   1.208 +    -1   // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
   1.209 +    -1,  // 'U' - UDAT_YEAR_NAME_FIELD
   1.210 +    -1,  // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
   1.211 +    -1,  // 'X' - UDAT_TIMEZONE_ISO_FIELD
   1.212 +    -1,  // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
   1.213 +};
   1.214 +
   1.215 +// When calendar uses hebr numbering (i.e. he@calendar=hebrew),
   1.216 +// offset the years within the current millenium down to 1-999
   1.217 +static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR = 5000;
   1.218 +static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR = 6000;
   1.219 +
   1.220 +static UMutex LOCK = U_MUTEX_INITIALIZER;
   1.221 +
   1.222 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
   1.223 +
   1.224 +//----------------------------------------------------------------------
   1.225 +
   1.226 +SimpleDateFormat::~SimpleDateFormat()
   1.227 +{
   1.228 +    delete fSymbols;
   1.229 +    if (fNumberFormatters) {
   1.230 +        uprv_free(fNumberFormatters);
   1.231 +    }
   1.232 +    if (fTimeZoneFormat) {
   1.233 +        delete fTimeZoneFormat;
   1.234 +    }
   1.235 +
   1.236 +    while (fOverrideList) {
   1.237 +        NSOverride *cur = fOverrideList;
   1.238 +        fOverrideList = cur->next;
   1.239 +        delete cur->nf;
   1.240 +        uprv_free(cur);
   1.241 +    }
   1.242 +}
   1.243 +
   1.244 +//----------------------------------------------------------------------
   1.245 +
   1.246 +SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
   1.247 +  :   fLocale(Locale::getDefault()),
   1.248 +      fSymbols(NULL),
   1.249 +      fTimeZoneFormat(NULL),
   1.250 +      fNumberFormatters(NULL),
   1.251 +      fOverrideList(NULL),
   1.252 +      fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
   1.253 +{
   1.254 +    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
   1.255 +    construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status);
   1.256 +    initializeDefaultCentury();
   1.257 +}
   1.258 +
   1.259 +//----------------------------------------------------------------------
   1.260 +
   1.261 +SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
   1.262 +                                   UErrorCode &status)
   1.263 +:   fPattern(pattern),
   1.264 +    fLocale(Locale::getDefault()),
   1.265 +    fSymbols(NULL),
   1.266 +    fTimeZoneFormat(NULL),
   1.267 +    fNumberFormatters(NULL),
   1.268 +    fOverrideList(NULL),
   1.269 +    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
   1.270 +{
   1.271 +    fDateOverride.setToBogus();
   1.272 +    fTimeOverride.setToBogus();
   1.273 +    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
   1.274 +    initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
   1.275 +    initialize(fLocale, status);
   1.276 +    initializeDefaultCentury();
   1.277 +
   1.278 +}
   1.279 +//----------------------------------------------------------------------
   1.280 +
   1.281 +SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
   1.282 +                                   const UnicodeString& override,
   1.283 +                                   UErrorCode &status)
   1.284 +:   fPattern(pattern),
   1.285 +    fLocale(Locale::getDefault()),
   1.286 +    fSymbols(NULL),
   1.287 +    fTimeZoneFormat(NULL),
   1.288 +    fNumberFormatters(NULL),
   1.289 +    fOverrideList(NULL),
   1.290 +    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
   1.291 +{
   1.292 +    fDateOverride.setTo(override);
   1.293 +    fTimeOverride.setToBogus();
   1.294 +    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
   1.295 +    initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
   1.296 +    initialize(fLocale, status);
   1.297 +    initializeDefaultCentury();
   1.298 +
   1.299 +    processOverrideString(fLocale,override,kOvrStrBoth,status);
   1.300 +
   1.301 +}
   1.302 +
   1.303 +//----------------------------------------------------------------------
   1.304 +
   1.305 +SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
   1.306 +                                   const Locale& locale,
   1.307 +                                   UErrorCode& status)
   1.308 +:   fPattern(pattern),
   1.309 +    fLocale(locale),
   1.310 +    fTimeZoneFormat(NULL),
   1.311 +    fNumberFormatters(NULL),
   1.312 +    fOverrideList(NULL),
   1.313 +    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
   1.314 +{
   1.315 +
   1.316 +    fDateOverride.setToBogus();
   1.317 +    fTimeOverride.setToBogus();
   1.318 +    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
   1.319 +
   1.320 +    initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
   1.321 +    initialize(fLocale, status);
   1.322 +    initializeDefaultCentury();
   1.323 +}
   1.324 +
   1.325 +//----------------------------------------------------------------------
   1.326 +
   1.327 +SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
   1.328 +                                   const UnicodeString& override,
   1.329 +                                   const Locale& locale,
   1.330 +                                   UErrorCode& status)
   1.331 +:   fPattern(pattern),
   1.332 +    fLocale(locale),
   1.333 +    fTimeZoneFormat(NULL),
   1.334 +    fNumberFormatters(NULL),
   1.335 +    fOverrideList(NULL),
   1.336 +    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
   1.337 +{
   1.338 +
   1.339 +    fDateOverride.setTo(override);
   1.340 +    fTimeOverride.setToBogus();
   1.341 +    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
   1.342 +
   1.343 +    initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
   1.344 +    initialize(fLocale, status);
   1.345 +    initializeDefaultCentury();
   1.346 +
   1.347 +    processOverrideString(locale,override,kOvrStrBoth,status);
   1.348 +
   1.349 +}
   1.350 +
   1.351 +//----------------------------------------------------------------------
   1.352 +
   1.353 +SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
   1.354 +                                   DateFormatSymbols* symbolsToAdopt,
   1.355 +                                   UErrorCode& status)
   1.356 +:   fPattern(pattern),
   1.357 +    fLocale(Locale::getDefault()),
   1.358 +    fSymbols(symbolsToAdopt),
   1.359 +    fTimeZoneFormat(NULL),
   1.360 +    fNumberFormatters(NULL),
   1.361 +    fOverrideList(NULL),
   1.362 +    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
   1.363 +{
   1.364 +
   1.365 +    fDateOverride.setToBogus();
   1.366 +    fTimeOverride.setToBogus();
   1.367 +    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
   1.368 +
   1.369 +    initializeCalendar(NULL,fLocale,status);
   1.370 +    initialize(fLocale, status);
   1.371 +    initializeDefaultCentury();
   1.372 +}
   1.373 +
   1.374 +//----------------------------------------------------------------------
   1.375 +
   1.376 +SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
   1.377 +                                   const DateFormatSymbols& symbols,
   1.378 +                                   UErrorCode& status)
   1.379 +:   fPattern(pattern),
   1.380 +    fLocale(Locale::getDefault()),
   1.381 +    fSymbols(new DateFormatSymbols(symbols)),
   1.382 +    fTimeZoneFormat(NULL),
   1.383 +    fNumberFormatters(NULL),
   1.384 +    fOverrideList(NULL),
   1.385 +    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
   1.386 +{
   1.387 +
   1.388 +    fDateOverride.setToBogus();
   1.389 +    fTimeOverride.setToBogus();
   1.390 +    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
   1.391 +
   1.392 +    initializeCalendar(NULL, fLocale, status);
   1.393 +    initialize(fLocale, status);
   1.394 +    initializeDefaultCentury();
   1.395 +}
   1.396 +
   1.397 +//----------------------------------------------------------------------
   1.398 +
   1.399 +// Not for public consumption; used by DateFormat
   1.400 +SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
   1.401 +                                   EStyle dateStyle,
   1.402 +                                   const Locale& locale,
   1.403 +                                   UErrorCode& status)
   1.404 +:   fLocale(locale),
   1.405 +    fSymbols(NULL),
   1.406 +    fTimeZoneFormat(NULL),
   1.407 +    fNumberFormatters(NULL),
   1.408 +    fOverrideList(NULL),
   1.409 +    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
   1.410 +{
   1.411 +    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
   1.412 +    construct(timeStyle, dateStyle, fLocale, status);
   1.413 +    if(U_SUCCESS(status)) {
   1.414 +      initializeDefaultCentury();
   1.415 +    }
   1.416 +}
   1.417 +
   1.418 +//----------------------------------------------------------------------
   1.419 +
   1.420 +/**
   1.421 + * Not for public consumption; used by DateFormat.  This constructor
   1.422 + * never fails.  If the resource data is not available, it uses the
   1.423 + * the last resort symbols.
   1.424 + */
   1.425 +SimpleDateFormat::SimpleDateFormat(const Locale& locale,
   1.426 +                                   UErrorCode& status)
   1.427 +:   fPattern(gDefaultPattern),
   1.428 +    fLocale(locale),
   1.429 +    fSymbols(NULL),
   1.430 +    fTimeZoneFormat(NULL),
   1.431 +    fNumberFormatters(NULL),
   1.432 +    fOverrideList(NULL),
   1.433 +    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
   1.434 +{
   1.435 +    if (U_FAILURE(status)) return;
   1.436 +    initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status);
   1.437 +    if (U_FAILURE(status))
   1.438 +    {
   1.439 +        status = U_ZERO_ERROR;
   1.440 +        delete fSymbols;
   1.441 +        // This constructor doesn't fail; it uses last resort data
   1.442 +        fSymbols = new DateFormatSymbols(status);
   1.443 +        /* test for NULL */
   1.444 +        if (fSymbols == 0) {
   1.445 +            status = U_MEMORY_ALLOCATION_ERROR;
   1.446 +            return;
   1.447 +        }
   1.448 +    }
   1.449 +
   1.450 +    fDateOverride.setToBogus();
   1.451 +    fTimeOverride.setToBogus();
   1.452 +    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
   1.453 +
   1.454 +    initialize(fLocale, status);
   1.455 +    if(U_SUCCESS(status)) {
   1.456 +      initializeDefaultCentury();
   1.457 +    }
   1.458 +}
   1.459 +
   1.460 +//----------------------------------------------------------------------
   1.461 +
   1.462 +SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
   1.463 +:   DateFormat(other),
   1.464 +    fLocale(other.fLocale),
   1.465 +    fSymbols(NULL),
   1.466 +    fTimeZoneFormat(NULL),
   1.467 +    fNumberFormatters(NULL),
   1.468 +    fOverrideList(NULL),
   1.469 +    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
   1.470 +{
   1.471 +    UErrorCode status = U_ZERO_ERROR;
   1.472 +    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
   1.473 +    *this = other;
   1.474 +}
   1.475 +
   1.476 +//----------------------------------------------------------------------
   1.477 +
   1.478 +SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
   1.479 +{
   1.480 +    if (this == &other) {
   1.481 +        return *this;
   1.482 +    }
   1.483 +    DateFormat::operator=(other);
   1.484 +
   1.485 +    delete fSymbols;
   1.486 +    fSymbols = NULL;
   1.487 +
   1.488 +    if (other.fSymbols)
   1.489 +        fSymbols = new DateFormatSymbols(*other.fSymbols);
   1.490 +
   1.491 +    fDefaultCenturyStart         = other.fDefaultCenturyStart;
   1.492 +    fDefaultCenturyStartYear     = other.fDefaultCenturyStartYear;
   1.493 +    fHaveDefaultCentury          = other.fHaveDefaultCentury;
   1.494 +
   1.495 +    fPattern = other.fPattern;
   1.496 +
   1.497 +    // TimeZoneFormat in ICU4C only depends on a locale for now
   1.498 +    if (fLocale != other.fLocale) {
   1.499 +        delete fTimeZoneFormat;
   1.500 +        fTimeZoneFormat = NULL; // forces lazy instantiation with the other locale
   1.501 +        fLocale = other.fLocale;
   1.502 +    }
   1.503 +
   1.504 +    fCapitalizationContext = other.fCapitalizationContext;
   1.505 +
   1.506 +    return *this;
   1.507 +}
   1.508 +
   1.509 +//----------------------------------------------------------------------
   1.510 +
   1.511 +Format*
   1.512 +SimpleDateFormat::clone() const
   1.513 +{
   1.514 +    return new SimpleDateFormat(*this);
   1.515 +}
   1.516 +
   1.517 +//----------------------------------------------------------------------
   1.518 +
   1.519 +UBool
   1.520 +SimpleDateFormat::operator==(const Format& other) const
   1.521 +{
   1.522 +    if (DateFormat::operator==(other)) {
   1.523 +        // DateFormat::operator== guarantees following cast is safe
   1.524 +        SimpleDateFormat* that = (SimpleDateFormat*)&other;
   1.525 +        return (fPattern             == that->fPattern &&
   1.526 +                fSymbols             != NULL && // Check for pathological object
   1.527 +                that->fSymbols       != NULL && // Check for pathological object
   1.528 +                *fSymbols            == *that->fSymbols &&
   1.529 +                fHaveDefaultCentury  == that->fHaveDefaultCentury &&
   1.530 +                fDefaultCenturyStart == that->fDefaultCenturyStart &&
   1.531 +                fCapitalizationContext == that->fCapitalizationContext);
   1.532 +    }
   1.533 +    return FALSE;
   1.534 +}
   1.535 +
   1.536 +//----------------------------------------------------------------------
   1.537 +
   1.538 +void SimpleDateFormat::construct(EStyle timeStyle,
   1.539 +                                 EStyle dateStyle,
   1.540 +                                 const Locale& locale,
   1.541 +                                 UErrorCode& status)
   1.542 +{
   1.543 +    // called by several constructors to load pattern data from the resources
   1.544 +    if (U_FAILURE(status)) return;
   1.545 +
   1.546 +    // We will need the calendar to know what type of symbols to load.
   1.547 +    initializeCalendar(NULL, locale, status);
   1.548 +    if (U_FAILURE(status)) return;
   1.549 +
   1.550 +    CalendarData calData(locale, fCalendar?fCalendar->getType():NULL, status);
   1.551 +    UResourceBundle *dateTimePatterns = calData.getByKey(gDateTimePatternsTag, status);
   1.552 +    UResourceBundle *currentBundle;
   1.553 +
   1.554 +    if (U_FAILURE(status)) return;
   1.555 +
   1.556 +    if (ures_getSize(dateTimePatterns) <= kDateTime)
   1.557 +    {
   1.558 +        status = U_INVALID_FORMAT_ERROR;
   1.559 +        return;
   1.560 +    }
   1.561 +
   1.562 +    setLocaleIDs(ures_getLocaleByType(dateTimePatterns, ULOC_VALID_LOCALE, &status),
   1.563 +                 ures_getLocaleByType(dateTimePatterns, ULOC_ACTUAL_LOCALE, &status));
   1.564 +
   1.565 +    // create a symbols object from the locale
   1.566 +    initializeSymbols(locale,fCalendar, status);
   1.567 +    if (U_FAILURE(status)) return;
   1.568 +    /* test for NULL */
   1.569 +    if (fSymbols == 0) {
   1.570 +        status = U_MEMORY_ALLOCATION_ERROR;
   1.571 +        return;
   1.572 +    }
   1.573 +
   1.574 +    const UChar *resStr,*ovrStr;
   1.575 +    int32_t resStrLen,ovrStrLen = 0;
   1.576 +    fDateOverride.setToBogus();
   1.577 +    fTimeOverride.setToBogus();
   1.578 +
   1.579 +    // if the pattern should include both date and time information, use the date/time
   1.580 +    // pattern string as a guide to tell use how to glue together the appropriate date
   1.581 +    // and time pattern strings.  The actual gluing-together is handled by a convenience
   1.582 +    // method on MessageFormat.
   1.583 +    if ((timeStyle != kNone) && (dateStyle != kNone))
   1.584 +    {
   1.585 +        Formattable timeDateArray[2];
   1.586 +
   1.587 +        // use Formattable::adoptString() so that we can use fastCopyFrom()
   1.588 +        // instead of Formattable::setString()'s unaware, safe, deep string clone
   1.589 +        // see Jitterbug 2296
   1.590 +
   1.591 +        currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)timeStyle, NULL, &status);
   1.592 +        if (U_FAILURE(status)) {
   1.593 +           status = U_INVALID_FORMAT_ERROR;
   1.594 +           return;
   1.595 +        }
   1.596 +        switch (ures_getType(currentBundle)) {
   1.597 +            case URES_STRING: {
   1.598 +               resStr = ures_getString(currentBundle, &resStrLen, &status);
   1.599 +               break;
   1.600 +            }
   1.601 +            case URES_ARRAY: {
   1.602 +               resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status);
   1.603 +               ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status);
   1.604 +               fTimeOverride.setTo(TRUE, ovrStr, ovrStrLen);
   1.605 +               break;
   1.606 +            }
   1.607 +            default: {
   1.608 +               status = U_INVALID_FORMAT_ERROR;
   1.609 +               ures_close(currentBundle);
   1.610 +               return;
   1.611 +            }
   1.612 +        }
   1.613 +        ures_close(currentBundle);
   1.614 +
   1.615 +        UnicodeString *tempus1 = new UnicodeString(TRUE, resStr, resStrLen);
   1.616 +        // NULL pointer check
   1.617 +        if (tempus1 == NULL) {
   1.618 +            status = U_MEMORY_ALLOCATION_ERROR;
   1.619 +            return;
   1.620 +        }
   1.621 +        timeDateArray[0].adoptString(tempus1);
   1.622 +
   1.623 +        currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)dateStyle, NULL, &status);
   1.624 +        if (U_FAILURE(status)) {
   1.625 +           status = U_INVALID_FORMAT_ERROR;
   1.626 +           return;
   1.627 +        }
   1.628 +        switch (ures_getType(currentBundle)) {
   1.629 +            case URES_STRING: {
   1.630 +               resStr = ures_getString(currentBundle, &resStrLen, &status);
   1.631 +               break;
   1.632 +            }
   1.633 +            case URES_ARRAY: {
   1.634 +               resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status);
   1.635 +               ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status);
   1.636 +               fDateOverride.setTo(TRUE, ovrStr, ovrStrLen);
   1.637 +               break;
   1.638 +            }
   1.639 +            default: {
   1.640 +               status = U_INVALID_FORMAT_ERROR;
   1.641 +               ures_close(currentBundle);
   1.642 +               return;
   1.643 +            }
   1.644 +        }
   1.645 +        ures_close(currentBundle);
   1.646 +
   1.647 +        UnicodeString *tempus2 = new UnicodeString(TRUE, resStr, resStrLen);
   1.648 +        // Null pointer check
   1.649 +        if (tempus2 == NULL) {
   1.650 +            status = U_MEMORY_ALLOCATION_ERROR;
   1.651 +            return;
   1.652 +        }
   1.653 +        timeDateArray[1].adoptString(tempus2);
   1.654 +
   1.655 +        int32_t glueIndex = kDateTime;
   1.656 +        int32_t patternsSize = ures_getSize(dateTimePatterns);
   1.657 +        if (patternsSize >= (kDateTimeOffset + kShort + 1)) {
   1.658 +            // Get proper date time format
   1.659 +            glueIndex = (int32_t)(kDateTimeOffset + (dateStyle - kDateOffset));
   1.660 +        }
   1.661 +
   1.662 +        resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &status);
   1.663 +        MessageFormat::format(UnicodeString(TRUE, resStr, resStrLen), timeDateArray, 2, fPattern, status);
   1.664 +    }
   1.665 +    // if the pattern includes just time data or just date date, load the appropriate
   1.666 +    // pattern string from the resources
   1.667 +    // setTo() - see DateFormatSymbols::assignArray comments
   1.668 +    else if (timeStyle != kNone) {
   1.669 +        currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)timeStyle, NULL, &status);
   1.670 +        if (U_FAILURE(status)) {
   1.671 +           status = U_INVALID_FORMAT_ERROR;
   1.672 +           return;
   1.673 +        }
   1.674 +        switch (ures_getType(currentBundle)) {
   1.675 +            case URES_STRING: {
   1.676 +               resStr = ures_getString(currentBundle, &resStrLen, &status);
   1.677 +               break;
   1.678 +            }
   1.679 +            case URES_ARRAY: {
   1.680 +               resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status);
   1.681 +               ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status);
   1.682 +               fDateOverride.setTo(TRUE, ovrStr, ovrStrLen);
   1.683 +               break;
   1.684 +            }
   1.685 +            default: {
   1.686 +               status = U_INVALID_FORMAT_ERROR;
   1.687 +                ures_close(currentBundle);
   1.688 +               return;
   1.689 +            }
   1.690 +        }
   1.691 +        fPattern.setTo(TRUE, resStr, resStrLen);
   1.692 +        ures_close(currentBundle);
   1.693 +    }
   1.694 +    else if (dateStyle != kNone) {
   1.695 +        currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)dateStyle, NULL, &status);
   1.696 +        if (U_FAILURE(status)) {
   1.697 +           status = U_INVALID_FORMAT_ERROR;
   1.698 +           return;
   1.699 +        }
   1.700 +        switch (ures_getType(currentBundle)) {
   1.701 +            case URES_STRING: {
   1.702 +               resStr = ures_getString(currentBundle, &resStrLen, &status);
   1.703 +               break;
   1.704 +            }
   1.705 +            case URES_ARRAY: {
   1.706 +               resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status);
   1.707 +               ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status);
   1.708 +               fDateOverride.setTo(TRUE, ovrStr, ovrStrLen);
   1.709 +               break;
   1.710 +            }
   1.711 +            default: {
   1.712 +               status = U_INVALID_FORMAT_ERROR;
   1.713 +               ures_close(currentBundle);
   1.714 +               return;
   1.715 +            }
   1.716 +        }
   1.717 +        fPattern.setTo(TRUE, resStr, resStrLen);
   1.718 +        ures_close(currentBundle);
   1.719 +    }
   1.720 +
   1.721 +    // and if it includes _neither_, that's an error
   1.722 +    else
   1.723 +        status = U_INVALID_FORMAT_ERROR;
   1.724 +
   1.725 +    // finally, finish initializing by creating a Calendar and a NumberFormat
   1.726 +    initialize(locale, status);
   1.727 +}
   1.728 +
   1.729 +//----------------------------------------------------------------------
   1.730 +
   1.731 +Calendar*
   1.732 +SimpleDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
   1.733 +{
   1.734 +    if(!U_FAILURE(status)) {
   1.735 +        fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status);
   1.736 +    }
   1.737 +    if (U_SUCCESS(status) && fCalendar == NULL) {
   1.738 +        status = U_MEMORY_ALLOCATION_ERROR;
   1.739 +    }
   1.740 +    return fCalendar;
   1.741 +}
   1.742 +
   1.743 +void
   1.744 +SimpleDateFormat::initializeSymbols(const Locale& locale, Calendar* calendar, UErrorCode& status)
   1.745 +{
   1.746 +  if(U_FAILURE(status)) {
   1.747 +    fSymbols = NULL;
   1.748 +  } else {
   1.749 +    // pass in calendar type - use NULL (default) if no calendar set (or err).
   1.750 +    fSymbols = new DateFormatSymbols(locale, calendar?calendar->getType() :NULL , status);
   1.751 +    // Null pointer check
   1.752 +    if (fSymbols == NULL) {
   1.753 +        status = U_MEMORY_ALLOCATION_ERROR;
   1.754 +        return;
   1.755 +    }
   1.756 +  }
   1.757 +}
   1.758 +
   1.759 +void
   1.760 +SimpleDateFormat::initialize(const Locale& locale,
   1.761 +                             UErrorCode& status)
   1.762 +{
   1.763 +    if (U_FAILURE(status)) return;
   1.764 +
   1.765 +    // We don't need to check that the row count is >= 1, since all 2d arrays have at
   1.766 +    // least one row
   1.767 +    fNumberFormat = NumberFormat::createInstance(locale, status);
   1.768 +    if (fNumberFormat != NULL && U_SUCCESS(status))
   1.769 +    {
   1.770 +        // no matter what the locale's default number format looked like, we want
   1.771 +        // to modify it so that it doesn't use thousands separators, doesn't always
   1.772 +        // show the decimal point, and recognizes integers only when parsing
   1.773 +
   1.774 +        fNumberFormat->setGroupingUsed(FALSE);
   1.775 +        DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fNumberFormat);
   1.776 +        if (decfmt != NULL) {
   1.777 +            decfmt->setDecimalSeparatorAlwaysShown(FALSE);
   1.778 +        }
   1.779 +        fNumberFormat->setParseIntegerOnly(TRUE);
   1.780 +        fNumberFormat->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
   1.781 +
   1.782 +        //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
   1.783 +
   1.784 +        initNumberFormatters(locale,status);
   1.785 +
   1.786 +    }
   1.787 +    else if (U_SUCCESS(status))
   1.788 +    {
   1.789 +        status = U_MISSING_RESOURCE_ERROR;
   1.790 +    }
   1.791 +}
   1.792 +
   1.793 +/* Initialize the fields we use to disambiguate ambiguous years. Separate
   1.794 + * so we can call it from readObject().
   1.795 + */
   1.796 +void SimpleDateFormat::initializeDefaultCentury()
   1.797 +{
   1.798 +  if(fCalendar) {
   1.799 +    fHaveDefaultCentury = fCalendar->haveDefaultCentury();
   1.800 +    if(fHaveDefaultCentury) {
   1.801 +      fDefaultCenturyStart = fCalendar->defaultCenturyStart();
   1.802 +      fDefaultCenturyStartYear = fCalendar->defaultCenturyStartYear();
   1.803 +    } else {
   1.804 +      fDefaultCenturyStart = DBL_MIN;
   1.805 +      fDefaultCenturyStartYear = -1;
   1.806 +    }
   1.807 +  }
   1.808 +}
   1.809 +
   1.810 +/* Define one-century window into which to disambiguate dates using
   1.811 + * two-digit years. Make public in JDK 1.2.
   1.812 + */
   1.813 +void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status)
   1.814 +{
   1.815 +    if(U_FAILURE(status)) {
   1.816 +        return;
   1.817 +    }
   1.818 +    if(!fCalendar) {
   1.819 +      status = U_ILLEGAL_ARGUMENT_ERROR;
   1.820 +      return;
   1.821 +    }
   1.822 +
   1.823 +    fCalendar->setTime(startDate, status);
   1.824 +    if(U_SUCCESS(status)) {
   1.825 +        fHaveDefaultCentury = TRUE;
   1.826 +        fDefaultCenturyStart = startDate;
   1.827 +        fDefaultCenturyStartYear = fCalendar->get(UCAL_YEAR, status);
   1.828 +    }
   1.829 +}
   1.830 +
   1.831 +//----------------------------------------------------------------------
   1.832 +
   1.833 +UnicodeString&
   1.834 +SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const
   1.835 +{
   1.836 +  UErrorCode status = U_ZERO_ERROR;
   1.837 +  FieldPositionOnlyHandler handler(pos);
   1.838 +  return _format(cal, appendTo, handler, status);
   1.839 +}
   1.840 +
   1.841 +//----------------------------------------------------------------------
   1.842 +
   1.843 +UnicodeString&
   1.844 +SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo,
   1.845 +                         FieldPositionIterator* posIter, UErrorCode& status) const
   1.846 +{
   1.847 +  FieldPositionIteratorHandler handler(posIter, status);
   1.848 +  return _format(cal, appendTo, handler, status);
   1.849 +}
   1.850 +
   1.851 +//----------------------------------------------------------------------
   1.852 +
   1.853 +UnicodeString&
   1.854 +SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo,
   1.855 +                            FieldPositionHandler& handler, UErrorCode& status) const
   1.856 +{
   1.857 +    if ( U_FAILURE(status) ) {
   1.858 +       return appendTo; 
   1.859 +    }
   1.860 +    Calendar* workCal = &cal;
   1.861 +    Calendar* calClone = NULL;
   1.862 +    if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) {
   1.863 +        // Different calendar type
   1.864 +        // We use the time and time zone from the input calendar, but
   1.865 +        // do not use the input calendar for field calculation.
   1.866 +        calClone = fCalendar->clone();
   1.867 +        if (calClone != NULL) {
   1.868 +            UDate t = cal.getTime(status);
   1.869 +            calClone->setTime(t, status);
   1.870 +            calClone->setTimeZone(cal.getTimeZone());
   1.871 +            workCal = calClone;
   1.872 +        } else {
   1.873 +            status = U_MEMORY_ALLOCATION_ERROR;
   1.874 +            return appendTo;
   1.875 +        }
   1.876 +    }
   1.877 +
   1.878 +    UBool inQuote = FALSE;
   1.879 +    UChar prevCh = 0;
   1.880 +    int32_t count = 0;
   1.881 +    int32_t fieldNum = 0;
   1.882 +
   1.883 +    // loop through the pattern string character by character
   1.884 +    for (int32_t i = 0; i < fPattern.length() && U_SUCCESS(status); ++i) {
   1.885 +        UChar ch = fPattern[i];
   1.886 +
   1.887 +        // Use subFormat() to format a repeated pattern character
   1.888 +        // when a different pattern or non-pattern character is seen
   1.889 +        if (ch != prevCh && count > 0) {
   1.890 +            subFormat(appendTo, prevCh, count, fCapitalizationContext, fieldNum++, handler, *workCal, status);
   1.891 +            count = 0;
   1.892 +        }
   1.893 +        if (ch == QUOTE) {
   1.894 +            // Consecutive single quotes are a single quote literal,
   1.895 +            // either outside of quotes or between quotes
   1.896 +            if ((i+1) < fPattern.length() && fPattern[i+1] == QUOTE) {
   1.897 +                appendTo += (UChar)QUOTE;
   1.898 +                ++i;
   1.899 +            } else {
   1.900 +                inQuote = ! inQuote;
   1.901 +            }
   1.902 +        }
   1.903 +        else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
   1.904 +                    || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
   1.905 +            // ch is a date-time pattern character to be interpreted
   1.906 +            // by subFormat(); count the number of times it is repeated
   1.907 +            prevCh = ch;
   1.908 +            ++count;
   1.909 +        }
   1.910 +        else {
   1.911 +            // Append quoted characters and unquoted non-pattern characters
   1.912 +            appendTo += ch;
   1.913 +        }
   1.914 +    }
   1.915 +
   1.916 +    // Format the last item in the pattern, if any
   1.917 +    if (count > 0) {
   1.918 +        subFormat(appendTo, prevCh, count, fCapitalizationContext, fieldNum++, handler, *workCal, status);
   1.919 +    }
   1.920 +
   1.921 +    if (calClone != NULL) {
   1.922 +        delete calClone;
   1.923 +    }
   1.924 +
   1.925 +    return appendTo;
   1.926 +}
   1.927 +
   1.928 +//----------------------------------------------------------------------
   1.929 +
   1.930 +/* Map calendar field into calendar field level.
   1.931 + * the larger the level, the smaller the field unit.
   1.932 + * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
   1.933 + * UCAL_MONTH level is 20.
   1.934 + * NOTE: if new fields adds in, the table needs to update.
   1.935 + */
   1.936 +const int32_t
   1.937 +SimpleDateFormat::fgCalendarFieldToLevel[] =
   1.938 +{
   1.939 +    /*GyM*/ 0, 10, 20,
   1.940 +    /*wW*/ 20, 30,
   1.941 +    /*dDEF*/ 30, 20, 30, 30,
   1.942 +    /*ahHm*/ 40, 50, 50, 60,
   1.943 +    /*sS..*/ 70, 80,
   1.944 +    /*z?Y*/ 0, 0, 10,
   1.945 +    /*eug*/ 30, 10, 0,
   1.946 +    /*A*/ 40
   1.947 +};
   1.948 +
   1.949 +
   1.950 +/* Map calendar field LETTER into calendar field level.
   1.951 + * the larger the level, the smaller the field unit.
   1.952 + * NOTE: if new fields adds in, the table needs to update.
   1.953 + */
   1.954 +const int32_t
   1.955 +SimpleDateFormat::fgPatternCharToLevel[] = {
   1.956 +    //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
   1.957 +        -1, 40, -1, -1, 20, 30, 30,  0, 50, -1, -1, 50, 20, 20, -1,  0,
   1.958 +    //   P   Q   R   S   T   U   V   W   X   Y   Z
   1.959 +        -1, 20, -1, 80, -1, 10,  0, 30,  0, 10,  0, -1, -1, -1, -1, -1,
   1.960 +    //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
   1.961 +        -1, 40, -1, 30, 30, 30, -1,  0, 50, -1, -1, 50, -1, 60, -1, -1,
   1.962 +    //   p   q   r   s   t   u   v   w   x   y   z
   1.963 +        -1, 20, -1, 70, -1, 10,  0, 20,  0, 10,  0, -1, -1, -1, -1, -1
   1.964 +};
   1.965 +
   1.966 +
   1.967 +// Map index into pattern character string to Calendar field number.
   1.968 +const UCalendarDateFields
   1.969 +SimpleDateFormat::fgPatternIndexToCalendarField[] =
   1.970 +{
   1.971 +    /*GyM*/ UCAL_ERA, UCAL_YEAR, UCAL_MONTH,
   1.972 +    /*dkH*/ UCAL_DATE, UCAL_HOUR_OF_DAY, UCAL_HOUR_OF_DAY,
   1.973 +    /*msS*/ UCAL_MINUTE, UCAL_SECOND, UCAL_MILLISECOND,
   1.974 +    /*EDF*/ UCAL_DAY_OF_WEEK, UCAL_DAY_OF_YEAR, UCAL_DAY_OF_WEEK_IN_MONTH,
   1.975 +    /*wWa*/ UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, UCAL_AM_PM,
   1.976 +    /*hKz*/ UCAL_HOUR, UCAL_HOUR, UCAL_ZONE_OFFSET,
   1.977 +    /*Yeu*/ UCAL_YEAR_WOY, UCAL_DOW_LOCAL, UCAL_EXTENDED_YEAR,
   1.978 +    /*gAZ*/ UCAL_JULIAN_DAY, UCAL_MILLISECONDS_IN_DAY, UCAL_ZONE_OFFSET,
   1.979 +    /*v*/   UCAL_ZONE_OFFSET,
   1.980 +    /*c*/   UCAL_DOW_LOCAL,
   1.981 +    /*L*/   UCAL_MONTH,
   1.982 +    /*Q*/   UCAL_MONTH,
   1.983 +    /*q*/   UCAL_MONTH,
   1.984 +    /*V*/   UCAL_ZONE_OFFSET,
   1.985 +    /*U*/   UCAL_YEAR,
   1.986 +    /*O*/   UCAL_ZONE_OFFSET,
   1.987 +    /*Xx*/  UCAL_ZONE_OFFSET, UCAL_ZONE_OFFSET,
   1.988 +};
   1.989 +
   1.990 +// Map index into pattern character string to DateFormat field number
   1.991 +const UDateFormatField
   1.992 +SimpleDateFormat::fgPatternIndexToDateFormatField[] = {
   1.993 +    /*GyM*/ UDAT_ERA_FIELD, UDAT_YEAR_FIELD, UDAT_MONTH_FIELD,
   1.994 +    /*dkH*/ UDAT_DATE_FIELD, UDAT_HOUR_OF_DAY1_FIELD, UDAT_HOUR_OF_DAY0_FIELD,
   1.995 +    /*msS*/ UDAT_MINUTE_FIELD, UDAT_SECOND_FIELD, UDAT_FRACTIONAL_SECOND_FIELD,
   1.996 +    /*EDF*/ UDAT_DAY_OF_WEEK_FIELD, UDAT_DAY_OF_YEAR_FIELD, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD,
   1.997 +    /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD, UDAT_WEEK_OF_MONTH_FIELD, UDAT_AM_PM_FIELD,
   1.998 +    /*hKz*/ UDAT_HOUR1_FIELD, UDAT_HOUR0_FIELD, UDAT_TIMEZONE_FIELD,
   1.999 +    /*Yeu*/ UDAT_YEAR_WOY_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_EXTENDED_YEAR_FIELD,
  1.1000 +    /*gAZ*/ UDAT_JULIAN_DAY_FIELD, UDAT_MILLISECONDS_IN_DAY_FIELD, UDAT_TIMEZONE_RFC_FIELD,
  1.1001 +    /*v*/   UDAT_TIMEZONE_GENERIC_FIELD,
  1.1002 +    /*c*/   UDAT_STANDALONE_DAY_FIELD,
  1.1003 +    /*L*/   UDAT_STANDALONE_MONTH_FIELD,
  1.1004 +    /*Q*/   UDAT_QUARTER_FIELD,
  1.1005 +    /*q*/   UDAT_STANDALONE_QUARTER_FIELD,
  1.1006 +    /*V*/   UDAT_TIMEZONE_SPECIAL_FIELD,
  1.1007 +    /*U*/   UDAT_YEAR_NAME_FIELD,
  1.1008 +    /*O*/   UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD,
  1.1009 +    /*Xx*/  UDAT_TIMEZONE_ISO_FIELD, UDAT_TIMEZONE_ISO_LOCAL_FIELD,
  1.1010 +};
  1.1011 +
  1.1012 +//----------------------------------------------------------------------
  1.1013 +
  1.1014 +/**
  1.1015 + * Append symbols[value] to dst.  Make sure the array index is not out
  1.1016 + * of bounds.
  1.1017 + */
  1.1018 +static inline void
  1.1019 +_appendSymbol(UnicodeString& dst,
  1.1020 +              int32_t value,
  1.1021 +              const UnicodeString* symbols,
  1.1022 +              int32_t symbolsCount) {
  1.1023 +    U_ASSERT(0 <= value && value < symbolsCount);
  1.1024 +    if (0 <= value && value < symbolsCount) {
  1.1025 +        dst += symbols[value];
  1.1026 +    }
  1.1027 +}
  1.1028 +
  1.1029 +static inline void
  1.1030 +_appendSymbolWithMonthPattern(UnicodeString& dst, int32_t value, const UnicodeString* symbols, int32_t symbolsCount,
  1.1031 +              const UnicodeString* monthPattern, UErrorCode& status) {
  1.1032 +    U_ASSERT(0 <= value && value < symbolsCount);
  1.1033 +    if (0 <= value && value < symbolsCount) {
  1.1034 +        if (monthPattern == NULL) {
  1.1035 +            dst += symbols[value];
  1.1036 +        } else {
  1.1037 +            Formattable monthName((const UnicodeString&)(symbols[value]));
  1.1038 +            MessageFormat::format(*monthPattern, &monthName, 1, dst, status);
  1.1039 +        }
  1.1040 +    }
  1.1041 +}
  1.1042 +
  1.1043 +//----------------------------------------------------------------------
  1.1044 +void
  1.1045 +SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) {
  1.1046 +    if (U_FAILURE(status)) {
  1.1047 +        return;
  1.1048 +    }
  1.1049 +    if ( fDateOverride.isBogus() && fTimeOverride.isBogus() ) {
  1.1050 +        return;
  1.1051 +    }
  1.1052 +    umtx_lock(&LOCK);
  1.1053 +    if (fNumberFormatters == NULL) {
  1.1054 +        fNumberFormatters = (NumberFormat**)uprv_malloc(UDAT_FIELD_COUNT * sizeof(NumberFormat*));
  1.1055 +        if (fNumberFormatters) {
  1.1056 +            for (int32_t i = 0; i < UDAT_FIELD_COUNT; i++) {
  1.1057 +                fNumberFormatters[i] = fNumberFormat;
  1.1058 +            }
  1.1059 +        } else {
  1.1060 +            status = U_MEMORY_ALLOCATION_ERROR;
  1.1061 +        }
  1.1062 +    }
  1.1063 +    umtx_unlock(&LOCK);
  1.1064 +
  1.1065 +    processOverrideString(locale,fDateOverride,kOvrStrDate,status);
  1.1066 +    processOverrideString(locale,fTimeOverride,kOvrStrTime,status);
  1.1067 +
  1.1068 +}
  1.1069 +
  1.1070 +void
  1.1071 +SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeString &str, int8_t type, UErrorCode &status) {
  1.1072 +    if (str.isBogus()) {
  1.1073 +        return;
  1.1074 +    }
  1.1075 +    int32_t start = 0;
  1.1076 +    int32_t len;
  1.1077 +    UnicodeString nsName;
  1.1078 +    UnicodeString ovrField;
  1.1079 +    UBool moreToProcess = TRUE;
  1.1080 +
  1.1081 +    while (moreToProcess) {
  1.1082 +        int32_t delimiterPosition = str.indexOf((UChar)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE,start);
  1.1083 +        if (delimiterPosition == -1) {
  1.1084 +            moreToProcess = FALSE;
  1.1085 +            len = str.length() - start;
  1.1086 +        } else {
  1.1087 +            len = delimiterPosition - start;
  1.1088 +        }
  1.1089 +        UnicodeString currentString(str,start,len);
  1.1090 +        int32_t equalSignPosition = currentString.indexOf((UChar)ULOC_KEYWORD_ASSIGN_UNICODE,0);
  1.1091 +        if (equalSignPosition == -1) { // Simple override string such as "hebrew"
  1.1092 +            nsName.setTo(currentString);
  1.1093 +            ovrField.setToBogus();
  1.1094 +        } else { // Field specific override string such as "y=hebrew"
  1.1095 +            nsName.setTo(currentString,equalSignPosition+1);
  1.1096 +            ovrField.setTo(currentString,0,1); // We just need the first character.
  1.1097 +        }
  1.1098 +
  1.1099 +        int32_t nsNameHash = nsName.hashCode();
  1.1100 +        // See if the numbering system is in the override list, if not, then add it.
  1.1101 +        NSOverride *cur = fOverrideList;
  1.1102 +        NumberFormat *nf = NULL;
  1.1103 +        UBool found = FALSE;
  1.1104 +        while ( cur && !found ) {
  1.1105 +            if ( cur->hash == nsNameHash ) {
  1.1106 +                nf = cur->nf;
  1.1107 +                found = TRUE;
  1.1108 +            }
  1.1109 +            cur = cur->next;
  1.1110 +        }
  1.1111 +
  1.1112 +        if (!found) {
  1.1113 +           cur = (NSOverride *)uprv_malloc(sizeof(NSOverride));
  1.1114 +           if (cur) {
  1.1115 +               char kw[ULOC_KEYWORD_AND_VALUES_CAPACITY];
  1.1116 +               uprv_strcpy(kw,"numbers=");
  1.1117 +               nsName.extract(0,len,kw+8,ULOC_KEYWORD_AND_VALUES_CAPACITY-8,US_INV);
  1.1118 +
  1.1119 +               Locale ovrLoc(locale.getLanguage(),locale.getCountry(),locale.getVariant(),kw);
  1.1120 +               nf = NumberFormat::createInstance(ovrLoc,status);
  1.1121 +
  1.1122 +               // no matter what the locale's default number format looked like, we want
  1.1123 +               // to modify it so that it doesn't use thousands separators, doesn't always
  1.1124 +               // show the decimal point, and recognizes integers only when parsing
  1.1125 +
  1.1126 +               if (U_SUCCESS(status)) {
  1.1127 +                   nf->setGroupingUsed(FALSE);
  1.1128 +                   DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(nf);
  1.1129 +                   if (decfmt != NULL) {
  1.1130 +                       decfmt->setDecimalSeparatorAlwaysShown(FALSE);
  1.1131 +                   }
  1.1132 +                   nf->setParseIntegerOnly(TRUE);
  1.1133 +                   nf->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
  1.1134 +
  1.1135 +                   cur->nf = nf;
  1.1136 +                   cur->hash = nsNameHash;
  1.1137 +                   cur->next = fOverrideList;
  1.1138 +                   fOverrideList = cur;
  1.1139 +               }
  1.1140 +               else {
  1.1141 +                   // clean up before returning
  1.1142 +                   if (cur != NULL) {
  1.1143 +                       uprv_free(cur);
  1.1144 +                   }
  1.1145 +                  return;
  1.1146 +               }
  1.1147 +
  1.1148 +           } else {
  1.1149 +               status = U_MEMORY_ALLOCATION_ERROR;
  1.1150 +               return;
  1.1151 +           }
  1.1152 +        }
  1.1153 +
  1.1154 +        // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
  1.1155 +        // number formatters table.
  1.1156 +
  1.1157 +        if (ovrField.isBogus()) {
  1.1158 +            switch (type) {
  1.1159 +                case kOvrStrDate:
  1.1160 +                case kOvrStrBoth: {
  1.1161 +                    for ( int8_t i=0 ; i<kDateFieldsCount; i++ ) {
  1.1162 +                        fNumberFormatters[kDateFields[i]] = nf;
  1.1163 +                    }
  1.1164 +                    if (type==kOvrStrDate) {
  1.1165 +                        break;
  1.1166 +                    }
  1.1167 +                }
  1.1168 +                case kOvrStrTime : {
  1.1169 +                    for ( int8_t i=0 ; i<kTimeFieldsCount; i++ ) {
  1.1170 +                        fNumberFormatters[kTimeFields[i]] = nf;
  1.1171 +                    }
  1.1172 +                    break;
  1.1173 +                }
  1.1174 +            }
  1.1175 +        } else {
  1.1176 +           // if the pattern character is unrecognized, signal an error and bail out
  1.1177 +           UDateFormatField patternCharIndex =
  1.1178 +              DateFormatSymbols::getPatternCharIndex(ovrField.charAt(0));
  1.1179 +           if (patternCharIndex == UDAT_FIELD_COUNT) {
  1.1180 +               status = U_INVALID_FORMAT_ERROR;
  1.1181 +               return;
  1.1182 +           }
  1.1183 +
  1.1184 +           // Set the number formatter in the table
  1.1185 +           fNumberFormatters[patternCharIndex] = nf;
  1.1186 +        }
  1.1187 +
  1.1188 +        start = delimiterPosition + 1;
  1.1189 +    }
  1.1190 +}
  1.1191 +
  1.1192 +//---------------------------------------------------------------------
  1.1193 +void
  1.1194 +SimpleDateFormat::subFormat(UnicodeString &appendTo,
  1.1195 +                            UChar ch,
  1.1196 +                            int32_t count,
  1.1197 +                            UDisplayContext capitalizationContext,
  1.1198 +                            int32_t fieldNum,
  1.1199 +                            FieldPositionHandler& handler,
  1.1200 +                            Calendar& cal,
  1.1201 +                            UErrorCode& status) const
  1.1202 +{
  1.1203 +    if (U_FAILURE(status)) {
  1.1204 +        return;
  1.1205 +    }
  1.1206 +
  1.1207 +    // this function gets called by format() to produce the appropriate substitution
  1.1208 +    // text for an individual pattern symbol (e.g., "HH" or "yyyy")
  1.1209 +
  1.1210 +    UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
  1.1211 +    const int32_t maxIntCount = 10;
  1.1212 +    int32_t beginOffset = appendTo.length();
  1.1213 +    NumberFormat *currentNumberFormat;
  1.1214 +    DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType = DateFormatSymbols::kCapContextUsageOther;
  1.1215 +
  1.1216 +    UBool isHebrewCalendar = (uprv_strcmp(cal.getType(),"hebrew") == 0);
  1.1217 +    UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0 || uprv_strcmp(cal.getType(),"dangi") == 0);
  1.1218 +
  1.1219 +    // if the pattern character is unrecognized, signal an error and dump out
  1.1220 +    if (patternCharIndex == UDAT_FIELD_COUNT)
  1.1221 +    {
  1.1222 +        if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
  1.1223 +            status = U_INVALID_FORMAT_ERROR;
  1.1224 +        }
  1.1225 +        return;
  1.1226 +    }
  1.1227 +
  1.1228 +    UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
  1.1229 +    int32_t value = cal.get(field, status);
  1.1230 +    if (U_FAILURE(status)) {
  1.1231 +        return;
  1.1232 +    }
  1.1233 +
  1.1234 +    currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
  1.1235 +    UnicodeString hebr("hebr", 4, US_INV);
  1.1236 +    
  1.1237 +    switch (patternCharIndex) {
  1.1238 +
  1.1239 +    // for any "G" symbol, write out the appropriate era string
  1.1240 +    // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
  1.1241 +    case UDAT_ERA_FIELD:
  1.1242 +        if (isChineseCalendar) {
  1.1243 +            zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, 9); // as in ICU4J
  1.1244 +        } else {
  1.1245 +            if (count == 5) {
  1.1246 +                _appendSymbol(appendTo, value, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount);
  1.1247 +                capContextUsageType = DateFormatSymbols::kCapContextUsageEraNarrow;
  1.1248 +            } else if (count == 4) {
  1.1249 +                _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount);
  1.1250 +                capContextUsageType = DateFormatSymbols::kCapContextUsageEraWide;
  1.1251 +            } else {
  1.1252 +                _appendSymbol(appendTo, value, fSymbols->fEras, fSymbols->fErasCount);
  1.1253 +                capContextUsageType = DateFormatSymbols::kCapContextUsageEraAbbrev;
  1.1254 +            }
  1.1255 +        }
  1.1256 +        break;
  1.1257 +
  1.1258 +     case UDAT_YEAR_NAME_FIELD:
  1.1259 +        if (fSymbols->fShortYearNames != NULL && value <= fSymbols->fShortYearNamesCount) {
  1.1260 +            // the Calendar YEAR field runs 1 through 60 for cyclic years
  1.1261 +            _appendSymbol(appendTo, value - 1, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount);
  1.1262 +            break;
  1.1263 +        }
  1.1264 +        // else fall through to numeric year handling, do not break here
  1.1265 +
  1.1266 +   // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
  1.1267 +    // NEW: UTS#35:
  1.1268 +//Year         y     yy     yyy     yyyy     yyyyy
  1.1269 +//AD 1         1     01     001     0001     00001
  1.1270 +//AD 12       12     12     012     0012     00012
  1.1271 +//AD 123     123     23     123     0123     00123
  1.1272 +//AD 1234   1234     34    1234     1234     01234
  1.1273 +//AD 12345 12345     45   12345    12345     12345
  1.1274 +    case UDAT_YEAR_FIELD:
  1.1275 +    case UDAT_YEAR_WOY_FIELD:
  1.1276 +        if (fDateOverride.compare(hebr)==0 && value>HEBREW_CAL_CUR_MILLENIUM_START_YEAR && value<HEBREW_CAL_CUR_MILLENIUM_END_YEAR) {
  1.1277 +            value-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
  1.1278 +        }
  1.1279 +        if(count == 2)
  1.1280 +            zeroPaddingNumber(currentNumberFormat, appendTo, value, 2, 2);
  1.1281 +        else
  1.1282 +            zeroPaddingNumber(currentNumberFormat, appendTo, value, count, maxIntCount);
  1.1283 +        break;
  1.1284 +
  1.1285 +    // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
  1.1286 +    // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
  1.1287 +    // appropriate number of digits
  1.1288 +    // for "MMMMM"/"LLLLL", use the narrow form
  1.1289 +    case UDAT_MONTH_FIELD:
  1.1290 +    case UDAT_STANDALONE_MONTH_FIELD:
  1.1291 +        if ( isHebrewCalendar ) {
  1.1292 +           HebrewCalendar *hc = (HebrewCalendar*)&cal;
  1.1293 +           if (hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value == 6 && count >= 3 )
  1.1294 +               value = 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
  1.1295 +           if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6 && count < 3 )
  1.1296 +               value--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
  1.1297 +        }
  1.1298 +        {
  1.1299 +            int32_t isLeapMonth = (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount)?
  1.1300 +                        cal.get(UCAL_IS_LEAP_MONTH, status): 0;
  1.1301 +            // should consolidate the next section by using arrays of pointers & counts for the right symbols...
  1.1302 +            if (count == 5) {
  1.1303 +                if (patternCharIndex == UDAT_MONTH_FIELD) {
  1.1304 +                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fNarrowMonths, fSymbols->fNarrowMonthsCount,
  1.1305 +                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatNarrow]): NULL, status);
  1.1306 +                } else {
  1.1307 +                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneNarrowMonths, fSymbols->fStandaloneNarrowMonthsCount,
  1.1308 +                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow]): NULL, status);
  1.1309 +                }
  1.1310 +                capContextUsageType = DateFormatSymbols::kCapContextUsageMonthNarrow;
  1.1311 +            } else if (count == 4) {
  1.1312 +                if (patternCharIndex == UDAT_MONTH_FIELD) {
  1.1313 +                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fMonths, fSymbols->fMonthsCount,
  1.1314 +                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide]): NULL, status);
  1.1315 +                    capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat;
  1.1316 +                } else {
  1.1317 +                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount,
  1.1318 +                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide]): NULL, status);
  1.1319 +                    capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone;
  1.1320 +                }
  1.1321 +            } else if (count == 3) {
  1.1322 +                if (patternCharIndex == UDAT_MONTH_FIELD) {
  1.1323 +                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fShortMonths, fSymbols->fShortMonthsCount,
  1.1324 +                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev]): NULL, status);
  1.1325 +                    capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat;
  1.1326 +                } else {
  1.1327 +                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount,
  1.1328 +                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev]): NULL, status);
  1.1329 +                    capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone;
  1.1330 +                }
  1.1331 +            } else {
  1.1332 +                UnicodeString monthNumber;
  1.1333 +                zeroPaddingNumber(currentNumberFormat,monthNumber, value + 1, count, maxIntCount);
  1.1334 +                _appendSymbolWithMonthPattern(appendTo, 0, &monthNumber, 1,
  1.1335 +                        (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric]): NULL, status);
  1.1336 +            }
  1.1337 +        }
  1.1338 +        break;
  1.1339 +
  1.1340 +    // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
  1.1341 +    case UDAT_HOUR_OF_DAY1_FIELD:
  1.1342 +        if (value == 0)
  1.1343 +            zeroPaddingNumber(currentNumberFormat,appendTo, cal.getMaximum(UCAL_HOUR_OF_DAY) + 1, count, maxIntCount);
  1.1344 +        else
  1.1345 +            zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
  1.1346 +        break;
  1.1347 +
  1.1348 +    case UDAT_FRACTIONAL_SECOND_FIELD:
  1.1349 +        // Fractional seconds left-justify
  1.1350 +        {
  1.1351 +            currentNumberFormat->setMinimumIntegerDigits((count > 3) ? 3 : count);
  1.1352 +            currentNumberFormat->setMaximumIntegerDigits(maxIntCount);
  1.1353 +            if (count == 1) {
  1.1354 +                value /= 100;
  1.1355 +            } else if (count == 2) {
  1.1356 +                value /= 10;
  1.1357 +            }
  1.1358 +            FieldPosition p(0);
  1.1359 +            currentNumberFormat->format(value, appendTo, p);
  1.1360 +            if (count > 3) {
  1.1361 +                currentNumberFormat->setMinimumIntegerDigits(count - 3);
  1.1362 +                currentNumberFormat->format((int32_t)0, appendTo, p);
  1.1363 +            }
  1.1364 +        }
  1.1365 +        break;
  1.1366 +
  1.1367 +    // for "ee" or "e", use local numeric day-of-the-week
  1.1368 +    // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name
  1.1369 +    // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
  1.1370 +    // for "EEEE" or "eeee", write out the wide day-of-the-week name
  1.1371 +    // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
  1.1372 +    case UDAT_DOW_LOCAL_FIELD:
  1.1373 +        if ( count < 3 ) {
  1.1374 +            zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
  1.1375 +            break;
  1.1376 +        }
  1.1377 +        // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
  1.1378 +        // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
  1.1379 +        value = cal.get(UCAL_DAY_OF_WEEK, status);
  1.1380 +        if (U_FAILURE(status)) {
  1.1381 +            return;
  1.1382 +        }
  1.1383 +        // fall through, do not break here
  1.1384 +    case UDAT_DAY_OF_WEEK_FIELD:
  1.1385 +        if (count == 5) {
  1.1386 +            _appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays,
  1.1387 +                          fSymbols->fNarrowWeekdaysCount);
  1.1388 +            capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow;
  1.1389 +        } else if (count == 4) {
  1.1390 +            _appendSymbol(appendTo, value, fSymbols->fWeekdays,
  1.1391 +                          fSymbols->fWeekdaysCount);
  1.1392 +            capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
  1.1393 +        } else if (count == 6) {
  1.1394 +            _appendSymbol(appendTo, value, fSymbols->fShorterWeekdays,
  1.1395 +                          fSymbols->fShorterWeekdaysCount);
  1.1396 +            capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
  1.1397 +        } else {
  1.1398 +            _appendSymbol(appendTo, value, fSymbols->fShortWeekdays,
  1.1399 +                          fSymbols->fShortWeekdaysCount);
  1.1400 +            capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
  1.1401 +        }
  1.1402 +        break;
  1.1403 +
  1.1404 +    // for "ccc", write out the abbreviated day-of-the-week name
  1.1405 +    // for "cccc", write out the wide day-of-the-week name
  1.1406 +    // for "ccccc", use the narrow day-of-the-week name
  1.1407 +    // for "ccccc", use the short day-of-the-week name
  1.1408 +    case UDAT_STANDALONE_DAY_FIELD:
  1.1409 +        if ( count < 3 ) {
  1.1410 +            zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, maxIntCount);
  1.1411 +            break;
  1.1412 +        }
  1.1413 +        // fall through to alpha DOW handling, but for that we don't want local day-of-week,
  1.1414 +        // we want standard day-of-week, so first fix value.
  1.1415 +        value = cal.get(UCAL_DAY_OF_WEEK, status);
  1.1416 +        if (U_FAILURE(status)) {
  1.1417 +            return;
  1.1418 +        }
  1.1419 +        if (count == 5) {
  1.1420 +            _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowWeekdays,
  1.1421 +                          fSymbols->fStandaloneNarrowWeekdaysCount);
  1.1422 +            capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow;
  1.1423 +        } else if (count == 4) {
  1.1424 +            _appendSymbol(appendTo, value, fSymbols->fStandaloneWeekdays,
  1.1425 +                          fSymbols->fStandaloneWeekdaysCount);
  1.1426 +            capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
  1.1427 +        } else if (count == 6) {
  1.1428 +            _appendSymbol(appendTo, value, fSymbols->fStandaloneShorterWeekdays,
  1.1429 +                          fSymbols->fStandaloneShorterWeekdaysCount);
  1.1430 +            capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
  1.1431 +        } else { // count == 3
  1.1432 +            _appendSymbol(appendTo, value, fSymbols->fStandaloneShortWeekdays,
  1.1433 +                          fSymbols->fStandaloneShortWeekdaysCount);
  1.1434 +            capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
  1.1435 +        }
  1.1436 +        break;
  1.1437 +
  1.1438 +    // for and "a" symbol, write out the whole AM/PM string
  1.1439 +    case UDAT_AM_PM_FIELD:
  1.1440 +        _appendSymbol(appendTo, value, fSymbols->fAmPms,
  1.1441 +                      fSymbols->fAmPmsCount);
  1.1442 +        break;
  1.1443 +
  1.1444 +    // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
  1.1445 +    // as "12"
  1.1446 +    case UDAT_HOUR1_FIELD:
  1.1447 +        if (value == 0)
  1.1448 +            zeroPaddingNumber(currentNumberFormat,appendTo, cal.getLeastMaximum(UCAL_HOUR) + 1, count, maxIntCount);
  1.1449 +        else
  1.1450 +            zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
  1.1451 +        break;
  1.1452 +
  1.1453 +    case UDAT_TIMEZONE_FIELD: // 'z'
  1.1454 +    case UDAT_TIMEZONE_RFC_FIELD: // 'Z'
  1.1455 +    case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
  1.1456 +    case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
  1.1457 +    case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O'
  1.1458 +    case UDAT_TIMEZONE_ISO_FIELD: // 'X'
  1.1459 +    case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x'
  1.1460 +        {
  1.1461 +            UnicodeString zoneString;
  1.1462 +            const TimeZone& tz = cal.getTimeZone();
  1.1463 +            UDate date = cal.getTime(status);
  1.1464 +            if (U_SUCCESS(status)) {
  1.1465 +                if (patternCharIndex == UDAT_TIMEZONE_FIELD) {
  1.1466 +                    if (count < 4) {
  1.1467 +                        // "z", "zz", "zzz"
  1.1468 +                        tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT, tz, date, zoneString);
  1.1469 +                        capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
  1.1470 +                    } else {
  1.1471 +                        // "zzzz" or longer
  1.1472 +                        tzFormat()->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, date, zoneString);
  1.1473 +                        capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong;
  1.1474 +                    }
  1.1475 +                }
  1.1476 +                else if (patternCharIndex == UDAT_TIMEZONE_RFC_FIELD) {
  1.1477 +                    if (count < 4) {
  1.1478 +                        // "Z"
  1.1479 +                        tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString);
  1.1480 +                    } else if (count == 5) {
  1.1481 +                        // "ZZZZZ"
  1.1482 +                        tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString);
  1.1483 +                    } else {
  1.1484 +                        // "ZZ", "ZZZ", "ZZZZ"
  1.1485 +                        tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString);
  1.1486 +                    }
  1.1487 +                }
  1.1488 +                else if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
  1.1489 +                    if (count == 1) {
  1.1490 +                        // "v"
  1.1491 +                        tzFormat()->format(UTZFMT_STYLE_GENERIC_SHORT, tz, date, zoneString);
  1.1492 +                        capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
  1.1493 +                    } else if (count == 4) {
  1.1494 +                        // "vvvv"
  1.1495 +                        tzFormat()->format(UTZFMT_STYLE_GENERIC_LONG, tz, date, zoneString);
  1.1496 +                        capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong;
  1.1497 +                    }
  1.1498 +                }
  1.1499 +                else if (patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD) {
  1.1500 +                    if (count == 1) {
  1.1501 +                        // "V"
  1.1502 +                        tzFormat()->format(UTZFMT_STYLE_ZONE_ID_SHORT, tz, date, zoneString);
  1.1503 +                    } else if (count == 2) {
  1.1504 +                        // "VV"
  1.1505 +                        tzFormat()->format(UTZFMT_STYLE_ZONE_ID, tz, date, zoneString);
  1.1506 +                    } else if (count == 3) {
  1.1507 +                        // "VVV"
  1.1508 +                        tzFormat()->format(UTZFMT_STYLE_EXEMPLAR_LOCATION, tz, date, zoneString);
  1.1509 +                    } else if (count == 4) {
  1.1510 +                        // "VVVV"
  1.1511 +                        tzFormat()->format(UTZFMT_STYLE_GENERIC_LOCATION, tz, date, zoneString);
  1.1512 +                        capContextUsageType = DateFormatSymbols::kCapContextUsageZoneLong;
  1.1513 +                    }
  1.1514 +                }
  1.1515 +                else if (patternCharIndex == UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD) {
  1.1516 +                    if (count == 1) {
  1.1517 +                        // "O"
  1.1518 +                        tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT, tz, date, zoneString);
  1.1519 +                    } else if (count == 4) {
  1.1520 +                        // "OOOO"
  1.1521 +                        tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString);
  1.1522 +                    }
  1.1523 +                }
  1.1524 +                else if (patternCharIndex == UDAT_TIMEZONE_ISO_FIELD) {
  1.1525 +                    if (count == 1) {
  1.1526 +                        // "X"
  1.1527 +                        tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_SHORT, tz, date, zoneString);
  1.1528 +                    } else if (count == 2) {
  1.1529 +                        // "XX"
  1.1530 +                        tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FIXED, tz, date, zoneString);
  1.1531 +                    } else if (count == 3) {
  1.1532 +                        // "XXX"
  1.1533 +                        tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED, tz, date, zoneString);
  1.1534 +                    } else if (count == 4) {
  1.1535 +                        // "XXXX"
  1.1536 +                        tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FULL, tz, date, zoneString);
  1.1537 +                    } else if (count == 5) {
  1.1538 +                        // "XXXXX"
  1.1539 +                        tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString);
  1.1540 +                    }
  1.1541 +                }
  1.1542 +                else if (patternCharIndex == UDAT_TIMEZONE_ISO_LOCAL_FIELD) {
  1.1543 +                    if (count == 1) {
  1.1544 +                        // "x"
  1.1545 +                        tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT, tz, date, zoneString);
  1.1546 +                    } else if (count == 2) {
  1.1547 +                        // "xx"
  1.1548 +                        tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED, tz, date, zoneString);
  1.1549 +                    } else if (count == 3) {
  1.1550 +                        // "xxx"
  1.1551 +                        tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED, tz, date, zoneString);
  1.1552 +                    } else if (count == 4) {
  1.1553 +                        // "xxxx"
  1.1554 +                        tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString);
  1.1555 +                    } else if (count == 5) {
  1.1556 +                        // "xxxxx"
  1.1557 +                        tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL, tz, date, zoneString);
  1.1558 +                    }
  1.1559 +                }
  1.1560 +                else {
  1.1561 +                    U_ASSERT(FALSE);
  1.1562 +                }
  1.1563 +            }
  1.1564 +            appendTo += zoneString;
  1.1565 +        }
  1.1566 +        break;
  1.1567 +
  1.1568 +    case UDAT_QUARTER_FIELD:
  1.1569 +        if (count >= 4)
  1.1570 +            _appendSymbol(appendTo, value/3, fSymbols->fQuarters,
  1.1571 +                          fSymbols->fQuartersCount);
  1.1572 +        else if (count == 3)
  1.1573 +            _appendSymbol(appendTo, value/3, fSymbols->fShortQuarters,
  1.1574 +                          fSymbols->fShortQuartersCount);
  1.1575 +        else
  1.1576 +            zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount);
  1.1577 +        break;
  1.1578 +
  1.1579 +    case UDAT_STANDALONE_QUARTER_FIELD:
  1.1580 +        if (count >= 4)
  1.1581 +            _appendSymbol(appendTo, value/3, fSymbols->fStandaloneQuarters,
  1.1582 +                          fSymbols->fStandaloneQuartersCount);
  1.1583 +        else if (count == 3)
  1.1584 +            _appendSymbol(appendTo, value/3, fSymbols->fStandaloneShortQuarters,
  1.1585 +                          fSymbols->fStandaloneShortQuartersCount);
  1.1586 +        else
  1.1587 +            zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount);
  1.1588 +        break;
  1.1589 +
  1.1590 +
  1.1591 +    // all of the other pattern symbols can be formatted as simple numbers with
  1.1592 +    // appropriate zero padding
  1.1593 +    default:
  1.1594 +        zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
  1.1595 +        break;
  1.1596 +    }
  1.1597 +#if !UCONFIG_NO_BREAK_ITERATION
  1.1598 +    if (fieldNum == 0) {
  1.1599 +        // first field, check to see whether we need to titlecase it
  1.1600 +        UBool titlecase = FALSE;
  1.1601 +        switch (capitalizationContext) {
  1.1602 +            case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
  1.1603 +                titlecase = TRUE;
  1.1604 +                break;
  1.1605 +            case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU:
  1.1606 +                titlecase = fSymbols->fCapitalization[capContextUsageType][0];
  1.1607 +                break;
  1.1608 +            case UDISPCTX_CAPITALIZATION_FOR_STANDALONE:
  1.1609 +                titlecase = fSymbols->fCapitalization[capContextUsageType][1];
  1.1610 +                break;
  1.1611 +            default:
  1.1612 +                // titlecase = FALSE;
  1.1613 +                break;
  1.1614 +        }
  1.1615 +        if (titlecase) {
  1.1616 +            UnicodeString firstField(appendTo, beginOffset);
  1.1617 +            firstField.toTitle(NULL, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
  1.1618 +            appendTo.replaceBetween(beginOffset, appendTo.length(), firstField);
  1.1619 +        }
  1.1620 +    }
  1.1621 +#endif
  1.1622 +
  1.1623 +    handler.addAttribute(fgPatternIndexToDateFormatField[patternCharIndex], beginOffset, appendTo.length());
  1.1624 +}
  1.1625 +
  1.1626 +//----------------------------------------------------------------------
  1.1627 +
  1.1628 +NumberFormat *
  1.1629 +SimpleDateFormat::getNumberFormatByIndex(UDateFormatField index) const {
  1.1630 +    if (fNumberFormatters != NULL) {
  1.1631 +        return fNumberFormatters[index];
  1.1632 +    } else {
  1.1633 +        return fNumberFormat;
  1.1634 +    }
  1.1635 +}
  1.1636 +
  1.1637 +//----------------------------------------------------------------------
  1.1638 +void
  1.1639 +SimpleDateFormat::zeroPaddingNumber(NumberFormat *currentNumberFormat,UnicodeString &appendTo,
  1.1640 +                                    int32_t value, int32_t minDigits, int32_t maxDigits) const
  1.1641 +{
  1.1642 +    if (currentNumberFormat!=NULL) {
  1.1643 +        FieldPosition pos(0);
  1.1644 +
  1.1645 +        currentNumberFormat->setMinimumIntegerDigits(minDigits);
  1.1646 +        currentNumberFormat->setMaximumIntegerDigits(maxDigits);
  1.1647 +        currentNumberFormat->format(value, appendTo, pos);  // 3rd arg is there to speed up processing
  1.1648 +    }
  1.1649 +}
  1.1650 +
  1.1651 +//----------------------------------------------------------------------
  1.1652 +
  1.1653 +/**
  1.1654 + * Return true if the given format character, occuring count
  1.1655 + * times, represents a numeric field.
  1.1656 + */
  1.1657 +UBool SimpleDateFormat::isNumeric(UChar formatChar, int32_t count) {
  1.1658 +    return DateFormatSymbols::isNumericPatternChar(formatChar, count);
  1.1659 +}
  1.1660 +
  1.1661 +UBool
  1.1662 +SimpleDateFormat::isAtNumericField(const UnicodeString &pattern, int32_t patternOffset) {
  1.1663 +    if (patternOffset >= pattern.length()) {
  1.1664 +        // not at any field
  1.1665 +        return FALSE;
  1.1666 +    }
  1.1667 +    UChar ch = pattern.charAt(patternOffset);
  1.1668 +    UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch);
  1.1669 +    if (f == UDAT_FIELD_COUNT) {
  1.1670 +        // not at any field
  1.1671 +        return FALSE;
  1.1672 +    }
  1.1673 +    int32_t i = patternOffset;
  1.1674 +    while (pattern.charAt(++i) == ch) {}
  1.1675 +    return DateFormatSymbols::isNumericField(f, i - patternOffset);
  1.1676 +}
  1.1677 +
  1.1678 +UBool
  1.1679 +SimpleDateFormat::isAfterNonNumericField(const UnicodeString &pattern, int32_t patternOffset) {
  1.1680 +    if (patternOffset <= 0) {
  1.1681 +        // not after any field
  1.1682 +        return FALSE;
  1.1683 +    }
  1.1684 +    UChar ch = pattern.charAt(--patternOffset);
  1.1685 +    UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch);
  1.1686 +    if (f == UDAT_FIELD_COUNT) {
  1.1687 +        // not after any field
  1.1688 +        return FALSE;
  1.1689 +    }
  1.1690 +    int32_t i = patternOffset;
  1.1691 +    while (pattern.charAt(--i) == ch) {}
  1.1692 +    return !DateFormatSymbols::isNumericField(f, patternOffset - i);
  1.1693 +}
  1.1694 +
  1.1695 +void
  1.1696 +SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& parsePos) const
  1.1697 +{
  1.1698 +    UErrorCode status = U_ZERO_ERROR;
  1.1699 +    int32_t pos = parsePos.getIndex();
  1.1700 +    int32_t start = pos;
  1.1701 +
  1.1702 +    UBool ambiguousYear[] = { FALSE };
  1.1703 +    int32_t saveHebrewMonth = -1;
  1.1704 +    int32_t count = 0;
  1.1705 +
  1.1706 +    // hack, reset tztype, cast away const
  1.1707 +    ((SimpleDateFormat*)this)->tztype = UTZFMT_TIME_TYPE_UNKNOWN;
  1.1708 +
  1.1709 +    // For parsing abutting numeric fields. 'abutPat' is the
  1.1710 +    // offset into 'pattern' of the first of 2 or more abutting
  1.1711 +    // numeric fields.  'abutStart' is the offset into 'text'
  1.1712 +    // where parsing the fields begins. 'abutPass' starts off as 0
  1.1713 +    // and increments each time we try to parse the fields.
  1.1714 +    int32_t abutPat = -1; // If >=0, we are in a run of abutting numeric fields
  1.1715 +    int32_t abutStart = 0;
  1.1716 +    int32_t abutPass = 0;
  1.1717 +    UBool inQuote = FALSE;
  1.1718 +
  1.1719 +    MessageFormat * numericLeapMonthFormatter = NULL;
  1.1720 +
  1.1721 +    Calendar* calClone = NULL;
  1.1722 +    Calendar *workCal = &cal;
  1.1723 +    if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) {
  1.1724 +        // Different calendar type
  1.1725 +        // We use the time/zone from the input calendar, but
  1.1726 +        // do not use the input calendar for field calculation.
  1.1727 +        calClone = fCalendar->clone();
  1.1728 +        if (calClone != NULL) {
  1.1729 +            calClone->setTime(cal.getTime(status),status);
  1.1730 +            if (U_FAILURE(status)) {
  1.1731 +                goto ExitParse;
  1.1732 +            }
  1.1733 +            calClone->setTimeZone(cal.getTimeZone());
  1.1734 +            workCal = calClone;
  1.1735 +        } else {
  1.1736 +            status = U_MEMORY_ALLOCATION_ERROR;
  1.1737 +            goto ExitParse;
  1.1738 +        }
  1.1739 +    }
  1.1740 +    
  1.1741 +    if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
  1.1742 +        numericLeapMonthFormatter = new MessageFormat(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric], fLocale, status);
  1.1743 +        if (numericLeapMonthFormatter == NULL) {
  1.1744 +             status = U_MEMORY_ALLOCATION_ERROR;
  1.1745 +             goto ExitParse;
  1.1746 +        } else if (U_FAILURE(status)) {
  1.1747 +             goto ExitParse; // this will delete numericLeapMonthFormatter
  1.1748 +        }
  1.1749 +    }
  1.1750 +
  1.1751 +    for (int32_t i=0; i<fPattern.length(); ++i) {
  1.1752 +        UChar ch = fPattern.charAt(i);
  1.1753 +
  1.1754 +        // Handle alphabetic field characters.
  1.1755 +        if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { // [A-Za-z]
  1.1756 +            int32_t fieldPat = i;
  1.1757 +
  1.1758 +            // Count the length of this field specifier
  1.1759 +            count = 1;
  1.1760 +            while ((i+1)<fPattern.length() &&
  1.1761 +                   fPattern.charAt(i+1) == ch) {
  1.1762 +                ++count;
  1.1763 +                ++i;
  1.1764 +            }
  1.1765 +
  1.1766 +            if (isNumeric(ch, count)) {
  1.1767 +                if (abutPat < 0) {
  1.1768 +                    // Determine if there is an abutting numeric field.
  1.1769 +                    // Record the start of a set of abutting numeric fields.
  1.1770 +                    if (isAtNumericField(fPattern, i + 1)) {
  1.1771 +                        abutPat = fieldPat;
  1.1772 +                        abutStart = pos;
  1.1773 +                        abutPass = 0;
  1.1774 +                    }
  1.1775 +                }
  1.1776 +            } else {
  1.1777 +                abutPat = -1; // End of any abutting fields
  1.1778 +            }
  1.1779 +
  1.1780 +            // Handle fields within a run of abutting numeric fields.  Take
  1.1781 +            // the pattern "HHmmss" as an example. We will try to parse
  1.1782 +            // 2/2/2 characters of the input text, then if that fails,
  1.1783 +            // 1/2/2.  We only adjust the width of the leftmost field; the
  1.1784 +            // others remain fixed.  This allows "123456" => 12:34:56, but
  1.1785 +            // "12345" => 1:23:45.  Likewise, for the pattern "yyyyMMdd" we
  1.1786 +            // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
  1.1787 +            if (abutPat >= 0) {
  1.1788 +                // If we are at the start of a run of abutting fields, then
  1.1789 +                // shorten this field in each pass.  If we can't shorten
  1.1790 +                // this field any more, then the parse of this set of
  1.1791 +                // abutting numeric fields has failed.
  1.1792 +                if (fieldPat == abutPat) {
  1.1793 +                    count -= abutPass++;
  1.1794 +                    if (count == 0) {
  1.1795 +                        status = U_PARSE_ERROR;
  1.1796 +                        goto ExitParse;
  1.1797 +                    }
  1.1798 +                }
  1.1799 +
  1.1800 +                pos = subParse(text, pos, ch, count,
  1.1801 +                               TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter);
  1.1802 +
  1.1803 +                // If the parse fails anywhere in the run, back up to the
  1.1804 +                // start of the run and retry.
  1.1805 +                if (pos < 0) {
  1.1806 +                    i = abutPat - 1;
  1.1807 +                    pos = abutStart;
  1.1808 +                    continue;
  1.1809 +                }
  1.1810 +            }
  1.1811 +
  1.1812 +            // Handle non-numeric fields and non-abutting numeric
  1.1813 +            // fields.
  1.1814 +            else if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
  1.1815 +                int32_t s = subParse(text, pos, ch, count,
  1.1816 +                               FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter);
  1.1817 +
  1.1818 +                if (s == -pos-1) {
  1.1819 +                    // era not present, in special cases allow this to continue
  1.1820 +                    // from the position where the era was expected
  1.1821 +                    s = pos;
  1.1822 +
  1.1823 +                    if (i+1 < fPattern.length()) {
  1.1824 +                        // move to next pattern character
  1.1825 +                        UChar ch = fPattern.charAt(i+1);
  1.1826 +
  1.1827 +                        // check for whitespace
  1.1828 +                        if (PatternProps::isWhiteSpace(ch)) {
  1.1829 +                            i++;
  1.1830 +                            // Advance over run in pattern
  1.1831 +                            while ((i+1)<fPattern.length() &&
  1.1832 +                                   PatternProps::isWhiteSpace(fPattern.charAt(i+1))) {
  1.1833 +                                ++i;
  1.1834 +                            }
  1.1835 +                        }
  1.1836 +                    }
  1.1837 +                }
  1.1838 +                else if (s <= 0) {
  1.1839 +                    status = U_PARSE_ERROR;
  1.1840 +                    goto ExitParse;
  1.1841 +                }
  1.1842 +                pos = s;
  1.1843 +            }
  1.1844 +        }
  1.1845 +
  1.1846 +        // Handle literal pattern characters.  These are any
  1.1847 +        // quoted characters and non-alphabetic unquoted
  1.1848 +        // characters.
  1.1849 +        else {
  1.1850 +
  1.1851 +            abutPat = -1; // End of any abutting fields
  1.1852 +            
  1.1853 +            if (! matchLiterals(fPattern, i, text, pos, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status))) {
  1.1854 +                status = U_PARSE_ERROR;
  1.1855 +                goto ExitParse;
  1.1856 +            }
  1.1857 +        }
  1.1858 +    }
  1.1859 +
  1.1860 +    // Special hack for trailing "." after non-numeric field.
  1.1861 +    if (text.charAt(pos) == 0x2e && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) {
  1.1862 +        // only do if the last field is not numeric
  1.1863 +        if (isAfterNonNumericField(fPattern, fPattern.length())) {
  1.1864 +            pos++; // skip the extra "."
  1.1865 +        }
  1.1866 +    }
  1.1867 +
  1.1868 +    // At this point the fields of Calendar have been set.  Calendar
  1.1869 +    // will fill in default values for missing fields when the time
  1.1870 +    // is computed.
  1.1871 +
  1.1872 +    parsePos.setIndex(pos);
  1.1873 +
  1.1874 +    // This part is a problem:  When we call parsedDate.after, we compute the time.
  1.1875 +    // Take the date April 3 2004 at 2:30 am.  When this is first set up, the year
  1.1876 +    // will be wrong if we're parsing a 2-digit year pattern.  It will be 1904.
  1.1877 +    // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day.  2:30 am
  1.1878 +    // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
  1.1879 +    // on that day.  It is therefore parsed out to fields as 3:30 am.  Then we
  1.1880 +    // add 100 years, and get April 3 2004 at 3:30 am.  Note that April 3 2004 is
  1.1881 +    // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
  1.1882 +    /*
  1.1883 +        UDate parsedDate = calendar.getTime();
  1.1884 +        if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
  1.1885 +            calendar.add(Calendar.YEAR, 100);
  1.1886 +            parsedDate = calendar.getTime();
  1.1887 +        }
  1.1888 +    */
  1.1889 +    // Because of the above condition, save off the fields in case we need to readjust.
  1.1890 +    // The procedure we use here is not particularly efficient, but there is no other
  1.1891 +    // way to do this given the API restrictions present in Calendar.  We minimize
  1.1892 +    // inefficiency by only performing this computation when it might apply, that is,
  1.1893 +    // when the two-digit year is equal to the start year, and thus might fall at the
  1.1894 +    // front or the back of the default century.  This only works because we adjust
  1.1895 +    // the year correctly to start with in other cases -- see subParse().
  1.1896 +    if (ambiguousYear[0] || tztype != UTZFMT_TIME_TYPE_UNKNOWN) // If this is true then the two-digit year == the default start year
  1.1897 +    {
  1.1898 +        // We need a copy of the fields, and we need to avoid triggering a call to
  1.1899 +        // complete(), which will recalculate the fields.  Since we can't access
  1.1900 +        // the fields[] array in Calendar, we clone the entire object.  This will
  1.1901 +        // stop working if Calendar.clone() is ever rewritten to call complete().
  1.1902 +        Calendar *copy;
  1.1903 +        if (ambiguousYear[0]) {
  1.1904 +            copy = cal.clone();
  1.1905 +            // Check for failed cloning.
  1.1906 +            if (copy == NULL) {
  1.1907 +                status = U_MEMORY_ALLOCATION_ERROR;
  1.1908 +                goto ExitParse;
  1.1909 +            }
  1.1910 +            UDate parsedDate = copy->getTime(status);
  1.1911 +            // {sfb} check internalGetDefaultCenturyStart
  1.1912 +            if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) {
  1.1913 +                // We can't use add here because that does a complete() first.
  1.1914 +                cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100);
  1.1915 +            }
  1.1916 +            delete copy;
  1.1917 +        }
  1.1918 +
  1.1919 +        if (tztype != UTZFMT_TIME_TYPE_UNKNOWN) {
  1.1920 +            copy = cal.clone();
  1.1921 +            // Check for failed cloning.
  1.1922 +            if (copy == NULL) {
  1.1923 +                status = U_MEMORY_ALLOCATION_ERROR;
  1.1924 +                goto ExitParse;
  1.1925 +            }
  1.1926 +            const TimeZone & tz = cal.getTimeZone();
  1.1927 +            BasicTimeZone *btz = NULL;
  1.1928 +
  1.1929 +            if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL
  1.1930 +                || dynamic_cast<const SimpleTimeZone *>(&tz) != NULL
  1.1931 +                || dynamic_cast<const RuleBasedTimeZone *>(&tz) != NULL
  1.1932 +                || dynamic_cast<const VTimeZone *>(&tz) != NULL) {
  1.1933 +                btz = (BasicTimeZone*)&tz;
  1.1934 +            }
  1.1935 +
  1.1936 +            // Get local millis
  1.1937 +            copy->set(UCAL_ZONE_OFFSET, 0);
  1.1938 +            copy->set(UCAL_DST_OFFSET, 0);
  1.1939 +            UDate localMillis = copy->getTime(status);
  1.1940 +
  1.1941 +            // Make sure parsed time zone type (Standard or Daylight)
  1.1942 +            // matches the rule used by the parsed time zone.
  1.1943 +            int32_t raw, dst;
  1.1944 +            if (btz != NULL) {
  1.1945 +                if (tztype == UTZFMT_TIME_TYPE_STANDARD) {
  1.1946 +                    btz->getOffsetFromLocal(localMillis,
  1.1947 +                        BasicTimeZone::kStandard, BasicTimeZone::kStandard, raw, dst, status);
  1.1948 +                } else {
  1.1949 +                    btz->getOffsetFromLocal(localMillis,
  1.1950 +                        BasicTimeZone::kDaylight, BasicTimeZone::kDaylight, raw, dst, status);
  1.1951 +                }
  1.1952 +            } else {
  1.1953 +                // No good way to resolve ambiguous time at transition,
  1.1954 +                // but following code work in most case.
  1.1955 +                tz.getOffset(localMillis, TRUE, raw, dst, status);
  1.1956 +            }
  1.1957 +
  1.1958 +            // Now, compare the results with parsed type, either standard or daylight saving time
  1.1959 +            int32_t resolvedSavings = dst;
  1.1960 +            if (tztype == UTZFMT_TIME_TYPE_STANDARD) {
  1.1961 +                if (dst != 0) {
  1.1962 +                    // Override DST_OFFSET = 0 in the result calendar
  1.1963 +                    resolvedSavings = 0;
  1.1964 +                }
  1.1965 +            } else { // tztype == TZTYPE_DST
  1.1966 +                if (dst == 0) {
  1.1967 +                    if (btz != NULL) {
  1.1968 +                        UDate time = localMillis + raw;
  1.1969 +                        // We use the nearest daylight saving time rule.
  1.1970 +                        TimeZoneTransition beforeTrs, afterTrs;
  1.1971 +                        UDate beforeT = time, afterT = time;
  1.1972 +                        int32_t beforeSav = 0, afterSav = 0;
  1.1973 +                        UBool beforeTrsAvail, afterTrsAvail;
  1.1974 +
  1.1975 +                        // Search for DST rule before or on the time
  1.1976 +                        while (TRUE) {
  1.1977 +                            beforeTrsAvail = btz->getPreviousTransition(beforeT, TRUE, beforeTrs);
  1.1978 +                            if (!beforeTrsAvail) {
  1.1979 +                                break;
  1.1980 +                            }
  1.1981 +                            beforeT = beforeTrs.getTime() - 1;
  1.1982 +                            beforeSav = beforeTrs.getFrom()->getDSTSavings();
  1.1983 +                            if (beforeSav != 0) {
  1.1984 +                                break;
  1.1985 +                            }
  1.1986 +                        }
  1.1987 +
  1.1988 +                        // Search for DST rule after the time
  1.1989 +                        while (TRUE) {
  1.1990 +                            afterTrsAvail = btz->getNextTransition(afterT, FALSE, afterTrs);
  1.1991 +                            if (!afterTrsAvail) {
  1.1992 +                                break;
  1.1993 +                            }
  1.1994 +                            afterT = afterTrs.getTime();
  1.1995 +                            afterSav = afterTrs.getTo()->getDSTSavings();
  1.1996 +                            if (afterSav != 0) {
  1.1997 +                                break;
  1.1998 +                            }
  1.1999 +                        }
  1.2000 +
  1.2001 +                        if (beforeTrsAvail && afterTrsAvail) {
  1.2002 +                            if (time - beforeT > afterT - time) {
  1.2003 +                                resolvedSavings = afterSav;
  1.2004 +                            } else {
  1.2005 +                                resolvedSavings = beforeSav;
  1.2006 +                            }
  1.2007 +                        } else if (beforeTrsAvail && beforeSav != 0) {
  1.2008 +                            resolvedSavings = beforeSav;
  1.2009 +                        } else if (afterTrsAvail && afterSav != 0) {
  1.2010 +                            resolvedSavings = afterSav;
  1.2011 +                        } else {
  1.2012 +                            resolvedSavings = btz->getDSTSavings();
  1.2013 +                        }
  1.2014 +                    } else {
  1.2015 +                        resolvedSavings = tz.getDSTSavings();
  1.2016 +                    }
  1.2017 +                    if (resolvedSavings == 0) {
  1.2018 +                        // final fallback
  1.2019 +                        resolvedSavings = U_MILLIS_PER_HOUR;
  1.2020 +                    }
  1.2021 +                }
  1.2022 +            }
  1.2023 +            cal.set(UCAL_ZONE_OFFSET, raw);
  1.2024 +            cal.set(UCAL_DST_OFFSET, resolvedSavings);
  1.2025 +            delete copy;
  1.2026 +        }
  1.2027 +    }
  1.2028 +ExitParse:
  1.2029 +    // Set the parsed result if local calendar is used
  1.2030 +    // instead of the input calendar
  1.2031 +    if (U_SUCCESS(status) && workCal != &cal) {
  1.2032 +        cal.setTimeZone(workCal->getTimeZone());
  1.2033 +        cal.setTime(workCal->getTime(status), status);
  1.2034 +    }
  1.2035 +
  1.2036 +    if (numericLeapMonthFormatter != NULL) {
  1.2037 +        delete numericLeapMonthFormatter;
  1.2038 +    }
  1.2039 +    if (calClone != NULL) {
  1.2040 +        delete calClone;
  1.2041 +    }
  1.2042 +
  1.2043 +    // If any Calendar calls failed, we pretend that we
  1.2044 +    // couldn't parse the string, when in reality this isn't quite accurate--
  1.2045 +    // we did parse it; the Calendar calls just failed.
  1.2046 +    if (U_FAILURE(status)) {
  1.2047 +        parsePos.setErrorIndex(pos);
  1.2048 +        parsePos.setIndex(start);
  1.2049 +    }
  1.2050 +}
  1.2051 +
  1.2052 +//----------------------------------------------------------------------
  1.2053 +
  1.2054 +static UBool
  1.2055 +newBestMatchWithOptionalDot(const UnicodeString &lcaseText,
  1.2056 +                            const UnicodeString &data,
  1.2057 +                            UnicodeString &bestMatchName,
  1.2058 +                            int32_t &bestMatchLength);
  1.2059 +
  1.2060 +int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text,
  1.2061 +                              int32_t start,
  1.2062 +                              UCalendarDateFields field,
  1.2063 +                              const UnicodeString* data,
  1.2064 +                              int32_t dataCount,
  1.2065 +                              Calendar& cal) const
  1.2066 +{
  1.2067 +    int32_t i = 0;
  1.2068 +    int32_t count = dataCount;
  1.2069 +
  1.2070 +    // There may be multiple strings in the data[] array which begin with
  1.2071 +    // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
  1.2072 +    // We keep track of the longest match, and return that.  Note that this
  1.2073 +    // unfortunately requires us to test all array elements.
  1.2074 +    int32_t bestMatchLength = 0, bestMatch = -1;
  1.2075 +    UnicodeString bestMatchName;
  1.2076 +
  1.2077 +    // {sfb} kludge to support case-insensitive comparison
  1.2078 +    // {markus 2002oct11} do not just use caseCompareBetween because we do not know
  1.2079 +    // the length of the match after case folding
  1.2080 +    // {alan 20040607} don't case change the whole string, since the length
  1.2081 +    // can change
  1.2082 +    // TODO we need a case-insensitive startsWith function
  1.2083 +    UnicodeString lcaseText;
  1.2084 +    text.extract(start, INT32_MAX, lcaseText);
  1.2085 +    lcaseText.foldCase();
  1.2086 +
  1.2087 +    for (; i < count; ++i)
  1.2088 +    {
  1.2089 +        // Always compare if we have no match yet; otherwise only compare
  1.2090 +        // against potentially better matches (longer strings).
  1.2091 +
  1.2092 +        if (newBestMatchWithOptionalDot(lcaseText, data[i], bestMatchName, bestMatchLength)) {
  1.2093 +            bestMatch = i;
  1.2094 +        }
  1.2095 +    }
  1.2096 +    if (bestMatch >= 0)
  1.2097 +    {
  1.2098 +        cal.set(field, bestMatch * 3);
  1.2099 +
  1.2100 +        // Once we have a match, we have to determine the length of the
  1.2101 +        // original source string.  This will usually be == the length of
  1.2102 +        // the case folded string, but it may differ (e.g. sharp s).
  1.2103 +
  1.2104 +        // Most of the time, the length will be the same as the length
  1.2105 +        // of the string from the locale data.  Sometimes it will be
  1.2106 +        // different, in which case we will have to figure it out by
  1.2107 +        // adding a character at a time, until we have a match.  We do
  1.2108 +        // this all in one loop, where we try 'len' first (at index
  1.2109 +        // i==0).
  1.2110 +        int32_t len = bestMatchName.length(); // 99+% of the time
  1.2111 +        int32_t n = text.length() - start;
  1.2112 +        for (i=0; i<=n; ++i) {
  1.2113 +            int32_t j=i;
  1.2114 +            if (i == 0) {
  1.2115 +                j = len;
  1.2116 +            } else if (i == len) {
  1.2117 +                continue; // already tried this when i was 0
  1.2118 +            }
  1.2119 +            text.extract(start, j, lcaseText);
  1.2120 +            lcaseText.foldCase();
  1.2121 +            if (bestMatchName == lcaseText) {
  1.2122 +                return start + j;
  1.2123 +            }
  1.2124 +        }
  1.2125 +    }
  1.2126 +
  1.2127 +    return -start;
  1.2128 +}
  1.2129 +
  1.2130 +//----------------------------------------------------------------------
  1.2131 +UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
  1.2132 +                                      int32_t &patternOffset,
  1.2133 +                                      const UnicodeString &text,
  1.2134 +                                      int32_t &textOffset,
  1.2135 +                                      UBool lenient)
  1.2136 +{
  1.2137 +    UBool inQuote = FALSE;
  1.2138 +    UnicodeString literal;
  1.2139 +    int32_t i = patternOffset;
  1.2140 +    
  1.2141 +    // scan pattern looking for contiguous literal characters
  1.2142 +    for ( ; i < pattern.length(); i += 1) {
  1.2143 +        UChar ch = pattern.charAt(i);
  1.2144 +        
  1.2145 +        if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { // unquoted [A-Za-z]
  1.2146 +            break;
  1.2147 +        }
  1.2148 +        
  1.2149 +        if (ch == QUOTE) {
  1.2150 +            // Match a quote literal ('') inside OR outside of quotes
  1.2151 +            if ((i + 1) < pattern.length() && pattern.charAt(i + 1) == QUOTE) {
  1.2152 +                i += 1;
  1.2153 +            } else {
  1.2154 +                inQuote = !inQuote;
  1.2155 +                continue;
  1.2156 +            }
  1.2157 +        }
  1.2158 +        
  1.2159 +        literal += ch;
  1.2160 +    }
  1.2161 +    
  1.2162 +    // at this point, literal contains the literal text
  1.2163 +    // and i is the index of the next non-literal pattern character.
  1.2164 +    int32_t p;
  1.2165 +    int32_t t = textOffset;
  1.2166 +    
  1.2167 +    if (lenient) {
  1.2168 +        // trim leading, trailing whitespace from
  1.2169 +        // the literal text
  1.2170 +        literal.trim();
  1.2171 +        
  1.2172 +        // ignore any leading whitespace in the text
  1.2173 +        while (t < text.length() && u_isWhitespace(text.charAt(t))) {
  1.2174 +            t += 1;
  1.2175 +        }
  1.2176 +    }
  1.2177 +        
  1.2178 +    for (p = 0; p < literal.length() && t < text.length();) {
  1.2179 +        UBool needWhitespace = FALSE;
  1.2180 +        
  1.2181 +        while (p < literal.length() && PatternProps::isWhiteSpace(literal.charAt(p))) {
  1.2182 +            needWhitespace = TRUE;
  1.2183 +            p += 1;
  1.2184 +        }
  1.2185 +        
  1.2186 +        if (needWhitespace) {
  1.2187 +            int32_t tStart = t;
  1.2188 +            
  1.2189 +            while (t < text.length()) {
  1.2190 +                UChar tch = text.charAt(t);
  1.2191 +                
  1.2192 +                if (!u_isUWhiteSpace(tch) && !PatternProps::isWhiteSpace(tch)) {
  1.2193 +                    break;
  1.2194 +                }
  1.2195 +                
  1.2196 +                t += 1;
  1.2197 +            }
  1.2198 +            
  1.2199 +            // TODO: should we require internal spaces
  1.2200 +            // in lenient mode? (There won't be any
  1.2201 +            // leading or trailing spaces)
  1.2202 +            if (!lenient && t == tStart) {
  1.2203 +                // didn't find matching whitespace:
  1.2204 +                // an error in strict mode
  1.2205 +                return FALSE;
  1.2206 +            }
  1.2207 +            
  1.2208 +            // In strict mode, this run of whitespace
  1.2209 +            // may have been at the end.
  1.2210 +            if (p >= literal.length()) {
  1.2211 +                break;
  1.2212 +            }
  1.2213 +        }
  1.2214 +        
  1.2215 +        if (t >= text.length() || literal.charAt(p) != text.charAt(t)) {
  1.2216 +            // Ran out of text, or found a non-matching character:
  1.2217 +            // OK in lenient mode, an error in strict mode.
  1.2218 +            if (lenient) {
  1.2219 +                if (t == textOffset && text.charAt(t) == 0x2e &&
  1.2220 +                        isAfterNonNumericField(pattern, patternOffset)) {
  1.2221 +                    // Lenient mode and the literal input text begins with a "." and
  1.2222 +                    // we are after a non-numeric field: We skip the "."
  1.2223 +                    ++t;
  1.2224 +                    continue;  // Do not update p.
  1.2225 +                }
  1.2226 +                break;
  1.2227 +            }
  1.2228 +            
  1.2229 +            return FALSE;
  1.2230 +        }
  1.2231 +        ++p;
  1.2232 +        ++t;
  1.2233 +    }
  1.2234 +    
  1.2235 +    // At this point if we're in strict mode we have a complete match.
  1.2236 +    // If we're in lenient mode we may have a partial match, or no
  1.2237 +    // match at all.
  1.2238 +    if (p <= 0) {
  1.2239 +        // no match. Pretend it matched a run of whitespace
  1.2240 +        // and ignorables in the text.
  1.2241 +        const  UnicodeSet *ignorables = NULL;
  1.2242 +        UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(pattern.charAt(i));
  1.2243 +        if (patternCharIndex != UDAT_FIELD_COUNT) {
  1.2244 +            ignorables = SimpleDateFormatStaticSets::getIgnorables(patternCharIndex);
  1.2245 +        }
  1.2246 +        
  1.2247 +        for (t = textOffset; t < text.length(); t += 1) {
  1.2248 +            UChar ch = text.charAt(t);
  1.2249 +            
  1.2250 +            if (ignorables == NULL || !ignorables->contains(ch)) {
  1.2251 +                break;
  1.2252 +            }
  1.2253 +        }
  1.2254 +    }
  1.2255 +    
  1.2256 +    // if we get here, we've got a complete match.
  1.2257 +    patternOffset = i - 1;
  1.2258 +    textOffset = t;
  1.2259 +    
  1.2260 +    return TRUE;
  1.2261 +}
  1.2262 +
  1.2263 +//----------------------------------------------------------------------
  1.2264 +
  1.2265 +int32_t SimpleDateFormat::matchString(const UnicodeString& text,
  1.2266 +                              int32_t start,
  1.2267 +                              UCalendarDateFields field,
  1.2268 +                              const UnicodeString* data,
  1.2269 +                              int32_t dataCount,
  1.2270 +                              const UnicodeString* monthPattern,
  1.2271 +                              Calendar& cal) const
  1.2272 +{
  1.2273 +    int32_t i = 0;
  1.2274 +    int32_t count = dataCount;
  1.2275 +
  1.2276 +    if (field == UCAL_DAY_OF_WEEK) i = 1;
  1.2277 +
  1.2278 +    // There may be multiple strings in the data[] array which begin with
  1.2279 +    // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
  1.2280 +    // We keep track of the longest match, and return that.  Note that this
  1.2281 +    // unfortunately requires us to test all array elements.
  1.2282 +    int32_t bestMatchLength = 0, bestMatch = -1;
  1.2283 +    UnicodeString bestMatchName;
  1.2284 +    int32_t isLeapMonth = 0;
  1.2285 +
  1.2286 +    // {sfb} kludge to support case-insensitive comparison
  1.2287 +    // {markus 2002oct11} do not just use caseCompareBetween because we do not know
  1.2288 +    // the length of the match after case folding
  1.2289 +    // {alan 20040607} don't case change the whole string, since the length
  1.2290 +    // can change
  1.2291 +    // TODO we need a case-insensitive startsWith function
  1.2292 +    UnicodeString lcaseText;
  1.2293 +    text.extract(start, INT32_MAX, lcaseText);
  1.2294 +    lcaseText.foldCase();
  1.2295 +
  1.2296 +    for (; i < count; ++i)
  1.2297 +    {
  1.2298 +        // Always compare if we have no match yet; otherwise only compare
  1.2299 +        // against potentially better matches (longer strings).
  1.2300 +
  1.2301 +        if (newBestMatchWithOptionalDot(lcaseText, data[i], bestMatchName, bestMatchLength)) {
  1.2302 +            bestMatch = i;
  1.2303 +            isLeapMonth = 0;
  1.2304 +        }
  1.2305 +
  1.2306 +        if (monthPattern != NULL) {
  1.2307 +            UErrorCode status = U_ZERO_ERROR;
  1.2308 +            UnicodeString leapMonthName;
  1.2309 +            Formattable monthName((const UnicodeString&)(data[i]));
  1.2310 +            MessageFormat::format(*monthPattern, &monthName, 1, leapMonthName, status);
  1.2311 +            if (U_SUCCESS(status)) {
  1.2312 +                if (newBestMatchWithOptionalDot(lcaseText, leapMonthName, bestMatchName, bestMatchLength)) {
  1.2313 +                    bestMatch = i;
  1.2314 +                    isLeapMonth = 1;
  1.2315 +                }
  1.2316 +            }
  1.2317 +        }
  1.2318 +    }
  1.2319 +    if (bestMatch >= 0)
  1.2320 +    {
  1.2321 +        // Adjustment for Hebrew Calendar month Adar II
  1.2322 +        if (!strcmp(cal.getType(),"hebrew") && field==UCAL_MONTH && bestMatch==13) {
  1.2323 +            cal.set(field,6);
  1.2324 +        }
  1.2325 +        else {
  1.2326 +            if (field == UCAL_YEAR) {
  1.2327 +                bestMatch++; // only get here for cyclic year names, which match 1-based years 1-60
  1.2328 +            }
  1.2329 +            cal.set(field, bestMatch);
  1.2330 +        }
  1.2331 +        if (monthPattern != NULL) {
  1.2332 +            cal.set(UCAL_IS_LEAP_MONTH, isLeapMonth);
  1.2333 +        }
  1.2334 +
  1.2335 +        // Once we have a match, we have to determine the length of the
  1.2336 +        // original source string.  This will usually be == the length of
  1.2337 +        // the case folded string, but it may differ (e.g. sharp s).
  1.2338 +
  1.2339 +        // Most of the time, the length will be the same as the length
  1.2340 +        // of the string from the locale data.  Sometimes it will be
  1.2341 +        // different, in which case we will have to figure it out by
  1.2342 +        // adding a character at a time, until we have a match.  We do
  1.2343 +        // this all in one loop, where we try 'len' first (at index
  1.2344 +        // i==0).
  1.2345 +        int32_t len = bestMatchName.length(); // 99+% of the time
  1.2346 +        int32_t n = text.length() - start;
  1.2347 +        for (i=0; i<=n; ++i) {
  1.2348 +            int32_t j=i;
  1.2349 +            if (i == 0) {
  1.2350 +                j = len;
  1.2351 +            } else if (i == len) {
  1.2352 +                continue; // already tried this when i was 0
  1.2353 +            }
  1.2354 +            text.extract(start, j, lcaseText);
  1.2355 +            lcaseText.foldCase();
  1.2356 +            if (bestMatchName == lcaseText) {
  1.2357 +                return start + j;
  1.2358 +            }
  1.2359 +        }
  1.2360 +    }
  1.2361 +
  1.2362 +    return -start;
  1.2363 +}
  1.2364 +
  1.2365 +static UBool
  1.2366 +newBestMatchWithOptionalDot(const UnicodeString &lcaseText,
  1.2367 +                            const UnicodeString &data,
  1.2368 +                            UnicodeString &bestMatchName,
  1.2369 +                            int32_t &bestMatchLength) {
  1.2370 +    UnicodeString lcase;
  1.2371 +    lcase.fastCopyFrom(data).foldCase();
  1.2372 +    int32_t length = lcase.length();
  1.2373 +    if (length <= bestMatchLength) {
  1.2374 +        // data cannot provide a better match.
  1.2375 +        return FALSE;
  1.2376 +    }
  1.2377 +
  1.2378 +    if (lcaseText.compareBetween(0, length, lcase, 0, length) == 0) {
  1.2379 +        // normal match
  1.2380 +        bestMatchName = lcase;
  1.2381 +        bestMatchLength = length;
  1.2382 +        return TRUE;
  1.2383 +    }
  1.2384 +    if (lcase.charAt(--length) == 0x2e) {
  1.2385 +        if (lcaseText.compareBetween(0, length, lcase, 0, length) == 0) {
  1.2386 +            // The input text matches the data except for data's trailing dot.
  1.2387 +            bestMatchName = lcase;
  1.2388 +            bestMatchName.truncate(length);
  1.2389 +            bestMatchLength = length;
  1.2390 +            return TRUE;
  1.2391 +        }
  1.2392 +    }
  1.2393 +    return FALSE;
  1.2394 +}
  1.2395 +
  1.2396 +//----------------------------------------------------------------------
  1.2397 +
  1.2398 +void
  1.2399 +SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status)
  1.2400 +{
  1.2401 +    parseAmbiguousDatesAsAfter(d, status);
  1.2402 +}
  1.2403 +
  1.2404 +/**
  1.2405 + * Private member function that converts the parsed date strings into
  1.2406 + * timeFields. Returns -start (for ParsePosition) if failed.
  1.2407 + * @param text the time text to be parsed.
  1.2408 + * @param start where to start parsing.
  1.2409 + * @param ch the pattern character for the date field text to be parsed.
  1.2410 + * @param count the count of a pattern character.
  1.2411 + * @return the new start position if matching succeeded; a negative number
  1.2412 + * indicating matching failure, otherwise.
  1.2413 + */
  1.2414 +int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count,
  1.2415 +                           UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal,
  1.2416 +                           int32_t patLoc, MessageFormat * numericLeapMonthFormatter) const
  1.2417 +{
  1.2418 +    Formattable number;
  1.2419 +    int32_t value = 0;
  1.2420 +    int32_t i;
  1.2421 +    int32_t ps = 0;
  1.2422 +    UErrorCode status = U_ZERO_ERROR;
  1.2423 +    ParsePosition pos(0);
  1.2424 +    UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
  1.2425 +    NumberFormat *currentNumberFormat;
  1.2426 +    UnicodeString temp;
  1.2427 +    UBool gotNumber = FALSE;
  1.2428 +
  1.2429 +#if defined (U_DEBUG_CAL)
  1.2430 +    //fprintf(stderr, "%s:%d - [%c]  st=%d \n", __FILE__, __LINE__, (char) ch, start);
  1.2431 +#endif
  1.2432 +
  1.2433 +    if (patternCharIndex == UDAT_FIELD_COUNT) {
  1.2434 +        return -start;
  1.2435 +    }
  1.2436 +
  1.2437 +    currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
  1.2438 +    UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
  1.2439 +    UnicodeString hebr("hebr", 4, US_INV);
  1.2440 +
  1.2441 +    if (numericLeapMonthFormatter != NULL) {
  1.2442 +        numericLeapMonthFormatter->setFormats((const Format **)&currentNumberFormat, 1);
  1.2443 +    }
  1.2444 +    UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0 || uprv_strcmp(cal.getType(),"dangi") == 0);
  1.2445 +
  1.2446 +    // If there are any spaces here, skip over them.  If we hit the end
  1.2447 +    // of the string, then fail.
  1.2448 +    for (;;) {
  1.2449 +        if (start >= text.length()) {
  1.2450 +            return -start;
  1.2451 +        }
  1.2452 +        UChar32 c = text.char32At(start);
  1.2453 +        if (!u_isUWhiteSpace(c) /*||*/ && !PatternProps::isWhiteSpace(c)) {
  1.2454 +            break;
  1.2455 +        }
  1.2456 +        start += U16_LENGTH(c);
  1.2457 +    }
  1.2458 +    pos.setIndex(start);
  1.2459 +
  1.2460 +    // We handle a few special cases here where we need to parse
  1.2461 +    // a number value.  We handle further, more generic cases below.  We need
  1.2462 +    // to handle some of them here because some fields require extra processing on
  1.2463 +    // the parsed value.
  1.2464 +    if (patternCharIndex == UDAT_HOUR_OF_DAY1_FIELD ||                       // k
  1.2465 +        patternCharIndex == UDAT_HOUR_OF_DAY0_FIELD ||                       // H
  1.2466 +        patternCharIndex == UDAT_HOUR1_FIELD ||                              // h
  1.2467 +        patternCharIndex == UDAT_HOUR0_FIELD ||                              // K
  1.2468 +        (patternCharIndex == UDAT_DOW_LOCAL_FIELD && count <= 2) ||          // e
  1.2469 +        (patternCharIndex == UDAT_STANDALONE_DAY_FIELD && count <= 2) ||     // c
  1.2470 +        (patternCharIndex == UDAT_MONTH_FIELD && count <= 2) ||              // M
  1.2471 +        (patternCharIndex == UDAT_STANDALONE_MONTH_FIELD && count <= 2) ||   // L
  1.2472 +        (patternCharIndex == UDAT_QUARTER_FIELD && count <= 2) ||            // Q
  1.2473 +        (patternCharIndex == UDAT_STANDALONE_QUARTER_FIELD && count <= 2) || // q
  1.2474 +        patternCharIndex == UDAT_YEAR_FIELD ||                               // y
  1.2475 +        patternCharIndex == UDAT_YEAR_WOY_FIELD ||                           // Y
  1.2476 +        patternCharIndex == UDAT_YEAR_NAME_FIELD ||                          // U (falls back to numeric)
  1.2477 +        (patternCharIndex == UDAT_ERA_FIELD && isChineseCalendar) ||         // G
  1.2478 +        patternCharIndex == UDAT_FRACTIONAL_SECOND_FIELD)                    // S
  1.2479 +    {
  1.2480 +        int32_t parseStart = pos.getIndex();
  1.2481 +        // It would be good to unify this with the obeyCount logic below,
  1.2482 +        // but that's going to be difficult.
  1.2483 +        const UnicodeString* src;
  1.2484 +
  1.2485 +        UBool parsedNumericLeapMonth = FALSE;
  1.2486 +        if (numericLeapMonthFormatter != NULL && (patternCharIndex == UDAT_MONTH_FIELD || patternCharIndex == UDAT_STANDALONE_MONTH_FIELD)) {
  1.2487 +            int32_t argCount;
  1.2488 +            Formattable * args = numericLeapMonthFormatter->parse(text, pos, argCount);
  1.2489 +            if (args != NULL && argCount == 1 && pos.getIndex() > parseStart && args[0].isNumeric()) {
  1.2490 +                parsedNumericLeapMonth = TRUE;
  1.2491 +                number.setLong(args[0].getLong());
  1.2492 +                cal.set(UCAL_IS_LEAP_MONTH, 1);
  1.2493 +                delete[] args;
  1.2494 +            } else {
  1.2495 +                pos.setIndex(parseStart);
  1.2496 +                cal.set(UCAL_IS_LEAP_MONTH, 0);
  1.2497 +            }
  1.2498 +        }
  1.2499 +
  1.2500 +        if (!parsedNumericLeapMonth) {
  1.2501 +            if (obeyCount) {
  1.2502 +                if ((start+count) > text.length()) {
  1.2503 +                    return -start;
  1.2504 +                }
  1.2505 +
  1.2506 +                text.extractBetween(0, start + count, temp);
  1.2507 +                src = &temp;
  1.2508 +            } else {
  1.2509 +                src = &text;
  1.2510 +            }
  1.2511 +
  1.2512 +            parseInt(*src, number, pos, allowNegative,currentNumberFormat);
  1.2513 +        }
  1.2514 +
  1.2515 +        int32_t txtLoc = pos.getIndex();
  1.2516 +
  1.2517 +        if (txtLoc > parseStart) {
  1.2518 +            value = number.getLong();
  1.2519 +            gotNumber = TRUE;
  1.2520 +            
  1.2521 +            // suffix processing
  1.2522 +            if (value < 0 ) {
  1.2523 +                txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, TRUE);
  1.2524 +                if (txtLoc != pos.getIndex()) {
  1.2525 +                    value *= -1;
  1.2526 +                }
  1.2527 +            }
  1.2528 +            else {
  1.2529 +                txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, FALSE);
  1.2530 +            }
  1.2531 +
  1.2532 +            if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) {
  1.2533 +                // Check the range of the value
  1.2534 +                int32_t bias = gFieldRangeBias[patternCharIndex];
  1.2535 +                if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) {
  1.2536 +                    return -start;
  1.2537 +                }
  1.2538 +            }
  1.2539 +
  1.2540 +            pos.setIndex(txtLoc);
  1.2541 +        }
  1.2542 +    }
  1.2543 +    
  1.2544 +    // Make sure that we got a number if
  1.2545 +    // we want one, and didn't get one
  1.2546 +    // if we don't want one.
  1.2547 +    switch (patternCharIndex) {
  1.2548 +        case UDAT_HOUR_OF_DAY1_FIELD:
  1.2549 +        case UDAT_HOUR_OF_DAY0_FIELD:
  1.2550 +        case UDAT_HOUR1_FIELD:
  1.2551 +        case UDAT_HOUR0_FIELD:
  1.2552 +            // special range check for hours:
  1.2553 +            if (value < 0 || value > 24) {
  1.2554 +                return -start;
  1.2555 +            }
  1.2556 +            
  1.2557 +            // fall through to gotNumber check
  1.2558 +            
  1.2559 +        case UDAT_YEAR_FIELD:
  1.2560 +        case UDAT_YEAR_WOY_FIELD:
  1.2561 +        case UDAT_FRACTIONAL_SECOND_FIELD:
  1.2562 +            // these must be a number
  1.2563 +            if (! gotNumber) {
  1.2564 +                return -start;
  1.2565 +            }
  1.2566 +            
  1.2567 +            break;
  1.2568 +            
  1.2569 +        default:
  1.2570 +            // we check the rest of the fields below.
  1.2571 +            break;
  1.2572 +    }
  1.2573 +
  1.2574 +    switch (patternCharIndex) {
  1.2575 +    case UDAT_ERA_FIELD:
  1.2576 +        if (isChineseCalendar) {
  1.2577 +            if (!gotNumber) {
  1.2578 +                return -start;
  1.2579 +            }
  1.2580 +            cal.set(UCAL_ERA, value);
  1.2581 +            return pos.getIndex();
  1.2582 +        }
  1.2583 +        if (count == 5) {
  1.2584 +            ps = matchString(text, start, UCAL_ERA, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount, NULL, cal);
  1.2585 +        } else if (count == 4) {
  1.2586 +            ps = matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, NULL, cal);
  1.2587 +        } else {
  1.2588 +            ps = matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, NULL, cal);
  1.2589 +        }
  1.2590 +
  1.2591 +        // check return position, if it equals -start, then matchString error
  1.2592 +        // special case the return code so we don't necessarily fail out until we
  1.2593 +        // verify no year information also
  1.2594 +        if (ps == -start)
  1.2595 +            ps--;
  1.2596 +
  1.2597 +        return ps;
  1.2598 +
  1.2599 +    case UDAT_YEAR_FIELD:
  1.2600 +        // If there are 3 or more YEAR pattern characters, this indicates
  1.2601 +        // that the year value is to be treated literally, without any
  1.2602 +        // two-digit year adjustments (e.g., from "01" to 2001).  Otherwise
  1.2603 +        // we made adjustments to place the 2-digit year in the proper
  1.2604 +        // century, for parsed strings from "00" to "99".  Any other string
  1.2605 +        // is treated literally:  "2250", "-1", "1", "002".
  1.2606 +        if (fDateOverride.compare(hebr)==0 && value < 1000) {
  1.2607 +            value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
  1.2608 +        } else if ((pos.getIndex() - start) == 2 && !isChineseCalendar
  1.2609 +            && u_isdigit(text.charAt(start))
  1.2610 +            && u_isdigit(text.charAt(start+1)))
  1.2611 +        {
  1.2612 +        	// only adjust year for patterns less than 3.
  1.2613 +        	if(count < 3) {
  1.2614 +        		// Assume for example that the defaultCenturyStart is 6/18/1903.
  1.2615 +        		// This means that two-digit years will be forced into the range
  1.2616 +        		// 6/18/1903 to 6/17/2003.  As a result, years 00, 01, and 02
  1.2617 +        		// correspond to 2000, 2001, and 2002.  Years 04, 05, etc. correspond
  1.2618 +        		// to 1904, 1905, etc.  If the year is 03, then it is 2003 if the
  1.2619 +        		// other fields specify a date before 6/18, or 1903 if they specify a
  1.2620 +        		// date afterwards.  As a result, 03 is an ambiguous year.  All other
  1.2621 +        		// two-digit years are unambiguous.
  1.2622 +        		if(fHaveDefaultCentury) { // check if this formatter even has a pivot year
  1.2623 +        			int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
  1.2624 +        			ambiguousYear[0] = (value == ambiguousTwoDigitYear);
  1.2625 +        			value += (fDefaultCenturyStartYear/100)*100 +
  1.2626 +        					(value < ambiguousTwoDigitYear ? 100 : 0);
  1.2627 +        		}
  1.2628 +            }
  1.2629 +        }
  1.2630 +        cal.set(UCAL_YEAR, value);
  1.2631 +
  1.2632 +        // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
  1.2633 +        if (saveHebrewMonth >= 0) {
  1.2634 +            HebrewCalendar *hc = (HebrewCalendar*)&cal;
  1.2635 +            if (!hc->isLeapYear(value) && saveHebrewMonth >= 6) {
  1.2636 +               cal.set(UCAL_MONTH,saveHebrewMonth);
  1.2637 +            } else {
  1.2638 +               cal.set(UCAL_MONTH,saveHebrewMonth-1);
  1.2639 +            }
  1.2640 +            saveHebrewMonth = -1;
  1.2641 +        }
  1.2642 +        return pos.getIndex();
  1.2643 +
  1.2644 +    case UDAT_YEAR_WOY_FIELD:
  1.2645 +        // Comment is the same as for UDAT_Year_FIELDs - look above
  1.2646 +        if (fDateOverride.compare(hebr)==0 && value < 1000) {
  1.2647 +            value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
  1.2648 +        } else if ((pos.getIndex() - start) == 2
  1.2649 +            && u_isdigit(text.charAt(start))
  1.2650 +            && u_isdigit(text.charAt(start+1))
  1.2651 +            && fHaveDefaultCentury )
  1.2652 +        {
  1.2653 +            int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
  1.2654 +            ambiguousYear[0] = (value == ambiguousTwoDigitYear);
  1.2655 +            value += (fDefaultCenturyStartYear/100)*100 +
  1.2656 +                (value < ambiguousTwoDigitYear ? 100 : 0);
  1.2657 +        }
  1.2658 +        cal.set(UCAL_YEAR_WOY, value);
  1.2659 +        return pos.getIndex();
  1.2660 +
  1.2661 +    case UDAT_YEAR_NAME_FIELD:
  1.2662 +        if (fSymbols->fShortYearNames != NULL) {
  1.2663 +            int32_t newStart = matchString(text, start, UCAL_YEAR, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount, NULL, cal);
  1.2664 +            if (newStart > 0) {
  1.2665 +                return newStart;
  1.2666 +            }
  1.2667 +        }
  1.2668 +        if (gotNumber && (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC,status) || value > fSymbols->fShortYearNamesCount)) {
  1.2669 +            cal.set(UCAL_YEAR, value);
  1.2670 +            return pos.getIndex();
  1.2671 +        }
  1.2672 +        return -start;
  1.2673 +
  1.2674 +    case UDAT_MONTH_FIELD:
  1.2675 +    case UDAT_STANDALONE_MONTH_FIELD:
  1.2676 +        if (gotNumber) // i.e., M or MM.
  1.2677 +        {
  1.2678 +            // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
  1.2679 +            // or not it was a leap year.  We may or may not yet know what year it is, so might have to delay checking until
  1.2680 +            // the year is parsed.
  1.2681 +            if (!strcmp(cal.getType(),"hebrew")) {
  1.2682 +                HebrewCalendar *hc = (HebrewCalendar*)&cal;
  1.2683 +                if (cal.isSet(UCAL_YEAR)) {
  1.2684 +                   UErrorCode status = U_ZERO_ERROR;
  1.2685 +                   if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6) {
  1.2686 +                       cal.set(UCAL_MONTH, value);
  1.2687 +                   } else {
  1.2688 +                       cal.set(UCAL_MONTH, value - 1);
  1.2689 +                   }
  1.2690 +                } else {
  1.2691 +                    saveHebrewMonth = value;
  1.2692 +                }
  1.2693 +            } else {
  1.2694 +                // Don't want to parse the month if it is a string
  1.2695 +                // while pattern uses numeric style: M/MM, L/LL
  1.2696 +                // [We computed 'value' above.]
  1.2697 +                cal.set(UCAL_MONTH, value - 1);
  1.2698 +            }
  1.2699 +            return pos.getIndex();
  1.2700 +        } else {
  1.2701 +            // count >= 3 // i.e., MMM/MMMM, LLL/LLLL
  1.2702 +            // Want to be able to parse both short and long forms.
  1.2703 +            // Try count == 4 first:
  1.2704 +            UnicodeString * wideMonthPat = NULL;
  1.2705 +            UnicodeString * shortMonthPat = NULL;
  1.2706 +            if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
  1.2707 +                if (patternCharIndex==UDAT_MONTH_FIELD) {
  1.2708 +                    wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide];
  1.2709 +                    shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev];
  1.2710 +                } else {
  1.2711 +                    wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide];
  1.2712 +                    shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev];
  1.2713 +                }
  1.2714 +            }
  1.2715 +            int32_t newStart = 0;
  1.2716 +            if (patternCharIndex==UDAT_MONTH_FIELD) {
  1.2717 +                newStart = matchString(text, start, UCAL_MONTH, fSymbols->fMonths, fSymbols->fMonthsCount, wideMonthPat, cal); // try MMMM
  1.2718 +                if (newStart > 0) {
  1.2719 +                    return newStart;
  1.2720 +                }
  1.2721 +                newStart = matchString(text, start, UCAL_MONTH, fSymbols->fShortMonths, fSymbols->fShortMonthsCount, shortMonthPat, cal); // try MMM
  1.2722 +            } else {
  1.2723 +                newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, wideMonthPat, cal); // try LLLL
  1.2724 +                if (newStart > 0) {
  1.2725 +                    return newStart;
  1.2726 +                }
  1.2727 +                newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, shortMonthPat, cal); // try LLL
  1.2728 +            }
  1.2729 +            if (newStart > 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))  // currently we do not try to parse MMMMM/LLLLL: #8860
  1.2730 +                return newStart;
  1.2731 +            // else we allowing parsing as number, below
  1.2732 +        }
  1.2733 +        break;
  1.2734 +
  1.2735 +    case UDAT_HOUR_OF_DAY1_FIELD:
  1.2736 +        // [We computed 'value' above.]
  1.2737 +        if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1)
  1.2738 +            value = 0;
  1.2739 +            
  1.2740 +        // fall through to set field
  1.2741 +            
  1.2742 +    case UDAT_HOUR_OF_DAY0_FIELD:
  1.2743 +        cal.set(UCAL_HOUR_OF_DAY, value);
  1.2744 +        return pos.getIndex();
  1.2745 +
  1.2746 +    case UDAT_FRACTIONAL_SECOND_FIELD:
  1.2747 +        // Fractional seconds left-justify
  1.2748 +        i = pos.getIndex() - start;
  1.2749 +        if (i < 3) {
  1.2750 +            while (i < 3) {
  1.2751 +                value *= 10;
  1.2752 +                i++;
  1.2753 +            }
  1.2754 +        } else {
  1.2755 +            int32_t a = 1;
  1.2756 +            while (i > 3) {
  1.2757 +                a *= 10;
  1.2758 +                i--;
  1.2759 +            }
  1.2760 +            value /= a;
  1.2761 +        }
  1.2762 +        cal.set(UCAL_MILLISECOND, value);
  1.2763 +        return pos.getIndex();
  1.2764 +
  1.2765 +    case UDAT_DOW_LOCAL_FIELD:
  1.2766 +        if (gotNumber) // i.e., e or ee
  1.2767 +        {
  1.2768 +            // [We computed 'value' above.]
  1.2769 +            cal.set(UCAL_DOW_LOCAL, value);
  1.2770 +            return pos.getIndex();
  1.2771 +        }
  1.2772 +        // else for eee-eeeee fall through to handling of EEE-EEEEE
  1.2773 +        // fall through, do not break here
  1.2774 +    case UDAT_DAY_OF_WEEK_FIELD:
  1.2775 +        {
  1.2776 +            // Want to be able to parse both short and long forms.
  1.2777 +            // Try count == 4 (EEEE) wide first:
  1.2778 +            int32_t newStart = 0;
  1.2779 +            if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
  1.2780 +                                      fSymbols->fWeekdays, fSymbols->fWeekdaysCount, NULL, cal)) > 0)
  1.2781 +                return newStart;
  1.2782 +            // EEEE wide failed, now try EEE abbreviated
  1.2783 +            else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
  1.2784 +                                   fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, NULL, cal)) > 0)
  1.2785 +                return newStart;
  1.2786 +            // EEE abbreviated failed, now try EEEEEE short
  1.2787 +            else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
  1.2788 +                                   fSymbols->fShorterWeekdays, fSymbols->fShorterWeekdaysCount, NULL, cal)) > 0)
  1.2789 +                return newStart;
  1.2790 +            // EEEEEE short failed, now try EEEEE narrow
  1.2791 +            else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
  1.2792 +                                   fSymbols->fNarrowWeekdays, fSymbols->fNarrowWeekdaysCount, NULL, cal)) > 0)
  1.2793 +                return newStart;
  1.2794 +            else if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status) || patternCharIndex == UDAT_DAY_OF_WEEK_FIELD)
  1.2795 +                return newStart;
  1.2796 +            // else we allowing parsing as number, below
  1.2797 +        }
  1.2798 +        break;
  1.2799 +
  1.2800 +    case UDAT_STANDALONE_DAY_FIELD:
  1.2801 +        {
  1.2802 +            if (gotNumber) // c or cc
  1.2803 +            {
  1.2804 +                // [We computed 'value' above.]
  1.2805 +                cal.set(UCAL_DOW_LOCAL, value);
  1.2806 +                return pos.getIndex();
  1.2807 +            }
  1.2808 +            // Want to be able to parse both short and long forms.
  1.2809 +            // Try count == 4 (cccc) first:
  1.2810 +            int32_t newStart = 0;
  1.2811 +            if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
  1.2812 +                                      fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, NULL, cal)) > 0)
  1.2813 +                return newStart;
  1.2814 +            else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
  1.2815 +                                          fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, NULL, cal)) > 0)
  1.2816 +                return newStart;
  1.2817 +            else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
  1.2818 +                                          fSymbols->fStandaloneShorterWeekdays, fSymbols->fStandaloneShorterWeekdaysCount, NULL, cal)) > 0)
  1.2819 +                return newStart;
  1.2820 +            else if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
  1.2821 +                return newStart;
  1.2822 +            // else we allowing parsing as number, below
  1.2823 +        }
  1.2824 +        break;
  1.2825 +
  1.2826 +    case UDAT_AM_PM_FIELD:
  1.2827 +        return matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, NULL, cal);
  1.2828 +
  1.2829 +    case UDAT_HOUR1_FIELD:
  1.2830 +        // [We computed 'value' above.]
  1.2831 +        if (value == cal.getLeastMaximum(UCAL_HOUR)+1)
  1.2832 +            value = 0;
  1.2833 +            
  1.2834 +        // fall through to set field
  1.2835 +            
  1.2836 +    case UDAT_HOUR0_FIELD:
  1.2837 +        cal.set(UCAL_HOUR, value);
  1.2838 +        return pos.getIndex();
  1.2839 +
  1.2840 +    case UDAT_QUARTER_FIELD:
  1.2841 +        if (gotNumber) // i.e., Q or QQ.
  1.2842 +        {
  1.2843 +            // Don't want to parse the month if it is a string
  1.2844 +            // while pattern uses numeric style: Q or QQ.
  1.2845 +            // [We computed 'value' above.]
  1.2846 +            cal.set(UCAL_MONTH, (value - 1) * 3);
  1.2847 +            return pos.getIndex();
  1.2848 +        } else {
  1.2849 +            // count >= 3 // i.e., QQQ or QQQQ
  1.2850 +            // Want to be able to parse both short and long forms.
  1.2851 +            // Try count == 4 first:
  1.2852 +            int32_t newStart = 0;
  1.2853 +
  1.2854 +            if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
  1.2855 +                                      fSymbols->fQuarters, fSymbols->fQuartersCount, cal)) > 0)
  1.2856 +                return newStart;
  1.2857 +            else if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
  1.2858 +                                          fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal)) > 0)
  1.2859 +                return newStart;
  1.2860 +            else if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
  1.2861 +                return newStart;
  1.2862 +            // else we allowing parsing as number, below
  1.2863 +        }
  1.2864 +        break;
  1.2865 +
  1.2866 +    case UDAT_STANDALONE_QUARTER_FIELD:
  1.2867 +        if (gotNumber) // i.e., q or qq.
  1.2868 +        {
  1.2869 +            // Don't want to parse the month if it is a string
  1.2870 +            // while pattern uses numeric style: q or q.
  1.2871 +            // [We computed 'value' above.]
  1.2872 +            cal.set(UCAL_MONTH, (value - 1) * 3);
  1.2873 +            return pos.getIndex();
  1.2874 +        } else {
  1.2875 +            // count >= 3 // i.e., qqq or qqqq
  1.2876 +            // Want to be able to parse both short and long forms.
  1.2877 +            // Try count == 4 first:
  1.2878 +            int32_t newStart = 0;
  1.2879 +
  1.2880 +            if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
  1.2881 +                                      fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount, cal)) > 0)
  1.2882 +                return newStart;
  1.2883 +            else if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
  1.2884 +                                          fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal)) > 0)
  1.2885 +                return newStart;
  1.2886 +            else if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
  1.2887 +                return newStart;
  1.2888 +            // else we allowing parsing as number, below
  1.2889 +        }
  1.2890 +        break;
  1.2891 +
  1.2892 +    case UDAT_TIMEZONE_FIELD: // 'z'
  1.2893 +        {
  1.2894 +            UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.2895 +            UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_SPECIFIC_SHORT : UTZFMT_STYLE_SPECIFIC_LONG;
  1.2896 +            TimeZone *tz  = tzFormat()->parse(style, text, pos, &tzTimeType);
  1.2897 +            if (tz != NULL) {
  1.2898 +                ((SimpleDateFormat*)this)->tztype = tzTimeType;
  1.2899 +                cal.adoptTimeZone(tz);
  1.2900 +                return pos.getIndex();
  1.2901 +            }
  1.2902 +        }
  1.2903 +        break;
  1.2904 +    case UDAT_TIMEZONE_RFC_FIELD: // 'Z'
  1.2905 +        {
  1.2906 +            UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.2907 +            UTimeZoneFormatStyle style = (count < 4) ?
  1.2908 +                UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL : ((count == 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL: UTZFMT_STYLE_LOCALIZED_GMT);
  1.2909 +            TimeZone *tz  = tzFormat()->parse(style, text, pos, &tzTimeType);
  1.2910 +            if (tz != NULL) {
  1.2911 +                ((SimpleDateFormat*)this)->tztype = tzTimeType;
  1.2912 +                cal.adoptTimeZone(tz);
  1.2913 +                return pos.getIndex();
  1.2914 +            }
  1.2915 +            return -start;
  1.2916 +        }
  1.2917 +    case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
  1.2918 +        {
  1.2919 +            UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.2920 +            UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_GENERIC_SHORT : UTZFMT_STYLE_GENERIC_LONG;
  1.2921 +            TimeZone *tz  = tzFormat()->parse(style, text, pos, &tzTimeType);
  1.2922 +            if (tz != NULL) {
  1.2923 +                ((SimpleDateFormat*)this)->tztype = tzTimeType;
  1.2924 +                cal.adoptTimeZone(tz);
  1.2925 +                return pos.getIndex();
  1.2926 +            }
  1.2927 +            return -start;
  1.2928 +        }
  1.2929 +    case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
  1.2930 +        {
  1.2931 +            UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.2932 +            UTimeZoneFormatStyle style;
  1.2933 +            switch (count) {
  1.2934 +            case 1:
  1.2935 +                style = UTZFMT_STYLE_ZONE_ID_SHORT;
  1.2936 +                break;
  1.2937 +            case 2:
  1.2938 +                style = UTZFMT_STYLE_ZONE_ID;
  1.2939 +                break;
  1.2940 +            case 3:
  1.2941 +                style = UTZFMT_STYLE_EXEMPLAR_LOCATION;
  1.2942 +                break;
  1.2943 +            default:
  1.2944 +                style = UTZFMT_STYLE_GENERIC_LOCATION;
  1.2945 +                break;
  1.2946 +            }
  1.2947 +            TimeZone *tz  = tzFormat()->parse(style, text, pos, &tzTimeType);
  1.2948 +            if (tz != NULL) {
  1.2949 +                ((SimpleDateFormat*)this)->tztype = tzTimeType;
  1.2950 +                cal.adoptTimeZone(tz);
  1.2951 +                return pos.getIndex();
  1.2952 +            }
  1.2953 +            return -start;
  1.2954 +        }
  1.2955 +    case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O'
  1.2956 +        {
  1.2957 +            UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.2958 +            UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT : UTZFMT_STYLE_LOCALIZED_GMT;
  1.2959 +            TimeZone *tz  = tzFormat()->parse(style, text, pos, &tzTimeType);
  1.2960 +            if (tz != NULL) {
  1.2961 +                ((SimpleDateFormat*)this)->tztype = tzTimeType;
  1.2962 +                cal.adoptTimeZone(tz);
  1.2963 +                return pos.getIndex();
  1.2964 +            }
  1.2965 +            return -start;
  1.2966 +        }
  1.2967 +    case UDAT_TIMEZONE_ISO_FIELD: // 'X'
  1.2968 +        {
  1.2969 +            UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.2970 +            UTimeZoneFormatStyle style;
  1.2971 +            switch (count) {
  1.2972 +            case 1:
  1.2973 +                style = UTZFMT_STYLE_ISO_BASIC_SHORT;
  1.2974 +                break;
  1.2975 +            case 2:
  1.2976 +                style = UTZFMT_STYLE_ISO_BASIC_FIXED;
  1.2977 +                break;
  1.2978 +            case 3:
  1.2979 +                style = UTZFMT_STYLE_ISO_EXTENDED_FIXED;
  1.2980 +                break;
  1.2981 +            case 4:
  1.2982 +                style = UTZFMT_STYLE_ISO_BASIC_FULL;
  1.2983 +                break;
  1.2984 +            default:
  1.2985 +                style = UTZFMT_STYLE_ISO_EXTENDED_FULL;
  1.2986 +                break;
  1.2987 +            }
  1.2988 +            TimeZone *tz  = tzFormat()->parse(style, text, pos, &tzTimeType);
  1.2989 +            if (tz != NULL) {
  1.2990 +                ((SimpleDateFormat*)this)->tztype = tzTimeType;
  1.2991 +                cal.adoptTimeZone(tz);
  1.2992 +                return pos.getIndex();
  1.2993 +            }
  1.2994 +            return -start;
  1.2995 +        }
  1.2996 +    case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x'
  1.2997 +        {
  1.2998 +            UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.2999 +            UTimeZoneFormatStyle style;
  1.3000 +            switch (count) {
  1.3001 +            case 1:
  1.3002 +                style = UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT;
  1.3003 +                break;
  1.3004 +            case 2:
  1.3005 +                style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED;
  1.3006 +                break;
  1.3007 +            case 3:
  1.3008 +                style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED;
  1.3009 +                break;
  1.3010 +            case 4:
  1.3011 +                style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL;
  1.3012 +                break;
  1.3013 +            default:
  1.3014 +                style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL;
  1.3015 +                break;
  1.3016 +            }
  1.3017 +            TimeZone *tz  = tzFormat()->parse(style, text, pos, &tzTimeType);
  1.3018 +            if (tz != NULL) {
  1.3019 +                ((SimpleDateFormat*)this)->tztype = tzTimeType;
  1.3020 +                cal.adoptTimeZone(tz);
  1.3021 +                return pos.getIndex();
  1.3022 +            }
  1.3023 +            return -start;
  1.3024 +        }
  1.3025 +
  1.3026 +    default:
  1.3027 +        // Handle "generic" fields
  1.3028 +        // this is now handled below, outside the switch block
  1.3029 +        break;
  1.3030 +    }
  1.3031 +    // Handle "generic" fields:
  1.3032 +    // switch default case now handled here (outside switch block) to allow
  1.3033 +    // parsing of some string fields as digits for lenient case
  1.3034 +
  1.3035 +    int32_t parseStart = pos.getIndex();
  1.3036 +    const UnicodeString* src;
  1.3037 +    if (obeyCount) {
  1.3038 +        if ((start+count) > text.length()) {
  1.3039 +            return -start;
  1.3040 +        }
  1.3041 +        text.extractBetween(0, start + count, temp);
  1.3042 +        src = &temp;
  1.3043 +    } else {
  1.3044 +        src = &text;
  1.3045 +    }
  1.3046 +    parseInt(*src, number, pos, allowNegative,currentNumberFormat);
  1.3047 +    if (pos.getIndex() != parseStart) {
  1.3048 +        int32_t value = number.getLong();
  1.3049 +
  1.3050 +        // Don't need suffix processing here (as in number processing at the beginning of the function);
  1.3051 +        // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
  1.3052 +
  1.3053 +        if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) {
  1.3054 +            // Check the range of the value
  1.3055 +            int32_t bias = gFieldRangeBias[patternCharIndex];
  1.3056 +            if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) {
  1.3057 +                return -start;
  1.3058 +            }
  1.3059 +        }
  1.3060 +
  1.3061 +        // For the following, need to repeat some of the "if (gotNumber)" code above:
  1.3062 +        // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
  1.3063 +        // UDAT_[STANDALONE_]QUARTER_FIELD
  1.3064 +        switch (patternCharIndex) {
  1.3065 +        case UDAT_MONTH_FIELD:
  1.3066 +            // See notes under UDAT_MONTH_FIELD case above
  1.3067 +            if (!strcmp(cal.getType(),"hebrew")) {
  1.3068 +                HebrewCalendar *hc = (HebrewCalendar*)&cal;
  1.3069 +                if (cal.isSet(UCAL_YEAR)) {
  1.3070 +                   UErrorCode status = U_ZERO_ERROR;
  1.3071 +                   if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6) {
  1.3072 +                       cal.set(UCAL_MONTH, value);
  1.3073 +                   } else {
  1.3074 +                       cal.set(UCAL_MONTH, value - 1);
  1.3075 +                   }
  1.3076 +                } else {
  1.3077 +                    saveHebrewMonth = value;
  1.3078 +                }
  1.3079 +            } else {
  1.3080 +                cal.set(UCAL_MONTH, value - 1);
  1.3081 +            }
  1.3082 +            break;
  1.3083 +        case UDAT_STANDALONE_MONTH_FIELD:
  1.3084 +            cal.set(UCAL_MONTH, value - 1);
  1.3085 +            break;
  1.3086 +        case UDAT_DOW_LOCAL_FIELD:
  1.3087 +        case UDAT_STANDALONE_DAY_FIELD:
  1.3088 +            cal.set(UCAL_DOW_LOCAL, value);
  1.3089 +            break;
  1.3090 +        case UDAT_QUARTER_FIELD:
  1.3091 +        case UDAT_STANDALONE_QUARTER_FIELD:
  1.3092 +             cal.set(UCAL_MONTH, (value - 1) * 3);
  1.3093 +             break;
  1.3094 +        default:
  1.3095 +            cal.set(field, value);
  1.3096 +            break;
  1.3097 +        }
  1.3098 +        return pos.getIndex();
  1.3099 +    }
  1.3100 +    return -start;
  1.3101 +}
  1.3102 +
  1.3103 +/**
  1.3104 + * Parse an integer using fNumberFormat.  This method is semantically
  1.3105 + * const, but actually may modify fNumberFormat.
  1.3106 + */
  1.3107 +void SimpleDateFormat::parseInt(const UnicodeString& text,
  1.3108 +                                Formattable& number,
  1.3109 +                                ParsePosition& pos,
  1.3110 +                                UBool allowNegative,
  1.3111 +                                NumberFormat *fmt) const {
  1.3112 +    parseInt(text, number, -1, pos, allowNegative,fmt);
  1.3113 +}
  1.3114 +
  1.3115 +/**
  1.3116 + * Parse an integer using fNumberFormat up to maxDigits.
  1.3117 + */
  1.3118 +void SimpleDateFormat::parseInt(const UnicodeString& text,
  1.3119 +                                Formattable& number,
  1.3120 +                                int32_t maxDigits,
  1.3121 +                                ParsePosition& pos,
  1.3122 +                                UBool allowNegative,
  1.3123 +                                NumberFormat *fmt) const {
  1.3124 +    UnicodeString oldPrefix;
  1.3125 +    DecimalFormat* df = NULL;
  1.3126 +    if (!allowNegative && (df = dynamic_cast<DecimalFormat*>(fmt)) != NULL) {
  1.3127 +        df->getNegativePrefix(oldPrefix);
  1.3128 +        df->setNegativePrefix(UnicodeString(TRUE, SUPPRESS_NEGATIVE_PREFIX, -1));
  1.3129 +    }
  1.3130 +    int32_t oldPos = pos.getIndex();
  1.3131 +    fmt->parse(text, number, pos);
  1.3132 +    if (df != NULL) {
  1.3133 +        df->setNegativePrefix(oldPrefix);
  1.3134 +    }
  1.3135 +
  1.3136 +    if (maxDigits > 0) {
  1.3137 +        // adjust the result to fit into
  1.3138 +        // the maxDigits and move the position back
  1.3139 +        int32_t nDigits = pos.getIndex() - oldPos;
  1.3140 +        if (nDigits > maxDigits) {
  1.3141 +            int32_t val = number.getLong();
  1.3142 +            nDigits -= maxDigits;
  1.3143 +            while (nDigits > 0) {
  1.3144 +                val /= 10;
  1.3145 +                nDigits--;
  1.3146 +            }
  1.3147 +            pos.setIndex(oldPos + maxDigits);
  1.3148 +            number.setLong(val);
  1.3149 +        }
  1.3150 +    }
  1.3151 +}
  1.3152 +
  1.3153 +//----------------------------------------------------------------------
  1.3154 +
  1.3155 +void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern,
  1.3156 +                                        UnicodeString& translatedPattern,
  1.3157 +                                        const UnicodeString& from,
  1.3158 +                                        const UnicodeString& to,
  1.3159 +                                        UErrorCode& status)
  1.3160 +{
  1.3161 +  // run through the pattern and convert any pattern symbols from the version
  1.3162 +  // in "from" to the corresponding character ion "to".  This code takes
  1.3163 +  // quoted strings into account (it doesn't try to translate them), and it signals
  1.3164 +  // an error if a particular "pattern character" doesn't appear in "from".
  1.3165 +  // Depending on the values of "from" and "to" this can convert from generic
  1.3166 +  // to localized patterns or localized to generic.
  1.3167 +  if (U_FAILURE(status))
  1.3168 +    return;
  1.3169 +
  1.3170 +  translatedPattern.remove();
  1.3171 +  UBool inQuote = FALSE;
  1.3172 +  for (int32_t i = 0; i < originalPattern.length(); ++i) {
  1.3173 +    UChar c = originalPattern[i];
  1.3174 +    if (inQuote) {
  1.3175 +      if (c == QUOTE)
  1.3176 +    inQuote = FALSE;
  1.3177 +    }
  1.3178 +    else {
  1.3179 +      if (c == QUOTE)
  1.3180 +    inQuote = TRUE;
  1.3181 +      else if ((c >= 0x0061 /*'a'*/ && c <= 0x007A) /*'z'*/
  1.3182 +           || (c >= 0x0041 /*'A'*/ && c <= 0x005A /*'Z'*/)) {
  1.3183 +    int32_t ci = from.indexOf(c);
  1.3184 +    if (ci == -1) {
  1.3185 +      status = U_INVALID_FORMAT_ERROR;
  1.3186 +      return;
  1.3187 +    }
  1.3188 +    c = to[ci];
  1.3189 +      }
  1.3190 +    }
  1.3191 +    translatedPattern += c;
  1.3192 +  }
  1.3193 +  if (inQuote) {
  1.3194 +    status = U_INVALID_FORMAT_ERROR;
  1.3195 +    return;
  1.3196 +  }
  1.3197 +}
  1.3198 +
  1.3199 +//----------------------------------------------------------------------
  1.3200 +
  1.3201 +UnicodeString&
  1.3202 +SimpleDateFormat::toPattern(UnicodeString& result) const
  1.3203 +{
  1.3204 +    result = fPattern;
  1.3205 +    return result;
  1.3206 +}
  1.3207 +
  1.3208 +//----------------------------------------------------------------------
  1.3209 +
  1.3210 +UnicodeString&
  1.3211 +SimpleDateFormat::toLocalizedPattern(UnicodeString& result,
  1.3212 +                                     UErrorCode& status) const
  1.3213 +{
  1.3214 +    translatePattern(fPattern, result,
  1.3215 +                     UnicodeString(DateFormatSymbols::getPatternUChars()),
  1.3216 +                     fSymbols->fLocalPatternChars, status);
  1.3217 +    return result;
  1.3218 +}
  1.3219 +
  1.3220 +//----------------------------------------------------------------------
  1.3221 +
  1.3222 +void
  1.3223 +SimpleDateFormat::applyPattern(const UnicodeString& pattern)
  1.3224 +{
  1.3225 +    fPattern = pattern;
  1.3226 +}
  1.3227 +
  1.3228 +//----------------------------------------------------------------------
  1.3229 +
  1.3230 +void
  1.3231 +SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern,
  1.3232 +                                        UErrorCode &status)
  1.3233 +{
  1.3234 +    translatePattern(pattern, fPattern,
  1.3235 +                     fSymbols->fLocalPatternChars,
  1.3236 +                     UnicodeString(DateFormatSymbols::getPatternUChars()), status);
  1.3237 +}
  1.3238 +
  1.3239 +//----------------------------------------------------------------------
  1.3240 +
  1.3241 +const DateFormatSymbols*
  1.3242 +SimpleDateFormat::getDateFormatSymbols() const
  1.3243 +{
  1.3244 +    return fSymbols;
  1.3245 +}
  1.3246 +
  1.3247 +//----------------------------------------------------------------------
  1.3248 +
  1.3249 +void
  1.3250 +SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols* newFormatSymbols)
  1.3251 +{
  1.3252 +    delete fSymbols;
  1.3253 +    fSymbols = newFormatSymbols;
  1.3254 +}
  1.3255 +
  1.3256 +//----------------------------------------------------------------------
  1.3257 +void
  1.3258 +SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols)
  1.3259 +{
  1.3260 +    delete fSymbols;
  1.3261 +    fSymbols = new DateFormatSymbols(newFormatSymbols);
  1.3262 +}
  1.3263 +
  1.3264 +//----------------------------------------------------------------------
  1.3265 +const TimeZoneFormat*
  1.3266 +SimpleDateFormat::getTimeZoneFormat(void) const {
  1.3267 +    return (const TimeZoneFormat*)tzFormat();
  1.3268 +}
  1.3269 +
  1.3270 +//----------------------------------------------------------------------
  1.3271 +void
  1.3272 +SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat* timeZoneFormatToAdopt)
  1.3273 +{
  1.3274 +    delete fTimeZoneFormat;
  1.3275 +    fTimeZoneFormat = timeZoneFormatToAdopt;
  1.3276 +}
  1.3277 +
  1.3278 +//----------------------------------------------------------------------
  1.3279 +void
  1.3280 +SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat& newTimeZoneFormat)
  1.3281 +{
  1.3282 +    delete fTimeZoneFormat;
  1.3283 +    fTimeZoneFormat = new TimeZoneFormat(newTimeZoneFormat);
  1.3284 +}
  1.3285 +
  1.3286 +//----------------------------------------------------------------------
  1.3287 +
  1.3288 +
  1.3289 +void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt)
  1.3290 +{
  1.3291 +  UErrorCode status = U_ZERO_ERROR;
  1.3292 +  DateFormat::adoptCalendar(calendarToAdopt);
  1.3293 +  delete fSymbols;
  1.3294 +  fSymbols=NULL;
  1.3295 +  initializeSymbols(fLocale, fCalendar, status);  // we need new symbols
  1.3296 +  initializeDefaultCentury();  // we need a new century (possibly)
  1.3297 +}
  1.3298 +
  1.3299 +
  1.3300 +//----------------------------------------------------------------------
  1.3301 +
  1.3302 +
  1.3303 +void SimpleDateFormat::setContext(UDisplayContext value, UErrorCode& status)
  1.3304 +{
  1.3305 +    if (U_FAILURE(status))
  1.3306 +        return;
  1.3307 +    if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) {
  1.3308 +        fCapitalizationContext = value;
  1.3309 +    } else {
  1.3310 +        status = U_ILLEGAL_ARGUMENT_ERROR;
  1.3311 +   }
  1.3312 +}
  1.3313 +
  1.3314 +
  1.3315 +//----------------------------------------------------------------------
  1.3316 +
  1.3317 +
  1.3318 +UDisplayContext SimpleDateFormat::getContext(UDisplayContextType type, UErrorCode& status) const
  1.3319 +{
  1.3320 +    if (U_FAILURE(status))
  1.3321 +        return (UDisplayContext)0;
  1.3322 +    if (type != UDISPCTX_TYPE_CAPITALIZATION) {
  1.3323 +        status = U_ILLEGAL_ARGUMENT_ERROR;
  1.3324 +        return (UDisplayContext)0;
  1.3325 +    }
  1.3326 +    return fCapitalizationContext;
  1.3327 +}
  1.3328 +
  1.3329 +
  1.3330 +//----------------------------------------------------------------------
  1.3331 +
  1.3332 +
  1.3333 +UBool
  1.3334 +SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field) const {
  1.3335 +    return isFieldUnitIgnored(fPattern, field);
  1.3336 +}
  1.3337 +
  1.3338 +
  1.3339 +UBool
  1.3340 +SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern,
  1.3341 +                                     UCalendarDateFields field) {
  1.3342 +    int32_t fieldLevel = fgCalendarFieldToLevel[field];
  1.3343 +    int32_t level;
  1.3344 +    UChar ch;
  1.3345 +    UBool inQuote = FALSE;
  1.3346 +    UChar prevCh = 0;
  1.3347 +    int32_t count = 0;
  1.3348 +
  1.3349 +    for (int32_t i = 0; i < pattern.length(); ++i) {
  1.3350 +        ch = pattern[i];
  1.3351 +        if (ch != prevCh && count > 0) {
  1.3352 +            level = fgPatternCharToLevel[prevCh - PATTERN_CHAR_BASE];
  1.3353 +            // the larger the level, the smaller the field unit.
  1.3354 +            if ( fieldLevel <= level ) {
  1.3355 +                return FALSE;
  1.3356 +            }
  1.3357 +            count = 0;
  1.3358 +        }
  1.3359 +        if (ch == QUOTE) {
  1.3360 +            if ((i+1) < pattern.length() && pattern[i+1] == QUOTE) {
  1.3361 +                ++i;
  1.3362 +            } else {
  1.3363 +                inQuote = ! inQuote;
  1.3364 +            }
  1.3365 +        }
  1.3366 +        else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
  1.3367 +                    || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
  1.3368 +            prevCh = ch;
  1.3369 +            ++count;
  1.3370 +        }
  1.3371 +    }
  1.3372 +    if ( count > 0 ) {
  1.3373 +        // last item
  1.3374 +        level = fgPatternCharToLevel[prevCh - PATTERN_CHAR_BASE];
  1.3375 +            if ( fieldLevel <= level ) {
  1.3376 +                return FALSE;
  1.3377 +            }
  1.3378 +    }
  1.3379 +    return TRUE;
  1.3380 +}
  1.3381 +
  1.3382 +//----------------------------------------------------------------------
  1.3383 +
  1.3384 +const Locale&
  1.3385 +SimpleDateFormat::getSmpFmtLocale(void) const {
  1.3386 +    return fLocale;
  1.3387 +}
  1.3388 +
  1.3389 +//----------------------------------------------------------------------
  1.3390 +
  1.3391 +int32_t
  1.3392 +SimpleDateFormat::checkIntSuffix(const UnicodeString& text, int32_t start,
  1.3393 +                                 int32_t patLoc, UBool isNegative) const {
  1.3394 +    // local variables
  1.3395 +    UnicodeString suf;
  1.3396 +    int32_t patternMatch;
  1.3397 +    int32_t textPreMatch;
  1.3398 +    int32_t textPostMatch;
  1.3399 +
  1.3400 +    // check that we are still in range
  1.3401 +    if ( (start > text.length()) ||
  1.3402 +         (start < 0) ||
  1.3403 +         (patLoc < 0) ||
  1.3404 +         (patLoc > fPattern.length())) {
  1.3405 +        // out of range, don't advance location in text
  1.3406 +        return start;
  1.3407 +    }
  1.3408 +
  1.3409 +    // get the suffix
  1.3410 +    DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fNumberFormat);
  1.3411 +    if (decfmt != NULL) {
  1.3412 +        if (isNegative) {
  1.3413 +            suf = decfmt->getNegativeSuffix(suf);
  1.3414 +        }
  1.3415 +        else {
  1.3416 +            suf = decfmt->getPositiveSuffix(suf);
  1.3417 +        }
  1.3418 +    }
  1.3419 +
  1.3420 +    // check for suffix
  1.3421 +    if (suf.length() <= 0) {
  1.3422 +        return start;
  1.3423 +    }
  1.3424 +
  1.3425 +    // check suffix will be encountered in the pattern
  1.3426 +    patternMatch = compareSimpleAffix(suf,fPattern,patLoc);
  1.3427 +
  1.3428 +    // check if a suffix will be encountered in the text
  1.3429 +    textPreMatch = compareSimpleAffix(suf,text,start);
  1.3430 +
  1.3431 +    // check if a suffix was encountered in the text
  1.3432 +    textPostMatch = compareSimpleAffix(suf,text,start-suf.length());
  1.3433 +
  1.3434 +    // check for suffix match
  1.3435 +    if ((textPreMatch >= 0) && (patternMatch >= 0) && (textPreMatch == patternMatch)) {
  1.3436 +        return start;
  1.3437 +    }
  1.3438 +    else if ((textPostMatch >= 0) && (patternMatch >= 0) && (textPostMatch == patternMatch)) {
  1.3439 +        return  start - suf.length();
  1.3440 +    }
  1.3441 +
  1.3442 +    // should not get here
  1.3443 +    return start;
  1.3444 +}
  1.3445 +
  1.3446 +//----------------------------------------------------------------------
  1.3447 +
  1.3448 +int32_t
  1.3449 +SimpleDateFormat::compareSimpleAffix(const UnicodeString& affix,
  1.3450 +                   const UnicodeString& input,
  1.3451 +                   int32_t pos) const {
  1.3452 +    int32_t start = pos;
  1.3453 +    for (int32_t i=0; i<affix.length(); ) {
  1.3454 +        UChar32 c = affix.char32At(i);
  1.3455 +        int32_t len = U16_LENGTH(c);
  1.3456 +        if (PatternProps::isWhiteSpace(c)) {
  1.3457 +            // We may have a pattern like: \u200F \u0020
  1.3458 +            //        and input text like: \u200F \u0020
  1.3459 +            // Note that U+200F and U+0020 are Pattern_White_Space but only
  1.3460 +            // U+0020 is UWhiteSpace.  So we have to first do a direct
  1.3461 +            // match of the run of Pattern_White_Space in the pattern,
  1.3462 +            // then match any extra characters.
  1.3463 +            UBool literalMatch = FALSE;
  1.3464 +            while (pos < input.length() &&
  1.3465 +                   input.char32At(pos) == c) {
  1.3466 +                literalMatch = TRUE;
  1.3467 +                i += len;
  1.3468 +                pos += len;
  1.3469 +                if (i == affix.length()) {
  1.3470 +                    break;
  1.3471 +                }
  1.3472 +                c = affix.char32At(i);
  1.3473 +                len = U16_LENGTH(c);
  1.3474 +                if (!PatternProps::isWhiteSpace(c)) {
  1.3475 +                    break;
  1.3476 +                }
  1.3477 +            }
  1.3478 +
  1.3479 +            // Advance over run in pattern
  1.3480 +            i = skipPatternWhiteSpace(affix, i);
  1.3481 +
  1.3482 +            // Advance over run in input text
  1.3483 +            // Must see at least one white space char in input,
  1.3484 +            // unless we've already matched some characters literally.
  1.3485 +            int32_t s = pos;
  1.3486 +            pos = skipUWhiteSpace(input, pos);
  1.3487 +            if (pos == s && !literalMatch) {
  1.3488 +                return -1;
  1.3489 +            }
  1.3490 +
  1.3491 +            // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
  1.3492 +            // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
  1.3493 +            // is also in the affix.
  1.3494 +            i = skipUWhiteSpace(affix, i);
  1.3495 +        } else {
  1.3496 +            if (pos < input.length() &&
  1.3497 +                input.char32At(pos) == c) {
  1.3498 +                i += len;
  1.3499 +                pos += len;
  1.3500 +            } else {
  1.3501 +                return -1;
  1.3502 +            }
  1.3503 +        }
  1.3504 +    }
  1.3505 +    return pos - start;
  1.3506 +}
  1.3507 +
  1.3508 +//----------------------------------------------------------------------
  1.3509 +
  1.3510 +int32_t
  1.3511 +SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) const {
  1.3512 +    const UChar* s = text.getBuffer();
  1.3513 +    return (int32_t)(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s);
  1.3514 +}
  1.3515 +
  1.3516 +//----------------------------------------------------------------------
  1.3517 +
  1.3518 +int32_t
  1.3519 +SimpleDateFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) const {
  1.3520 +    while (pos < text.length()) {
  1.3521 +        UChar32 c = text.char32At(pos);
  1.3522 +        if (!u_isUWhiteSpace(c)) {
  1.3523 +            break;
  1.3524 +        }
  1.3525 +        pos += U16_LENGTH(c);
  1.3526 +    }
  1.3527 +    return pos;
  1.3528 +}
  1.3529 +
  1.3530 +//----------------------------------------------------------------------
  1.3531 +
  1.3532 +// Lazy TimeZoneFormat instantiation, semantically const.
  1.3533 +TimeZoneFormat *
  1.3534 +SimpleDateFormat::tzFormat() const {
  1.3535 +    if (fTimeZoneFormat == NULL) {
  1.3536 +        umtx_lock(&LOCK);
  1.3537 +        {
  1.3538 +            if (fTimeZoneFormat == NULL) {
  1.3539 +                UErrorCode status = U_ZERO_ERROR;
  1.3540 +                TimeZoneFormat *tzfmt = TimeZoneFormat::createInstance(fLocale, status);
  1.3541 +                if (U_FAILURE(status)) {
  1.3542 +                    return NULL;
  1.3543 +                }
  1.3544 +
  1.3545 +                const_cast<SimpleDateFormat *>(this)->fTimeZoneFormat = tzfmt;
  1.3546 +            }
  1.3547 +        }
  1.3548 +        umtx_unlock(&LOCK);
  1.3549 +    }
  1.3550 +    return fTimeZoneFormat;
  1.3551 +}
  1.3552 +
  1.3553 +U_NAMESPACE_END
  1.3554 +
  1.3555 +#endif /* #if !UCONFIG_NO_FORMATTING */
  1.3556 +
  1.3557 +//eof

mercurial