|
1 /* |
|
2 ******************************************************************************* |
|
3 * Copyright (C) 1997-2013, International Business Machines Corporation and * |
|
4 * others. All Rights Reserved. * |
|
5 ******************************************************************************* |
|
6 * |
|
7 * File SMPDTFMT.CPP |
|
8 * |
|
9 * Modification History: |
|
10 * |
|
11 * Date Name Description |
|
12 * 02/19/97 aliu Converted from java. |
|
13 * 03/31/97 aliu Modified extensively to work with 50 locales. |
|
14 * 04/01/97 aliu Added support for centuries. |
|
15 * 07/09/97 helena Made ParsePosition into a class. |
|
16 * 07/21/98 stephen Added initializeDefaultCentury. |
|
17 * Removed getZoneIndex (added in DateFormatSymbols) |
|
18 * Removed subParseLong |
|
19 * Removed chk |
|
20 * 02/22/99 stephen Removed character literals for EBCDIC safety |
|
21 * 10/14/99 aliu Updated 2-digit year parsing so that only "00" thru |
|
22 * "99" are recognized. {j28 4182066} |
|
23 * 11/15/99 weiv Added support for week of year/day of week format |
|
24 ******************************************************************************** |
|
25 */ |
|
26 |
|
27 #define ZID_KEY_MAX 128 |
|
28 |
|
29 #include "unicode/utypes.h" |
|
30 |
|
31 #if !UCONFIG_NO_FORMATTING |
|
32 |
|
33 #include "unicode/smpdtfmt.h" |
|
34 #include "unicode/dtfmtsym.h" |
|
35 #include "unicode/ures.h" |
|
36 #include "unicode/msgfmt.h" |
|
37 #include "unicode/calendar.h" |
|
38 #include "unicode/gregocal.h" |
|
39 #include "unicode/timezone.h" |
|
40 #include "unicode/decimfmt.h" |
|
41 #include "unicode/dcfmtsym.h" |
|
42 #include "unicode/uchar.h" |
|
43 #include "unicode/uniset.h" |
|
44 #include "unicode/ustring.h" |
|
45 #include "unicode/basictz.h" |
|
46 #include "unicode/simpletz.h" |
|
47 #include "unicode/rbtz.h" |
|
48 #include "unicode/tzfmt.h" |
|
49 #include "unicode/utf16.h" |
|
50 #include "unicode/vtzone.h" |
|
51 #include "unicode/udisplaycontext.h" |
|
52 #include "olsontz.h" |
|
53 #include "patternprops.h" |
|
54 #include "fphdlimp.h" |
|
55 #include "gregoimp.h" |
|
56 #include "hebrwcal.h" |
|
57 #include "cstring.h" |
|
58 #include "uassert.h" |
|
59 #include "cmemory.h" |
|
60 #include "umutex.h" |
|
61 #include <float.h> |
|
62 #include "smpdtfst.h" |
|
63 |
|
64 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) |
|
65 #include <stdio.h> |
|
66 #endif |
|
67 |
|
68 // ***************************************************************************** |
|
69 // class SimpleDateFormat |
|
70 // ***************************************************************************** |
|
71 |
|
72 U_NAMESPACE_BEGIN |
|
73 |
|
74 static const UChar PATTERN_CHAR_BASE = 0x40; |
|
75 |
|
76 /** |
|
77 * Last-resort string to use for "GMT" when constructing time zone strings. |
|
78 */ |
|
79 // For time zones that have no names, use strings GMT+minutes and |
|
80 // GMT-minutes. For instance, in France the time zone is GMT+60. |
|
81 // Also accepted are GMT+H:MM or GMT-H:MM. |
|
82 // Currently not being used |
|
83 //static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT" |
|
84 //static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+" |
|
85 //static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-" |
|
86 //static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */ |
|
87 //static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */ |
|
88 //static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */ |
|
89 //static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */ |
|
90 //static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */ |
|
91 //static const UChar gUt[] = {0x0055, 0x0054, 0x0000}; // "UT" |
|
92 //static const UChar gUtc[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT" |
|
93 |
|
94 typedef enum GmtPatSize { |
|
95 kGmtLen = 3, |
|
96 kGmtPatLen = 6, |
|
97 kNegHmsLen = 9, |
|
98 kNegHmLen = 6, |
|
99 kPosHmsLen = 9, |
|
100 kPosHmLen = 6, |
|
101 kUtLen = 2, |
|
102 kUtcLen = 3 |
|
103 } GmtPatSize; |
|
104 |
|
105 // Stuff needed for numbering system overrides |
|
106 |
|
107 typedef enum OvrStrType { |
|
108 kOvrStrDate = 0, |
|
109 kOvrStrTime = 1, |
|
110 kOvrStrBoth = 2 |
|
111 } OvrStrType; |
|
112 |
|
113 static const UDateFormatField kDateFields[] = { |
|
114 UDAT_YEAR_FIELD, |
|
115 UDAT_MONTH_FIELD, |
|
116 UDAT_DATE_FIELD, |
|
117 UDAT_DAY_OF_YEAR_FIELD, |
|
118 UDAT_DAY_OF_WEEK_IN_MONTH_FIELD, |
|
119 UDAT_WEEK_OF_YEAR_FIELD, |
|
120 UDAT_WEEK_OF_MONTH_FIELD, |
|
121 UDAT_YEAR_WOY_FIELD, |
|
122 UDAT_EXTENDED_YEAR_FIELD, |
|
123 UDAT_JULIAN_DAY_FIELD, |
|
124 UDAT_STANDALONE_DAY_FIELD, |
|
125 UDAT_STANDALONE_MONTH_FIELD, |
|
126 UDAT_QUARTER_FIELD, |
|
127 UDAT_STANDALONE_QUARTER_FIELD, |
|
128 UDAT_YEAR_NAME_FIELD }; |
|
129 static const int8_t kDateFieldsCount = 15; |
|
130 |
|
131 static const UDateFormatField kTimeFields[] = { |
|
132 UDAT_HOUR_OF_DAY1_FIELD, |
|
133 UDAT_HOUR_OF_DAY0_FIELD, |
|
134 UDAT_MINUTE_FIELD, |
|
135 UDAT_SECOND_FIELD, |
|
136 UDAT_FRACTIONAL_SECOND_FIELD, |
|
137 UDAT_HOUR1_FIELD, |
|
138 UDAT_HOUR0_FIELD, |
|
139 UDAT_MILLISECONDS_IN_DAY_FIELD, |
|
140 UDAT_TIMEZONE_RFC_FIELD, |
|
141 UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD }; |
|
142 static const int8_t kTimeFieldsCount = 10; |
|
143 |
|
144 |
|
145 // This is a pattern-of-last-resort used when we can't load a usable pattern out |
|
146 // of a resource. |
|
147 static const UChar gDefaultPattern[] = |
|
148 { |
|
149 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0 |
|
150 }; /* "yyyyMMdd hh:mm a" */ |
|
151 |
|
152 // This prefix is designed to NEVER MATCH real text, in order to |
|
153 // suppress the parsing of negative numbers. Adjust as needed (if |
|
154 // this becomes valid Unicode). |
|
155 static const UChar SUPPRESS_NEGATIVE_PREFIX[] = {0xAB00, 0}; |
|
156 |
|
157 /** |
|
158 * These are the tags we expect to see in normal resource bundle files associated |
|
159 * with a locale. |
|
160 */ |
|
161 static const char gDateTimePatternsTag[]="DateTimePatterns"; |
|
162 |
|
163 //static const UChar gEtcUTC[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC" |
|
164 static const UChar QUOTE = 0x27; // Single quote |
|
165 |
|
166 /* |
|
167 * The field range check bias for each UDateFormatField. |
|
168 * The bias is added to the minimum and maximum values |
|
169 * before they are compared to the parsed number. |
|
170 * For example, the calendar stores zero-based month numbers |
|
171 * but the parsed month numbers start at 1, so the bias is 1. |
|
172 * |
|
173 * A value of -1 means that the value is not checked. |
|
174 */ |
|
175 static const int32_t gFieldRangeBias[] = { |
|
176 -1, // 'G' - UDAT_ERA_FIELD |
|
177 -1, // 'y' - UDAT_YEAR_FIELD |
|
178 1, // 'M' - UDAT_MONTH_FIELD |
|
179 0, // 'd' - UDAT_DATE_FIELD |
|
180 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD |
|
181 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD |
|
182 0, // 'm' - UDAT_MINUTE_FIELD |
|
183 0, // 's' - UDAT_SEOND_FIELD |
|
184 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?) |
|
185 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?) |
|
186 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?) |
|
187 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?) |
|
188 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?) |
|
189 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?) |
|
190 -1, // 'a' - UDAT_AM_PM_FIELD |
|
191 -1, // 'h' - UDAT_HOUR1_FIELD |
|
192 -1, // 'K' - UDAT_HOUR0_FIELD |
|
193 -1, // 'z' - UDAT_TIMEZONE_FIELD |
|
194 -1, // 'Y' - UDAT_YEAR_WOY_FIELD |
|
195 -1, // 'e' - UDAT_DOW_LOCAL_FIELD |
|
196 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD |
|
197 -1, // 'g' - UDAT_JULIAN_DAY_FIELD |
|
198 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD |
|
199 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD |
|
200 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD |
|
201 0, // 'c' - UDAT_STANDALONE_DAY_FIELD |
|
202 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD |
|
203 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?) |
|
204 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD |
|
205 -1 // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD |
|
206 -1, // 'U' - UDAT_YEAR_NAME_FIELD |
|
207 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD |
|
208 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD |
|
209 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD |
|
210 }; |
|
211 |
|
212 // When calendar uses hebr numbering (i.e. he@calendar=hebrew), |
|
213 // offset the years within the current millenium down to 1-999 |
|
214 static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR = 5000; |
|
215 static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR = 6000; |
|
216 |
|
217 static UMutex LOCK = U_MUTEX_INITIALIZER; |
|
218 |
|
219 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat) |
|
220 |
|
221 //---------------------------------------------------------------------- |
|
222 |
|
223 SimpleDateFormat::~SimpleDateFormat() |
|
224 { |
|
225 delete fSymbols; |
|
226 if (fNumberFormatters) { |
|
227 uprv_free(fNumberFormatters); |
|
228 } |
|
229 if (fTimeZoneFormat) { |
|
230 delete fTimeZoneFormat; |
|
231 } |
|
232 |
|
233 while (fOverrideList) { |
|
234 NSOverride *cur = fOverrideList; |
|
235 fOverrideList = cur->next; |
|
236 delete cur->nf; |
|
237 uprv_free(cur); |
|
238 } |
|
239 } |
|
240 |
|
241 //---------------------------------------------------------------------- |
|
242 |
|
243 SimpleDateFormat::SimpleDateFormat(UErrorCode& status) |
|
244 : fLocale(Locale::getDefault()), |
|
245 fSymbols(NULL), |
|
246 fTimeZoneFormat(NULL), |
|
247 fNumberFormatters(NULL), |
|
248 fOverrideList(NULL), |
|
249 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
|
250 { |
|
251 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
|
252 construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status); |
|
253 initializeDefaultCentury(); |
|
254 } |
|
255 |
|
256 //---------------------------------------------------------------------- |
|
257 |
|
258 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
|
259 UErrorCode &status) |
|
260 : fPattern(pattern), |
|
261 fLocale(Locale::getDefault()), |
|
262 fSymbols(NULL), |
|
263 fTimeZoneFormat(NULL), |
|
264 fNumberFormatters(NULL), |
|
265 fOverrideList(NULL), |
|
266 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
|
267 { |
|
268 fDateOverride.setToBogus(); |
|
269 fTimeOverride.setToBogus(); |
|
270 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
|
271 initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); |
|
272 initialize(fLocale, status); |
|
273 initializeDefaultCentury(); |
|
274 |
|
275 } |
|
276 //---------------------------------------------------------------------- |
|
277 |
|
278 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
|
279 const UnicodeString& override, |
|
280 UErrorCode &status) |
|
281 : fPattern(pattern), |
|
282 fLocale(Locale::getDefault()), |
|
283 fSymbols(NULL), |
|
284 fTimeZoneFormat(NULL), |
|
285 fNumberFormatters(NULL), |
|
286 fOverrideList(NULL), |
|
287 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
|
288 { |
|
289 fDateOverride.setTo(override); |
|
290 fTimeOverride.setToBogus(); |
|
291 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
|
292 initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); |
|
293 initialize(fLocale, status); |
|
294 initializeDefaultCentury(); |
|
295 |
|
296 processOverrideString(fLocale,override,kOvrStrBoth,status); |
|
297 |
|
298 } |
|
299 |
|
300 //---------------------------------------------------------------------- |
|
301 |
|
302 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
|
303 const Locale& locale, |
|
304 UErrorCode& status) |
|
305 : fPattern(pattern), |
|
306 fLocale(locale), |
|
307 fTimeZoneFormat(NULL), |
|
308 fNumberFormatters(NULL), |
|
309 fOverrideList(NULL), |
|
310 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
|
311 { |
|
312 |
|
313 fDateOverride.setToBogus(); |
|
314 fTimeOverride.setToBogus(); |
|
315 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
|
316 |
|
317 initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); |
|
318 initialize(fLocale, status); |
|
319 initializeDefaultCentury(); |
|
320 } |
|
321 |
|
322 //---------------------------------------------------------------------- |
|
323 |
|
324 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
|
325 const UnicodeString& override, |
|
326 const Locale& locale, |
|
327 UErrorCode& status) |
|
328 : fPattern(pattern), |
|
329 fLocale(locale), |
|
330 fTimeZoneFormat(NULL), |
|
331 fNumberFormatters(NULL), |
|
332 fOverrideList(NULL), |
|
333 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
|
334 { |
|
335 |
|
336 fDateOverride.setTo(override); |
|
337 fTimeOverride.setToBogus(); |
|
338 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
|
339 |
|
340 initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); |
|
341 initialize(fLocale, status); |
|
342 initializeDefaultCentury(); |
|
343 |
|
344 processOverrideString(locale,override,kOvrStrBoth,status); |
|
345 |
|
346 } |
|
347 |
|
348 //---------------------------------------------------------------------- |
|
349 |
|
350 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
|
351 DateFormatSymbols* symbolsToAdopt, |
|
352 UErrorCode& status) |
|
353 : fPattern(pattern), |
|
354 fLocale(Locale::getDefault()), |
|
355 fSymbols(symbolsToAdopt), |
|
356 fTimeZoneFormat(NULL), |
|
357 fNumberFormatters(NULL), |
|
358 fOverrideList(NULL), |
|
359 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
|
360 { |
|
361 |
|
362 fDateOverride.setToBogus(); |
|
363 fTimeOverride.setToBogus(); |
|
364 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
|
365 |
|
366 initializeCalendar(NULL,fLocale,status); |
|
367 initialize(fLocale, status); |
|
368 initializeDefaultCentury(); |
|
369 } |
|
370 |
|
371 //---------------------------------------------------------------------- |
|
372 |
|
373 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
|
374 const DateFormatSymbols& symbols, |
|
375 UErrorCode& status) |
|
376 : fPattern(pattern), |
|
377 fLocale(Locale::getDefault()), |
|
378 fSymbols(new DateFormatSymbols(symbols)), |
|
379 fTimeZoneFormat(NULL), |
|
380 fNumberFormatters(NULL), |
|
381 fOverrideList(NULL), |
|
382 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
|
383 { |
|
384 |
|
385 fDateOverride.setToBogus(); |
|
386 fTimeOverride.setToBogus(); |
|
387 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
|
388 |
|
389 initializeCalendar(NULL, fLocale, status); |
|
390 initialize(fLocale, status); |
|
391 initializeDefaultCentury(); |
|
392 } |
|
393 |
|
394 //---------------------------------------------------------------------- |
|
395 |
|
396 // Not for public consumption; used by DateFormat |
|
397 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle, |
|
398 EStyle dateStyle, |
|
399 const Locale& locale, |
|
400 UErrorCode& status) |
|
401 : fLocale(locale), |
|
402 fSymbols(NULL), |
|
403 fTimeZoneFormat(NULL), |
|
404 fNumberFormatters(NULL), |
|
405 fOverrideList(NULL), |
|
406 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
|
407 { |
|
408 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
|
409 construct(timeStyle, dateStyle, fLocale, status); |
|
410 if(U_SUCCESS(status)) { |
|
411 initializeDefaultCentury(); |
|
412 } |
|
413 } |
|
414 |
|
415 //---------------------------------------------------------------------- |
|
416 |
|
417 /** |
|
418 * Not for public consumption; used by DateFormat. This constructor |
|
419 * never fails. If the resource data is not available, it uses the |
|
420 * the last resort symbols. |
|
421 */ |
|
422 SimpleDateFormat::SimpleDateFormat(const Locale& locale, |
|
423 UErrorCode& status) |
|
424 : fPattern(gDefaultPattern), |
|
425 fLocale(locale), |
|
426 fSymbols(NULL), |
|
427 fTimeZoneFormat(NULL), |
|
428 fNumberFormatters(NULL), |
|
429 fOverrideList(NULL), |
|
430 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
|
431 { |
|
432 if (U_FAILURE(status)) return; |
|
433 initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status); |
|
434 if (U_FAILURE(status)) |
|
435 { |
|
436 status = U_ZERO_ERROR; |
|
437 delete fSymbols; |
|
438 // This constructor doesn't fail; it uses last resort data |
|
439 fSymbols = new DateFormatSymbols(status); |
|
440 /* test for NULL */ |
|
441 if (fSymbols == 0) { |
|
442 status = U_MEMORY_ALLOCATION_ERROR; |
|
443 return; |
|
444 } |
|
445 } |
|
446 |
|
447 fDateOverride.setToBogus(); |
|
448 fTimeOverride.setToBogus(); |
|
449 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
|
450 |
|
451 initialize(fLocale, status); |
|
452 if(U_SUCCESS(status)) { |
|
453 initializeDefaultCentury(); |
|
454 } |
|
455 } |
|
456 |
|
457 //---------------------------------------------------------------------- |
|
458 |
|
459 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other) |
|
460 : DateFormat(other), |
|
461 fLocale(other.fLocale), |
|
462 fSymbols(NULL), |
|
463 fTimeZoneFormat(NULL), |
|
464 fNumberFormatters(NULL), |
|
465 fOverrideList(NULL), |
|
466 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
|
467 { |
|
468 UErrorCode status = U_ZERO_ERROR; |
|
469 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
|
470 *this = other; |
|
471 } |
|
472 |
|
473 //---------------------------------------------------------------------- |
|
474 |
|
475 SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other) |
|
476 { |
|
477 if (this == &other) { |
|
478 return *this; |
|
479 } |
|
480 DateFormat::operator=(other); |
|
481 |
|
482 delete fSymbols; |
|
483 fSymbols = NULL; |
|
484 |
|
485 if (other.fSymbols) |
|
486 fSymbols = new DateFormatSymbols(*other.fSymbols); |
|
487 |
|
488 fDefaultCenturyStart = other.fDefaultCenturyStart; |
|
489 fDefaultCenturyStartYear = other.fDefaultCenturyStartYear; |
|
490 fHaveDefaultCentury = other.fHaveDefaultCentury; |
|
491 |
|
492 fPattern = other.fPattern; |
|
493 |
|
494 // TimeZoneFormat in ICU4C only depends on a locale for now |
|
495 if (fLocale != other.fLocale) { |
|
496 delete fTimeZoneFormat; |
|
497 fTimeZoneFormat = NULL; // forces lazy instantiation with the other locale |
|
498 fLocale = other.fLocale; |
|
499 } |
|
500 |
|
501 fCapitalizationContext = other.fCapitalizationContext; |
|
502 |
|
503 return *this; |
|
504 } |
|
505 |
|
506 //---------------------------------------------------------------------- |
|
507 |
|
508 Format* |
|
509 SimpleDateFormat::clone() const |
|
510 { |
|
511 return new SimpleDateFormat(*this); |
|
512 } |
|
513 |
|
514 //---------------------------------------------------------------------- |
|
515 |
|
516 UBool |
|
517 SimpleDateFormat::operator==(const Format& other) const |
|
518 { |
|
519 if (DateFormat::operator==(other)) { |
|
520 // DateFormat::operator== guarantees following cast is safe |
|
521 SimpleDateFormat* that = (SimpleDateFormat*)&other; |
|
522 return (fPattern == that->fPattern && |
|
523 fSymbols != NULL && // Check for pathological object |
|
524 that->fSymbols != NULL && // Check for pathological object |
|
525 *fSymbols == *that->fSymbols && |
|
526 fHaveDefaultCentury == that->fHaveDefaultCentury && |
|
527 fDefaultCenturyStart == that->fDefaultCenturyStart && |
|
528 fCapitalizationContext == that->fCapitalizationContext); |
|
529 } |
|
530 return FALSE; |
|
531 } |
|
532 |
|
533 //---------------------------------------------------------------------- |
|
534 |
|
535 void SimpleDateFormat::construct(EStyle timeStyle, |
|
536 EStyle dateStyle, |
|
537 const Locale& locale, |
|
538 UErrorCode& status) |
|
539 { |
|
540 // called by several constructors to load pattern data from the resources |
|
541 if (U_FAILURE(status)) return; |
|
542 |
|
543 // We will need the calendar to know what type of symbols to load. |
|
544 initializeCalendar(NULL, locale, status); |
|
545 if (U_FAILURE(status)) return; |
|
546 |
|
547 CalendarData calData(locale, fCalendar?fCalendar->getType():NULL, status); |
|
548 UResourceBundle *dateTimePatterns = calData.getByKey(gDateTimePatternsTag, status); |
|
549 UResourceBundle *currentBundle; |
|
550 |
|
551 if (U_FAILURE(status)) return; |
|
552 |
|
553 if (ures_getSize(dateTimePatterns) <= kDateTime) |
|
554 { |
|
555 status = U_INVALID_FORMAT_ERROR; |
|
556 return; |
|
557 } |
|
558 |
|
559 setLocaleIDs(ures_getLocaleByType(dateTimePatterns, ULOC_VALID_LOCALE, &status), |
|
560 ures_getLocaleByType(dateTimePatterns, ULOC_ACTUAL_LOCALE, &status)); |
|
561 |
|
562 // create a symbols object from the locale |
|
563 initializeSymbols(locale,fCalendar, status); |
|
564 if (U_FAILURE(status)) return; |
|
565 /* test for NULL */ |
|
566 if (fSymbols == 0) { |
|
567 status = U_MEMORY_ALLOCATION_ERROR; |
|
568 return; |
|
569 } |
|
570 |
|
571 const UChar *resStr,*ovrStr; |
|
572 int32_t resStrLen,ovrStrLen = 0; |
|
573 fDateOverride.setToBogus(); |
|
574 fTimeOverride.setToBogus(); |
|
575 |
|
576 // if the pattern should include both date and time information, use the date/time |
|
577 // pattern string as a guide to tell use how to glue together the appropriate date |
|
578 // and time pattern strings. The actual gluing-together is handled by a convenience |
|
579 // method on MessageFormat. |
|
580 if ((timeStyle != kNone) && (dateStyle != kNone)) |
|
581 { |
|
582 Formattable timeDateArray[2]; |
|
583 |
|
584 // use Formattable::adoptString() so that we can use fastCopyFrom() |
|
585 // instead of Formattable::setString()'s unaware, safe, deep string clone |
|
586 // see Jitterbug 2296 |
|
587 |
|
588 currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)timeStyle, NULL, &status); |
|
589 if (U_FAILURE(status)) { |
|
590 status = U_INVALID_FORMAT_ERROR; |
|
591 return; |
|
592 } |
|
593 switch (ures_getType(currentBundle)) { |
|
594 case URES_STRING: { |
|
595 resStr = ures_getString(currentBundle, &resStrLen, &status); |
|
596 break; |
|
597 } |
|
598 case URES_ARRAY: { |
|
599 resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); |
|
600 ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); |
|
601 fTimeOverride.setTo(TRUE, ovrStr, ovrStrLen); |
|
602 break; |
|
603 } |
|
604 default: { |
|
605 status = U_INVALID_FORMAT_ERROR; |
|
606 ures_close(currentBundle); |
|
607 return; |
|
608 } |
|
609 } |
|
610 ures_close(currentBundle); |
|
611 |
|
612 UnicodeString *tempus1 = new UnicodeString(TRUE, resStr, resStrLen); |
|
613 // NULL pointer check |
|
614 if (tempus1 == NULL) { |
|
615 status = U_MEMORY_ALLOCATION_ERROR; |
|
616 return; |
|
617 } |
|
618 timeDateArray[0].adoptString(tempus1); |
|
619 |
|
620 currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)dateStyle, NULL, &status); |
|
621 if (U_FAILURE(status)) { |
|
622 status = U_INVALID_FORMAT_ERROR; |
|
623 return; |
|
624 } |
|
625 switch (ures_getType(currentBundle)) { |
|
626 case URES_STRING: { |
|
627 resStr = ures_getString(currentBundle, &resStrLen, &status); |
|
628 break; |
|
629 } |
|
630 case URES_ARRAY: { |
|
631 resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); |
|
632 ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); |
|
633 fDateOverride.setTo(TRUE, ovrStr, ovrStrLen); |
|
634 break; |
|
635 } |
|
636 default: { |
|
637 status = U_INVALID_FORMAT_ERROR; |
|
638 ures_close(currentBundle); |
|
639 return; |
|
640 } |
|
641 } |
|
642 ures_close(currentBundle); |
|
643 |
|
644 UnicodeString *tempus2 = new UnicodeString(TRUE, resStr, resStrLen); |
|
645 // Null pointer check |
|
646 if (tempus2 == NULL) { |
|
647 status = U_MEMORY_ALLOCATION_ERROR; |
|
648 return; |
|
649 } |
|
650 timeDateArray[1].adoptString(tempus2); |
|
651 |
|
652 int32_t glueIndex = kDateTime; |
|
653 int32_t patternsSize = ures_getSize(dateTimePatterns); |
|
654 if (patternsSize >= (kDateTimeOffset + kShort + 1)) { |
|
655 // Get proper date time format |
|
656 glueIndex = (int32_t)(kDateTimeOffset + (dateStyle - kDateOffset)); |
|
657 } |
|
658 |
|
659 resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &status); |
|
660 MessageFormat::format(UnicodeString(TRUE, resStr, resStrLen), timeDateArray, 2, fPattern, status); |
|
661 } |
|
662 // if the pattern includes just time data or just date date, load the appropriate |
|
663 // pattern string from the resources |
|
664 // setTo() - see DateFormatSymbols::assignArray comments |
|
665 else if (timeStyle != kNone) { |
|
666 currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)timeStyle, NULL, &status); |
|
667 if (U_FAILURE(status)) { |
|
668 status = U_INVALID_FORMAT_ERROR; |
|
669 return; |
|
670 } |
|
671 switch (ures_getType(currentBundle)) { |
|
672 case URES_STRING: { |
|
673 resStr = ures_getString(currentBundle, &resStrLen, &status); |
|
674 break; |
|
675 } |
|
676 case URES_ARRAY: { |
|
677 resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); |
|
678 ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); |
|
679 fDateOverride.setTo(TRUE, ovrStr, ovrStrLen); |
|
680 break; |
|
681 } |
|
682 default: { |
|
683 status = U_INVALID_FORMAT_ERROR; |
|
684 ures_close(currentBundle); |
|
685 return; |
|
686 } |
|
687 } |
|
688 fPattern.setTo(TRUE, resStr, resStrLen); |
|
689 ures_close(currentBundle); |
|
690 } |
|
691 else if (dateStyle != kNone) { |
|
692 currentBundle = ures_getByIndex(dateTimePatterns, (int32_t)dateStyle, NULL, &status); |
|
693 if (U_FAILURE(status)) { |
|
694 status = U_INVALID_FORMAT_ERROR; |
|
695 return; |
|
696 } |
|
697 switch (ures_getType(currentBundle)) { |
|
698 case URES_STRING: { |
|
699 resStr = ures_getString(currentBundle, &resStrLen, &status); |
|
700 break; |
|
701 } |
|
702 case URES_ARRAY: { |
|
703 resStr = ures_getStringByIndex(currentBundle, 0, &resStrLen, &status); |
|
704 ovrStr = ures_getStringByIndex(currentBundle, 1, &ovrStrLen, &status); |
|
705 fDateOverride.setTo(TRUE, ovrStr, ovrStrLen); |
|
706 break; |
|
707 } |
|
708 default: { |
|
709 status = U_INVALID_FORMAT_ERROR; |
|
710 ures_close(currentBundle); |
|
711 return; |
|
712 } |
|
713 } |
|
714 fPattern.setTo(TRUE, resStr, resStrLen); |
|
715 ures_close(currentBundle); |
|
716 } |
|
717 |
|
718 // and if it includes _neither_, that's an error |
|
719 else |
|
720 status = U_INVALID_FORMAT_ERROR; |
|
721 |
|
722 // finally, finish initializing by creating a Calendar and a NumberFormat |
|
723 initialize(locale, status); |
|
724 } |
|
725 |
|
726 //---------------------------------------------------------------------- |
|
727 |
|
728 Calendar* |
|
729 SimpleDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status) |
|
730 { |
|
731 if(!U_FAILURE(status)) { |
|
732 fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status); |
|
733 } |
|
734 if (U_SUCCESS(status) && fCalendar == NULL) { |
|
735 status = U_MEMORY_ALLOCATION_ERROR; |
|
736 } |
|
737 return fCalendar; |
|
738 } |
|
739 |
|
740 void |
|
741 SimpleDateFormat::initializeSymbols(const Locale& locale, Calendar* calendar, UErrorCode& status) |
|
742 { |
|
743 if(U_FAILURE(status)) { |
|
744 fSymbols = NULL; |
|
745 } else { |
|
746 // pass in calendar type - use NULL (default) if no calendar set (or err). |
|
747 fSymbols = new DateFormatSymbols(locale, calendar?calendar->getType() :NULL , status); |
|
748 // Null pointer check |
|
749 if (fSymbols == NULL) { |
|
750 status = U_MEMORY_ALLOCATION_ERROR; |
|
751 return; |
|
752 } |
|
753 } |
|
754 } |
|
755 |
|
756 void |
|
757 SimpleDateFormat::initialize(const Locale& locale, |
|
758 UErrorCode& status) |
|
759 { |
|
760 if (U_FAILURE(status)) return; |
|
761 |
|
762 // We don't need to check that the row count is >= 1, since all 2d arrays have at |
|
763 // least one row |
|
764 fNumberFormat = NumberFormat::createInstance(locale, status); |
|
765 if (fNumberFormat != NULL && U_SUCCESS(status)) |
|
766 { |
|
767 // no matter what the locale's default number format looked like, we want |
|
768 // to modify it so that it doesn't use thousands separators, doesn't always |
|
769 // show the decimal point, and recognizes integers only when parsing |
|
770 |
|
771 fNumberFormat->setGroupingUsed(FALSE); |
|
772 DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fNumberFormat); |
|
773 if (decfmt != NULL) { |
|
774 decfmt->setDecimalSeparatorAlwaysShown(FALSE); |
|
775 } |
|
776 fNumberFormat->setParseIntegerOnly(TRUE); |
|
777 fNumberFormat->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00" |
|
778 |
|
779 //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse |
|
780 |
|
781 initNumberFormatters(locale,status); |
|
782 |
|
783 } |
|
784 else if (U_SUCCESS(status)) |
|
785 { |
|
786 status = U_MISSING_RESOURCE_ERROR; |
|
787 } |
|
788 } |
|
789 |
|
790 /* Initialize the fields we use to disambiguate ambiguous years. Separate |
|
791 * so we can call it from readObject(). |
|
792 */ |
|
793 void SimpleDateFormat::initializeDefaultCentury() |
|
794 { |
|
795 if(fCalendar) { |
|
796 fHaveDefaultCentury = fCalendar->haveDefaultCentury(); |
|
797 if(fHaveDefaultCentury) { |
|
798 fDefaultCenturyStart = fCalendar->defaultCenturyStart(); |
|
799 fDefaultCenturyStartYear = fCalendar->defaultCenturyStartYear(); |
|
800 } else { |
|
801 fDefaultCenturyStart = DBL_MIN; |
|
802 fDefaultCenturyStartYear = -1; |
|
803 } |
|
804 } |
|
805 } |
|
806 |
|
807 /* Define one-century window into which to disambiguate dates using |
|
808 * two-digit years. Make public in JDK 1.2. |
|
809 */ |
|
810 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status) |
|
811 { |
|
812 if(U_FAILURE(status)) { |
|
813 return; |
|
814 } |
|
815 if(!fCalendar) { |
|
816 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
817 return; |
|
818 } |
|
819 |
|
820 fCalendar->setTime(startDate, status); |
|
821 if(U_SUCCESS(status)) { |
|
822 fHaveDefaultCentury = TRUE; |
|
823 fDefaultCenturyStart = startDate; |
|
824 fDefaultCenturyStartYear = fCalendar->get(UCAL_YEAR, status); |
|
825 } |
|
826 } |
|
827 |
|
828 //---------------------------------------------------------------------- |
|
829 |
|
830 UnicodeString& |
|
831 SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const |
|
832 { |
|
833 UErrorCode status = U_ZERO_ERROR; |
|
834 FieldPositionOnlyHandler handler(pos); |
|
835 return _format(cal, appendTo, handler, status); |
|
836 } |
|
837 |
|
838 //---------------------------------------------------------------------- |
|
839 |
|
840 UnicodeString& |
|
841 SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, |
|
842 FieldPositionIterator* posIter, UErrorCode& status) const |
|
843 { |
|
844 FieldPositionIteratorHandler handler(posIter, status); |
|
845 return _format(cal, appendTo, handler, status); |
|
846 } |
|
847 |
|
848 //---------------------------------------------------------------------- |
|
849 |
|
850 UnicodeString& |
|
851 SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, |
|
852 FieldPositionHandler& handler, UErrorCode& status) const |
|
853 { |
|
854 if ( U_FAILURE(status) ) { |
|
855 return appendTo; |
|
856 } |
|
857 Calendar* workCal = &cal; |
|
858 Calendar* calClone = NULL; |
|
859 if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) { |
|
860 // Different calendar type |
|
861 // We use the time and time zone from the input calendar, but |
|
862 // do not use the input calendar for field calculation. |
|
863 calClone = fCalendar->clone(); |
|
864 if (calClone != NULL) { |
|
865 UDate t = cal.getTime(status); |
|
866 calClone->setTime(t, status); |
|
867 calClone->setTimeZone(cal.getTimeZone()); |
|
868 workCal = calClone; |
|
869 } else { |
|
870 status = U_MEMORY_ALLOCATION_ERROR; |
|
871 return appendTo; |
|
872 } |
|
873 } |
|
874 |
|
875 UBool inQuote = FALSE; |
|
876 UChar prevCh = 0; |
|
877 int32_t count = 0; |
|
878 int32_t fieldNum = 0; |
|
879 |
|
880 // loop through the pattern string character by character |
|
881 for (int32_t i = 0; i < fPattern.length() && U_SUCCESS(status); ++i) { |
|
882 UChar ch = fPattern[i]; |
|
883 |
|
884 // Use subFormat() to format a repeated pattern character |
|
885 // when a different pattern or non-pattern character is seen |
|
886 if (ch != prevCh && count > 0) { |
|
887 subFormat(appendTo, prevCh, count, fCapitalizationContext, fieldNum++, handler, *workCal, status); |
|
888 count = 0; |
|
889 } |
|
890 if (ch == QUOTE) { |
|
891 // Consecutive single quotes are a single quote literal, |
|
892 // either outside of quotes or between quotes |
|
893 if ((i+1) < fPattern.length() && fPattern[i+1] == QUOTE) { |
|
894 appendTo += (UChar)QUOTE; |
|
895 ++i; |
|
896 } else { |
|
897 inQuote = ! inQuote; |
|
898 } |
|
899 } |
|
900 else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) |
|
901 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { |
|
902 // ch is a date-time pattern character to be interpreted |
|
903 // by subFormat(); count the number of times it is repeated |
|
904 prevCh = ch; |
|
905 ++count; |
|
906 } |
|
907 else { |
|
908 // Append quoted characters and unquoted non-pattern characters |
|
909 appendTo += ch; |
|
910 } |
|
911 } |
|
912 |
|
913 // Format the last item in the pattern, if any |
|
914 if (count > 0) { |
|
915 subFormat(appendTo, prevCh, count, fCapitalizationContext, fieldNum++, handler, *workCal, status); |
|
916 } |
|
917 |
|
918 if (calClone != NULL) { |
|
919 delete calClone; |
|
920 } |
|
921 |
|
922 return appendTo; |
|
923 } |
|
924 |
|
925 //---------------------------------------------------------------------- |
|
926 |
|
927 /* Map calendar field into calendar field level. |
|
928 * the larger the level, the smaller the field unit. |
|
929 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10, |
|
930 * UCAL_MONTH level is 20. |
|
931 * NOTE: if new fields adds in, the table needs to update. |
|
932 */ |
|
933 const int32_t |
|
934 SimpleDateFormat::fgCalendarFieldToLevel[] = |
|
935 { |
|
936 /*GyM*/ 0, 10, 20, |
|
937 /*wW*/ 20, 30, |
|
938 /*dDEF*/ 30, 20, 30, 30, |
|
939 /*ahHm*/ 40, 50, 50, 60, |
|
940 /*sS..*/ 70, 80, |
|
941 /*z?Y*/ 0, 0, 10, |
|
942 /*eug*/ 30, 10, 0, |
|
943 /*A*/ 40 |
|
944 }; |
|
945 |
|
946 |
|
947 /* Map calendar field LETTER into calendar field level. |
|
948 * the larger the level, the smaller the field unit. |
|
949 * NOTE: if new fields adds in, the table needs to update. |
|
950 */ |
|
951 const int32_t |
|
952 SimpleDateFormat::fgPatternCharToLevel[] = { |
|
953 // A B C D E F G H I J K L M N O |
|
954 -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0, |
|
955 // P Q R S T U V W X Y Z |
|
956 -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1, |
|
957 // a b c d e f g h i j k l m n o |
|
958 -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, -1, 60, -1, -1, |
|
959 // p q r s t u v w x y z |
|
960 -1, 20, -1, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1 |
|
961 }; |
|
962 |
|
963 |
|
964 // Map index into pattern character string to Calendar field number. |
|
965 const UCalendarDateFields |
|
966 SimpleDateFormat::fgPatternIndexToCalendarField[] = |
|
967 { |
|
968 /*GyM*/ UCAL_ERA, UCAL_YEAR, UCAL_MONTH, |
|
969 /*dkH*/ UCAL_DATE, UCAL_HOUR_OF_DAY, UCAL_HOUR_OF_DAY, |
|
970 /*msS*/ UCAL_MINUTE, UCAL_SECOND, UCAL_MILLISECOND, |
|
971 /*EDF*/ UCAL_DAY_OF_WEEK, UCAL_DAY_OF_YEAR, UCAL_DAY_OF_WEEK_IN_MONTH, |
|
972 /*wWa*/ UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, UCAL_AM_PM, |
|
973 /*hKz*/ UCAL_HOUR, UCAL_HOUR, UCAL_ZONE_OFFSET, |
|
974 /*Yeu*/ UCAL_YEAR_WOY, UCAL_DOW_LOCAL, UCAL_EXTENDED_YEAR, |
|
975 /*gAZ*/ UCAL_JULIAN_DAY, UCAL_MILLISECONDS_IN_DAY, UCAL_ZONE_OFFSET, |
|
976 /*v*/ UCAL_ZONE_OFFSET, |
|
977 /*c*/ UCAL_DOW_LOCAL, |
|
978 /*L*/ UCAL_MONTH, |
|
979 /*Q*/ UCAL_MONTH, |
|
980 /*q*/ UCAL_MONTH, |
|
981 /*V*/ UCAL_ZONE_OFFSET, |
|
982 /*U*/ UCAL_YEAR, |
|
983 /*O*/ UCAL_ZONE_OFFSET, |
|
984 /*Xx*/ UCAL_ZONE_OFFSET, UCAL_ZONE_OFFSET, |
|
985 }; |
|
986 |
|
987 // Map index into pattern character string to DateFormat field number |
|
988 const UDateFormatField |
|
989 SimpleDateFormat::fgPatternIndexToDateFormatField[] = { |
|
990 /*GyM*/ UDAT_ERA_FIELD, UDAT_YEAR_FIELD, UDAT_MONTH_FIELD, |
|
991 /*dkH*/ UDAT_DATE_FIELD, UDAT_HOUR_OF_DAY1_FIELD, UDAT_HOUR_OF_DAY0_FIELD, |
|
992 /*msS*/ UDAT_MINUTE_FIELD, UDAT_SECOND_FIELD, UDAT_FRACTIONAL_SECOND_FIELD, |
|
993 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD, UDAT_DAY_OF_YEAR_FIELD, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD, |
|
994 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD, UDAT_WEEK_OF_MONTH_FIELD, UDAT_AM_PM_FIELD, |
|
995 /*hKz*/ UDAT_HOUR1_FIELD, UDAT_HOUR0_FIELD, UDAT_TIMEZONE_FIELD, |
|
996 /*Yeu*/ UDAT_YEAR_WOY_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_EXTENDED_YEAR_FIELD, |
|
997 /*gAZ*/ UDAT_JULIAN_DAY_FIELD, UDAT_MILLISECONDS_IN_DAY_FIELD, UDAT_TIMEZONE_RFC_FIELD, |
|
998 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD, |
|
999 /*c*/ UDAT_STANDALONE_DAY_FIELD, |
|
1000 /*L*/ UDAT_STANDALONE_MONTH_FIELD, |
|
1001 /*Q*/ UDAT_QUARTER_FIELD, |
|
1002 /*q*/ UDAT_STANDALONE_QUARTER_FIELD, |
|
1003 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD, |
|
1004 /*U*/ UDAT_YEAR_NAME_FIELD, |
|
1005 /*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD, |
|
1006 /*Xx*/ UDAT_TIMEZONE_ISO_FIELD, UDAT_TIMEZONE_ISO_LOCAL_FIELD, |
|
1007 }; |
|
1008 |
|
1009 //---------------------------------------------------------------------- |
|
1010 |
|
1011 /** |
|
1012 * Append symbols[value] to dst. Make sure the array index is not out |
|
1013 * of bounds. |
|
1014 */ |
|
1015 static inline void |
|
1016 _appendSymbol(UnicodeString& dst, |
|
1017 int32_t value, |
|
1018 const UnicodeString* symbols, |
|
1019 int32_t symbolsCount) { |
|
1020 U_ASSERT(0 <= value && value < symbolsCount); |
|
1021 if (0 <= value && value < symbolsCount) { |
|
1022 dst += symbols[value]; |
|
1023 } |
|
1024 } |
|
1025 |
|
1026 static inline void |
|
1027 _appendSymbolWithMonthPattern(UnicodeString& dst, int32_t value, const UnicodeString* symbols, int32_t symbolsCount, |
|
1028 const UnicodeString* monthPattern, UErrorCode& status) { |
|
1029 U_ASSERT(0 <= value && value < symbolsCount); |
|
1030 if (0 <= value && value < symbolsCount) { |
|
1031 if (monthPattern == NULL) { |
|
1032 dst += symbols[value]; |
|
1033 } else { |
|
1034 Formattable monthName((const UnicodeString&)(symbols[value])); |
|
1035 MessageFormat::format(*monthPattern, &monthName, 1, dst, status); |
|
1036 } |
|
1037 } |
|
1038 } |
|
1039 |
|
1040 //---------------------------------------------------------------------- |
|
1041 void |
|
1042 SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) { |
|
1043 if (U_FAILURE(status)) { |
|
1044 return; |
|
1045 } |
|
1046 if ( fDateOverride.isBogus() && fTimeOverride.isBogus() ) { |
|
1047 return; |
|
1048 } |
|
1049 umtx_lock(&LOCK); |
|
1050 if (fNumberFormatters == NULL) { |
|
1051 fNumberFormatters = (NumberFormat**)uprv_malloc(UDAT_FIELD_COUNT * sizeof(NumberFormat*)); |
|
1052 if (fNumberFormatters) { |
|
1053 for (int32_t i = 0; i < UDAT_FIELD_COUNT; i++) { |
|
1054 fNumberFormatters[i] = fNumberFormat; |
|
1055 } |
|
1056 } else { |
|
1057 status = U_MEMORY_ALLOCATION_ERROR; |
|
1058 } |
|
1059 } |
|
1060 umtx_unlock(&LOCK); |
|
1061 |
|
1062 processOverrideString(locale,fDateOverride,kOvrStrDate,status); |
|
1063 processOverrideString(locale,fTimeOverride,kOvrStrTime,status); |
|
1064 |
|
1065 } |
|
1066 |
|
1067 void |
|
1068 SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeString &str, int8_t type, UErrorCode &status) { |
|
1069 if (str.isBogus()) { |
|
1070 return; |
|
1071 } |
|
1072 int32_t start = 0; |
|
1073 int32_t len; |
|
1074 UnicodeString nsName; |
|
1075 UnicodeString ovrField; |
|
1076 UBool moreToProcess = TRUE; |
|
1077 |
|
1078 while (moreToProcess) { |
|
1079 int32_t delimiterPosition = str.indexOf((UChar)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE,start); |
|
1080 if (delimiterPosition == -1) { |
|
1081 moreToProcess = FALSE; |
|
1082 len = str.length() - start; |
|
1083 } else { |
|
1084 len = delimiterPosition - start; |
|
1085 } |
|
1086 UnicodeString currentString(str,start,len); |
|
1087 int32_t equalSignPosition = currentString.indexOf((UChar)ULOC_KEYWORD_ASSIGN_UNICODE,0); |
|
1088 if (equalSignPosition == -1) { // Simple override string such as "hebrew" |
|
1089 nsName.setTo(currentString); |
|
1090 ovrField.setToBogus(); |
|
1091 } else { // Field specific override string such as "y=hebrew" |
|
1092 nsName.setTo(currentString,equalSignPosition+1); |
|
1093 ovrField.setTo(currentString,0,1); // We just need the first character. |
|
1094 } |
|
1095 |
|
1096 int32_t nsNameHash = nsName.hashCode(); |
|
1097 // See if the numbering system is in the override list, if not, then add it. |
|
1098 NSOverride *cur = fOverrideList; |
|
1099 NumberFormat *nf = NULL; |
|
1100 UBool found = FALSE; |
|
1101 while ( cur && !found ) { |
|
1102 if ( cur->hash == nsNameHash ) { |
|
1103 nf = cur->nf; |
|
1104 found = TRUE; |
|
1105 } |
|
1106 cur = cur->next; |
|
1107 } |
|
1108 |
|
1109 if (!found) { |
|
1110 cur = (NSOverride *)uprv_malloc(sizeof(NSOverride)); |
|
1111 if (cur) { |
|
1112 char kw[ULOC_KEYWORD_AND_VALUES_CAPACITY]; |
|
1113 uprv_strcpy(kw,"numbers="); |
|
1114 nsName.extract(0,len,kw+8,ULOC_KEYWORD_AND_VALUES_CAPACITY-8,US_INV); |
|
1115 |
|
1116 Locale ovrLoc(locale.getLanguage(),locale.getCountry(),locale.getVariant(),kw); |
|
1117 nf = NumberFormat::createInstance(ovrLoc,status); |
|
1118 |
|
1119 // no matter what the locale's default number format looked like, we want |
|
1120 // to modify it so that it doesn't use thousands separators, doesn't always |
|
1121 // show the decimal point, and recognizes integers only when parsing |
|
1122 |
|
1123 if (U_SUCCESS(status)) { |
|
1124 nf->setGroupingUsed(FALSE); |
|
1125 DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(nf); |
|
1126 if (decfmt != NULL) { |
|
1127 decfmt->setDecimalSeparatorAlwaysShown(FALSE); |
|
1128 } |
|
1129 nf->setParseIntegerOnly(TRUE); |
|
1130 nf->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00" |
|
1131 |
|
1132 cur->nf = nf; |
|
1133 cur->hash = nsNameHash; |
|
1134 cur->next = fOverrideList; |
|
1135 fOverrideList = cur; |
|
1136 } |
|
1137 else { |
|
1138 // clean up before returning |
|
1139 if (cur != NULL) { |
|
1140 uprv_free(cur); |
|
1141 } |
|
1142 return; |
|
1143 } |
|
1144 |
|
1145 } else { |
|
1146 status = U_MEMORY_ALLOCATION_ERROR; |
|
1147 return; |
|
1148 } |
|
1149 } |
|
1150 |
|
1151 // Now that we have an appropriate number formatter, fill in the appropriate spaces in the |
|
1152 // number formatters table. |
|
1153 |
|
1154 if (ovrField.isBogus()) { |
|
1155 switch (type) { |
|
1156 case kOvrStrDate: |
|
1157 case kOvrStrBoth: { |
|
1158 for ( int8_t i=0 ; i<kDateFieldsCount; i++ ) { |
|
1159 fNumberFormatters[kDateFields[i]] = nf; |
|
1160 } |
|
1161 if (type==kOvrStrDate) { |
|
1162 break; |
|
1163 } |
|
1164 } |
|
1165 case kOvrStrTime : { |
|
1166 for ( int8_t i=0 ; i<kTimeFieldsCount; i++ ) { |
|
1167 fNumberFormatters[kTimeFields[i]] = nf; |
|
1168 } |
|
1169 break; |
|
1170 } |
|
1171 } |
|
1172 } else { |
|
1173 // if the pattern character is unrecognized, signal an error and bail out |
|
1174 UDateFormatField patternCharIndex = |
|
1175 DateFormatSymbols::getPatternCharIndex(ovrField.charAt(0)); |
|
1176 if (patternCharIndex == UDAT_FIELD_COUNT) { |
|
1177 status = U_INVALID_FORMAT_ERROR; |
|
1178 return; |
|
1179 } |
|
1180 |
|
1181 // Set the number formatter in the table |
|
1182 fNumberFormatters[patternCharIndex] = nf; |
|
1183 } |
|
1184 |
|
1185 start = delimiterPosition + 1; |
|
1186 } |
|
1187 } |
|
1188 |
|
1189 //--------------------------------------------------------------------- |
|
1190 void |
|
1191 SimpleDateFormat::subFormat(UnicodeString &appendTo, |
|
1192 UChar ch, |
|
1193 int32_t count, |
|
1194 UDisplayContext capitalizationContext, |
|
1195 int32_t fieldNum, |
|
1196 FieldPositionHandler& handler, |
|
1197 Calendar& cal, |
|
1198 UErrorCode& status) const |
|
1199 { |
|
1200 if (U_FAILURE(status)) { |
|
1201 return; |
|
1202 } |
|
1203 |
|
1204 // this function gets called by format() to produce the appropriate substitution |
|
1205 // text for an individual pattern symbol (e.g., "HH" or "yyyy") |
|
1206 |
|
1207 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch); |
|
1208 const int32_t maxIntCount = 10; |
|
1209 int32_t beginOffset = appendTo.length(); |
|
1210 NumberFormat *currentNumberFormat; |
|
1211 DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType = DateFormatSymbols::kCapContextUsageOther; |
|
1212 |
|
1213 UBool isHebrewCalendar = (uprv_strcmp(cal.getType(),"hebrew") == 0); |
|
1214 UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0 || uprv_strcmp(cal.getType(),"dangi") == 0); |
|
1215 |
|
1216 // if the pattern character is unrecognized, signal an error and dump out |
|
1217 if (patternCharIndex == UDAT_FIELD_COUNT) |
|
1218 { |
|
1219 if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored |
|
1220 status = U_INVALID_FORMAT_ERROR; |
|
1221 } |
|
1222 return; |
|
1223 } |
|
1224 |
|
1225 UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; |
|
1226 int32_t value = cal.get(field, status); |
|
1227 if (U_FAILURE(status)) { |
|
1228 return; |
|
1229 } |
|
1230 |
|
1231 currentNumberFormat = getNumberFormatByIndex(patternCharIndex); |
|
1232 UnicodeString hebr("hebr", 4, US_INV); |
|
1233 |
|
1234 switch (patternCharIndex) { |
|
1235 |
|
1236 // for any "G" symbol, write out the appropriate era string |
|
1237 // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name |
|
1238 case UDAT_ERA_FIELD: |
|
1239 if (isChineseCalendar) { |
|
1240 zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, 9); // as in ICU4J |
|
1241 } else { |
|
1242 if (count == 5) { |
|
1243 _appendSymbol(appendTo, value, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount); |
|
1244 capContextUsageType = DateFormatSymbols::kCapContextUsageEraNarrow; |
|
1245 } else if (count == 4) { |
|
1246 _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount); |
|
1247 capContextUsageType = DateFormatSymbols::kCapContextUsageEraWide; |
|
1248 } else { |
|
1249 _appendSymbol(appendTo, value, fSymbols->fEras, fSymbols->fErasCount); |
|
1250 capContextUsageType = DateFormatSymbols::kCapContextUsageEraAbbrev; |
|
1251 } |
|
1252 } |
|
1253 break; |
|
1254 |
|
1255 case UDAT_YEAR_NAME_FIELD: |
|
1256 if (fSymbols->fShortYearNames != NULL && value <= fSymbols->fShortYearNamesCount) { |
|
1257 // the Calendar YEAR field runs 1 through 60 for cyclic years |
|
1258 _appendSymbol(appendTo, value - 1, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount); |
|
1259 break; |
|
1260 } |
|
1261 // else fall through to numeric year handling, do not break here |
|
1262 |
|
1263 // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits |
|
1264 // NEW: UTS#35: |
|
1265 //Year y yy yyy yyyy yyyyy |
|
1266 //AD 1 1 01 001 0001 00001 |
|
1267 //AD 12 12 12 012 0012 00012 |
|
1268 //AD 123 123 23 123 0123 00123 |
|
1269 //AD 1234 1234 34 1234 1234 01234 |
|
1270 //AD 12345 12345 45 12345 12345 12345 |
|
1271 case UDAT_YEAR_FIELD: |
|
1272 case UDAT_YEAR_WOY_FIELD: |
|
1273 if (fDateOverride.compare(hebr)==0 && value>HEBREW_CAL_CUR_MILLENIUM_START_YEAR && value<HEBREW_CAL_CUR_MILLENIUM_END_YEAR) { |
|
1274 value-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR; |
|
1275 } |
|
1276 if(count == 2) |
|
1277 zeroPaddingNumber(currentNumberFormat, appendTo, value, 2, 2); |
|
1278 else |
|
1279 zeroPaddingNumber(currentNumberFormat, appendTo, value, count, maxIntCount); |
|
1280 break; |
|
1281 |
|
1282 // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month |
|
1283 // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the |
|
1284 // appropriate number of digits |
|
1285 // for "MMMMM"/"LLLLL", use the narrow form |
|
1286 case UDAT_MONTH_FIELD: |
|
1287 case UDAT_STANDALONE_MONTH_FIELD: |
|
1288 if ( isHebrewCalendar ) { |
|
1289 HebrewCalendar *hc = (HebrewCalendar*)&cal; |
|
1290 if (hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value == 6 && count >= 3 ) |
|
1291 value = 13; // Show alternate form for Adar II in leap years in Hebrew calendar. |
|
1292 if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6 && count < 3 ) |
|
1293 value--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7. |
|
1294 } |
|
1295 { |
|
1296 int32_t isLeapMonth = (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount)? |
|
1297 cal.get(UCAL_IS_LEAP_MONTH, status): 0; |
|
1298 // should consolidate the next section by using arrays of pointers & counts for the right symbols... |
|
1299 if (count == 5) { |
|
1300 if (patternCharIndex == UDAT_MONTH_FIELD) { |
|
1301 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fNarrowMonths, fSymbols->fNarrowMonthsCount, |
|
1302 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatNarrow]): NULL, status); |
|
1303 } else { |
|
1304 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneNarrowMonths, fSymbols->fStandaloneNarrowMonthsCount, |
|
1305 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow]): NULL, status); |
|
1306 } |
|
1307 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthNarrow; |
|
1308 } else if (count == 4) { |
|
1309 if (patternCharIndex == UDAT_MONTH_FIELD) { |
|
1310 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fMonths, fSymbols->fMonthsCount, |
|
1311 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide]): NULL, status); |
|
1312 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat; |
|
1313 } else { |
|
1314 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, |
|
1315 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide]): NULL, status); |
|
1316 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone; |
|
1317 } |
|
1318 } else if (count == 3) { |
|
1319 if (patternCharIndex == UDAT_MONTH_FIELD) { |
|
1320 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fShortMonths, fSymbols->fShortMonthsCount, |
|
1321 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev]): NULL, status); |
|
1322 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat; |
|
1323 } else { |
|
1324 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, |
|
1325 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev]): NULL, status); |
|
1326 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone; |
|
1327 } |
|
1328 } else { |
|
1329 UnicodeString monthNumber; |
|
1330 zeroPaddingNumber(currentNumberFormat,monthNumber, value + 1, count, maxIntCount); |
|
1331 _appendSymbolWithMonthPattern(appendTo, 0, &monthNumber, 1, |
|
1332 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric]): NULL, status); |
|
1333 } |
|
1334 } |
|
1335 break; |
|
1336 |
|
1337 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24" |
|
1338 case UDAT_HOUR_OF_DAY1_FIELD: |
|
1339 if (value == 0) |
|
1340 zeroPaddingNumber(currentNumberFormat,appendTo, cal.getMaximum(UCAL_HOUR_OF_DAY) + 1, count, maxIntCount); |
|
1341 else |
|
1342 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); |
|
1343 break; |
|
1344 |
|
1345 case UDAT_FRACTIONAL_SECOND_FIELD: |
|
1346 // Fractional seconds left-justify |
|
1347 { |
|
1348 currentNumberFormat->setMinimumIntegerDigits((count > 3) ? 3 : count); |
|
1349 currentNumberFormat->setMaximumIntegerDigits(maxIntCount); |
|
1350 if (count == 1) { |
|
1351 value /= 100; |
|
1352 } else if (count == 2) { |
|
1353 value /= 10; |
|
1354 } |
|
1355 FieldPosition p(0); |
|
1356 currentNumberFormat->format(value, appendTo, p); |
|
1357 if (count > 3) { |
|
1358 currentNumberFormat->setMinimumIntegerDigits(count - 3); |
|
1359 currentNumberFormat->format((int32_t)0, appendTo, p); |
|
1360 } |
|
1361 } |
|
1362 break; |
|
1363 |
|
1364 // for "ee" or "e", use local numeric day-of-the-week |
|
1365 // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name |
|
1366 // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name |
|
1367 // for "EEEE" or "eeee", write out the wide day-of-the-week name |
|
1368 // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name |
|
1369 case UDAT_DOW_LOCAL_FIELD: |
|
1370 if ( count < 3 ) { |
|
1371 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); |
|
1372 break; |
|
1373 } |
|
1374 // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week, |
|
1375 // we want standard day-of-week, so first fix value to work for EEEEE-EEE. |
|
1376 value = cal.get(UCAL_DAY_OF_WEEK, status); |
|
1377 if (U_FAILURE(status)) { |
|
1378 return; |
|
1379 } |
|
1380 // fall through, do not break here |
|
1381 case UDAT_DAY_OF_WEEK_FIELD: |
|
1382 if (count == 5) { |
|
1383 _appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays, |
|
1384 fSymbols->fNarrowWeekdaysCount); |
|
1385 capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow; |
|
1386 } else if (count == 4) { |
|
1387 _appendSymbol(appendTo, value, fSymbols->fWeekdays, |
|
1388 fSymbols->fWeekdaysCount); |
|
1389 capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat; |
|
1390 } else if (count == 6) { |
|
1391 _appendSymbol(appendTo, value, fSymbols->fShorterWeekdays, |
|
1392 fSymbols->fShorterWeekdaysCount); |
|
1393 capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat; |
|
1394 } else { |
|
1395 _appendSymbol(appendTo, value, fSymbols->fShortWeekdays, |
|
1396 fSymbols->fShortWeekdaysCount); |
|
1397 capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat; |
|
1398 } |
|
1399 break; |
|
1400 |
|
1401 // for "ccc", write out the abbreviated day-of-the-week name |
|
1402 // for "cccc", write out the wide day-of-the-week name |
|
1403 // for "ccccc", use the narrow day-of-the-week name |
|
1404 // for "ccccc", use the short day-of-the-week name |
|
1405 case UDAT_STANDALONE_DAY_FIELD: |
|
1406 if ( count < 3 ) { |
|
1407 zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, maxIntCount); |
|
1408 break; |
|
1409 } |
|
1410 // fall through to alpha DOW handling, but for that we don't want local day-of-week, |
|
1411 // we want standard day-of-week, so first fix value. |
|
1412 value = cal.get(UCAL_DAY_OF_WEEK, status); |
|
1413 if (U_FAILURE(status)) { |
|
1414 return; |
|
1415 } |
|
1416 if (count == 5) { |
|
1417 _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowWeekdays, |
|
1418 fSymbols->fStandaloneNarrowWeekdaysCount); |
|
1419 capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow; |
|
1420 } else if (count == 4) { |
|
1421 _appendSymbol(appendTo, value, fSymbols->fStandaloneWeekdays, |
|
1422 fSymbols->fStandaloneWeekdaysCount); |
|
1423 capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone; |
|
1424 } else if (count == 6) { |
|
1425 _appendSymbol(appendTo, value, fSymbols->fStandaloneShorterWeekdays, |
|
1426 fSymbols->fStandaloneShorterWeekdaysCount); |
|
1427 capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone; |
|
1428 } else { // count == 3 |
|
1429 _appendSymbol(appendTo, value, fSymbols->fStandaloneShortWeekdays, |
|
1430 fSymbols->fStandaloneShortWeekdaysCount); |
|
1431 capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone; |
|
1432 } |
|
1433 break; |
|
1434 |
|
1435 // for and "a" symbol, write out the whole AM/PM string |
|
1436 case UDAT_AM_PM_FIELD: |
|
1437 _appendSymbol(appendTo, value, fSymbols->fAmPms, |
|
1438 fSymbols->fAmPmsCount); |
|
1439 break; |
|
1440 |
|
1441 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up |
|
1442 // as "12" |
|
1443 case UDAT_HOUR1_FIELD: |
|
1444 if (value == 0) |
|
1445 zeroPaddingNumber(currentNumberFormat,appendTo, cal.getLeastMaximum(UCAL_HOUR) + 1, count, maxIntCount); |
|
1446 else |
|
1447 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); |
|
1448 break; |
|
1449 |
|
1450 case UDAT_TIMEZONE_FIELD: // 'z' |
|
1451 case UDAT_TIMEZONE_RFC_FIELD: // 'Z' |
|
1452 case UDAT_TIMEZONE_GENERIC_FIELD: // 'v' |
|
1453 case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V' |
|
1454 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O' |
|
1455 case UDAT_TIMEZONE_ISO_FIELD: // 'X' |
|
1456 case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x' |
|
1457 { |
|
1458 UnicodeString zoneString; |
|
1459 const TimeZone& tz = cal.getTimeZone(); |
|
1460 UDate date = cal.getTime(status); |
|
1461 if (U_SUCCESS(status)) { |
|
1462 if (patternCharIndex == UDAT_TIMEZONE_FIELD) { |
|
1463 if (count < 4) { |
|
1464 // "z", "zz", "zzz" |
|
1465 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT, tz, date, zoneString); |
|
1466 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort; |
|
1467 } else { |
|
1468 // "zzzz" or longer |
|
1469 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, date, zoneString); |
|
1470 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong; |
|
1471 } |
|
1472 } |
|
1473 else if (patternCharIndex == UDAT_TIMEZONE_RFC_FIELD) { |
|
1474 if (count < 4) { |
|
1475 // "Z" |
|
1476 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString); |
|
1477 } else if (count == 5) { |
|
1478 // "ZZZZZ" |
|
1479 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString); |
|
1480 } else { |
|
1481 // "ZZ", "ZZZ", "ZZZZ" |
|
1482 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString); |
|
1483 } |
|
1484 } |
|
1485 else if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) { |
|
1486 if (count == 1) { |
|
1487 // "v" |
|
1488 tzFormat()->format(UTZFMT_STYLE_GENERIC_SHORT, tz, date, zoneString); |
|
1489 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort; |
|
1490 } else if (count == 4) { |
|
1491 // "vvvv" |
|
1492 tzFormat()->format(UTZFMT_STYLE_GENERIC_LONG, tz, date, zoneString); |
|
1493 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong; |
|
1494 } |
|
1495 } |
|
1496 else if (patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD) { |
|
1497 if (count == 1) { |
|
1498 // "V" |
|
1499 tzFormat()->format(UTZFMT_STYLE_ZONE_ID_SHORT, tz, date, zoneString); |
|
1500 } else if (count == 2) { |
|
1501 // "VV" |
|
1502 tzFormat()->format(UTZFMT_STYLE_ZONE_ID, tz, date, zoneString); |
|
1503 } else if (count == 3) { |
|
1504 // "VVV" |
|
1505 tzFormat()->format(UTZFMT_STYLE_EXEMPLAR_LOCATION, tz, date, zoneString); |
|
1506 } else if (count == 4) { |
|
1507 // "VVVV" |
|
1508 tzFormat()->format(UTZFMT_STYLE_GENERIC_LOCATION, tz, date, zoneString); |
|
1509 capContextUsageType = DateFormatSymbols::kCapContextUsageZoneLong; |
|
1510 } |
|
1511 } |
|
1512 else if (patternCharIndex == UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD) { |
|
1513 if (count == 1) { |
|
1514 // "O" |
|
1515 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT, tz, date, zoneString); |
|
1516 } else if (count == 4) { |
|
1517 // "OOOO" |
|
1518 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString); |
|
1519 } |
|
1520 } |
|
1521 else if (patternCharIndex == UDAT_TIMEZONE_ISO_FIELD) { |
|
1522 if (count == 1) { |
|
1523 // "X" |
|
1524 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_SHORT, tz, date, zoneString); |
|
1525 } else if (count == 2) { |
|
1526 // "XX" |
|
1527 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FIXED, tz, date, zoneString); |
|
1528 } else if (count == 3) { |
|
1529 // "XXX" |
|
1530 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED, tz, date, zoneString); |
|
1531 } else if (count == 4) { |
|
1532 // "XXXX" |
|
1533 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FULL, tz, date, zoneString); |
|
1534 } else if (count == 5) { |
|
1535 // "XXXXX" |
|
1536 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString); |
|
1537 } |
|
1538 } |
|
1539 else if (patternCharIndex == UDAT_TIMEZONE_ISO_LOCAL_FIELD) { |
|
1540 if (count == 1) { |
|
1541 // "x" |
|
1542 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT, tz, date, zoneString); |
|
1543 } else if (count == 2) { |
|
1544 // "xx" |
|
1545 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED, tz, date, zoneString); |
|
1546 } else if (count == 3) { |
|
1547 // "xxx" |
|
1548 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED, tz, date, zoneString); |
|
1549 } else if (count == 4) { |
|
1550 // "xxxx" |
|
1551 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString); |
|
1552 } else if (count == 5) { |
|
1553 // "xxxxx" |
|
1554 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL, tz, date, zoneString); |
|
1555 } |
|
1556 } |
|
1557 else { |
|
1558 U_ASSERT(FALSE); |
|
1559 } |
|
1560 } |
|
1561 appendTo += zoneString; |
|
1562 } |
|
1563 break; |
|
1564 |
|
1565 case UDAT_QUARTER_FIELD: |
|
1566 if (count >= 4) |
|
1567 _appendSymbol(appendTo, value/3, fSymbols->fQuarters, |
|
1568 fSymbols->fQuartersCount); |
|
1569 else if (count == 3) |
|
1570 _appendSymbol(appendTo, value/3, fSymbols->fShortQuarters, |
|
1571 fSymbols->fShortQuartersCount); |
|
1572 else |
|
1573 zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount); |
|
1574 break; |
|
1575 |
|
1576 case UDAT_STANDALONE_QUARTER_FIELD: |
|
1577 if (count >= 4) |
|
1578 _appendSymbol(appendTo, value/3, fSymbols->fStandaloneQuarters, |
|
1579 fSymbols->fStandaloneQuartersCount); |
|
1580 else if (count == 3) |
|
1581 _appendSymbol(appendTo, value/3, fSymbols->fStandaloneShortQuarters, |
|
1582 fSymbols->fStandaloneShortQuartersCount); |
|
1583 else |
|
1584 zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount); |
|
1585 break; |
|
1586 |
|
1587 |
|
1588 // all of the other pattern symbols can be formatted as simple numbers with |
|
1589 // appropriate zero padding |
|
1590 default: |
|
1591 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); |
|
1592 break; |
|
1593 } |
|
1594 #if !UCONFIG_NO_BREAK_ITERATION |
|
1595 if (fieldNum == 0) { |
|
1596 // first field, check to see whether we need to titlecase it |
|
1597 UBool titlecase = FALSE; |
|
1598 switch (capitalizationContext) { |
|
1599 case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE: |
|
1600 titlecase = TRUE; |
|
1601 break; |
|
1602 case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU: |
|
1603 titlecase = fSymbols->fCapitalization[capContextUsageType][0]; |
|
1604 break; |
|
1605 case UDISPCTX_CAPITALIZATION_FOR_STANDALONE: |
|
1606 titlecase = fSymbols->fCapitalization[capContextUsageType][1]; |
|
1607 break; |
|
1608 default: |
|
1609 // titlecase = FALSE; |
|
1610 break; |
|
1611 } |
|
1612 if (titlecase) { |
|
1613 UnicodeString firstField(appendTo, beginOffset); |
|
1614 firstField.toTitle(NULL, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); |
|
1615 appendTo.replaceBetween(beginOffset, appendTo.length(), firstField); |
|
1616 } |
|
1617 } |
|
1618 #endif |
|
1619 |
|
1620 handler.addAttribute(fgPatternIndexToDateFormatField[patternCharIndex], beginOffset, appendTo.length()); |
|
1621 } |
|
1622 |
|
1623 //---------------------------------------------------------------------- |
|
1624 |
|
1625 NumberFormat * |
|
1626 SimpleDateFormat::getNumberFormatByIndex(UDateFormatField index) const { |
|
1627 if (fNumberFormatters != NULL) { |
|
1628 return fNumberFormatters[index]; |
|
1629 } else { |
|
1630 return fNumberFormat; |
|
1631 } |
|
1632 } |
|
1633 |
|
1634 //---------------------------------------------------------------------- |
|
1635 void |
|
1636 SimpleDateFormat::zeroPaddingNumber(NumberFormat *currentNumberFormat,UnicodeString &appendTo, |
|
1637 int32_t value, int32_t minDigits, int32_t maxDigits) const |
|
1638 { |
|
1639 if (currentNumberFormat!=NULL) { |
|
1640 FieldPosition pos(0); |
|
1641 |
|
1642 currentNumberFormat->setMinimumIntegerDigits(minDigits); |
|
1643 currentNumberFormat->setMaximumIntegerDigits(maxDigits); |
|
1644 currentNumberFormat->format(value, appendTo, pos); // 3rd arg is there to speed up processing |
|
1645 } |
|
1646 } |
|
1647 |
|
1648 //---------------------------------------------------------------------- |
|
1649 |
|
1650 /** |
|
1651 * Return true if the given format character, occuring count |
|
1652 * times, represents a numeric field. |
|
1653 */ |
|
1654 UBool SimpleDateFormat::isNumeric(UChar formatChar, int32_t count) { |
|
1655 return DateFormatSymbols::isNumericPatternChar(formatChar, count); |
|
1656 } |
|
1657 |
|
1658 UBool |
|
1659 SimpleDateFormat::isAtNumericField(const UnicodeString &pattern, int32_t patternOffset) { |
|
1660 if (patternOffset >= pattern.length()) { |
|
1661 // not at any field |
|
1662 return FALSE; |
|
1663 } |
|
1664 UChar ch = pattern.charAt(patternOffset); |
|
1665 UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch); |
|
1666 if (f == UDAT_FIELD_COUNT) { |
|
1667 // not at any field |
|
1668 return FALSE; |
|
1669 } |
|
1670 int32_t i = patternOffset; |
|
1671 while (pattern.charAt(++i) == ch) {} |
|
1672 return DateFormatSymbols::isNumericField(f, i - patternOffset); |
|
1673 } |
|
1674 |
|
1675 UBool |
|
1676 SimpleDateFormat::isAfterNonNumericField(const UnicodeString &pattern, int32_t patternOffset) { |
|
1677 if (patternOffset <= 0) { |
|
1678 // not after any field |
|
1679 return FALSE; |
|
1680 } |
|
1681 UChar ch = pattern.charAt(--patternOffset); |
|
1682 UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch); |
|
1683 if (f == UDAT_FIELD_COUNT) { |
|
1684 // not after any field |
|
1685 return FALSE; |
|
1686 } |
|
1687 int32_t i = patternOffset; |
|
1688 while (pattern.charAt(--i) == ch) {} |
|
1689 return !DateFormatSymbols::isNumericField(f, patternOffset - i); |
|
1690 } |
|
1691 |
|
1692 void |
|
1693 SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& parsePos) const |
|
1694 { |
|
1695 UErrorCode status = U_ZERO_ERROR; |
|
1696 int32_t pos = parsePos.getIndex(); |
|
1697 int32_t start = pos; |
|
1698 |
|
1699 UBool ambiguousYear[] = { FALSE }; |
|
1700 int32_t saveHebrewMonth = -1; |
|
1701 int32_t count = 0; |
|
1702 |
|
1703 // hack, reset tztype, cast away const |
|
1704 ((SimpleDateFormat*)this)->tztype = UTZFMT_TIME_TYPE_UNKNOWN; |
|
1705 |
|
1706 // For parsing abutting numeric fields. 'abutPat' is the |
|
1707 // offset into 'pattern' of the first of 2 or more abutting |
|
1708 // numeric fields. 'abutStart' is the offset into 'text' |
|
1709 // where parsing the fields begins. 'abutPass' starts off as 0 |
|
1710 // and increments each time we try to parse the fields. |
|
1711 int32_t abutPat = -1; // If >=0, we are in a run of abutting numeric fields |
|
1712 int32_t abutStart = 0; |
|
1713 int32_t abutPass = 0; |
|
1714 UBool inQuote = FALSE; |
|
1715 |
|
1716 MessageFormat * numericLeapMonthFormatter = NULL; |
|
1717 |
|
1718 Calendar* calClone = NULL; |
|
1719 Calendar *workCal = &cal; |
|
1720 if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) { |
|
1721 // Different calendar type |
|
1722 // We use the time/zone from the input calendar, but |
|
1723 // do not use the input calendar for field calculation. |
|
1724 calClone = fCalendar->clone(); |
|
1725 if (calClone != NULL) { |
|
1726 calClone->setTime(cal.getTime(status),status); |
|
1727 if (U_FAILURE(status)) { |
|
1728 goto ExitParse; |
|
1729 } |
|
1730 calClone->setTimeZone(cal.getTimeZone()); |
|
1731 workCal = calClone; |
|
1732 } else { |
|
1733 status = U_MEMORY_ALLOCATION_ERROR; |
|
1734 goto ExitParse; |
|
1735 } |
|
1736 } |
|
1737 |
|
1738 if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) { |
|
1739 numericLeapMonthFormatter = new MessageFormat(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric], fLocale, status); |
|
1740 if (numericLeapMonthFormatter == NULL) { |
|
1741 status = U_MEMORY_ALLOCATION_ERROR; |
|
1742 goto ExitParse; |
|
1743 } else if (U_FAILURE(status)) { |
|
1744 goto ExitParse; // this will delete numericLeapMonthFormatter |
|
1745 } |
|
1746 } |
|
1747 |
|
1748 for (int32_t i=0; i<fPattern.length(); ++i) { |
|
1749 UChar ch = fPattern.charAt(i); |
|
1750 |
|
1751 // Handle alphabetic field characters. |
|
1752 if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { // [A-Za-z] |
|
1753 int32_t fieldPat = i; |
|
1754 |
|
1755 // Count the length of this field specifier |
|
1756 count = 1; |
|
1757 while ((i+1)<fPattern.length() && |
|
1758 fPattern.charAt(i+1) == ch) { |
|
1759 ++count; |
|
1760 ++i; |
|
1761 } |
|
1762 |
|
1763 if (isNumeric(ch, count)) { |
|
1764 if (abutPat < 0) { |
|
1765 // Determine if there is an abutting numeric field. |
|
1766 // Record the start of a set of abutting numeric fields. |
|
1767 if (isAtNumericField(fPattern, i + 1)) { |
|
1768 abutPat = fieldPat; |
|
1769 abutStart = pos; |
|
1770 abutPass = 0; |
|
1771 } |
|
1772 } |
|
1773 } else { |
|
1774 abutPat = -1; // End of any abutting fields |
|
1775 } |
|
1776 |
|
1777 // Handle fields within a run of abutting numeric fields. Take |
|
1778 // the pattern "HHmmss" as an example. We will try to parse |
|
1779 // 2/2/2 characters of the input text, then if that fails, |
|
1780 // 1/2/2. We only adjust the width of the leftmost field; the |
|
1781 // others remain fixed. This allows "123456" => 12:34:56, but |
|
1782 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we |
|
1783 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2. |
|
1784 if (abutPat >= 0) { |
|
1785 // If we are at the start of a run of abutting fields, then |
|
1786 // shorten this field in each pass. If we can't shorten |
|
1787 // this field any more, then the parse of this set of |
|
1788 // abutting numeric fields has failed. |
|
1789 if (fieldPat == abutPat) { |
|
1790 count -= abutPass++; |
|
1791 if (count == 0) { |
|
1792 status = U_PARSE_ERROR; |
|
1793 goto ExitParse; |
|
1794 } |
|
1795 } |
|
1796 |
|
1797 pos = subParse(text, pos, ch, count, |
|
1798 TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter); |
|
1799 |
|
1800 // If the parse fails anywhere in the run, back up to the |
|
1801 // start of the run and retry. |
|
1802 if (pos < 0) { |
|
1803 i = abutPat - 1; |
|
1804 pos = abutStart; |
|
1805 continue; |
|
1806 } |
|
1807 } |
|
1808 |
|
1809 // Handle non-numeric fields and non-abutting numeric |
|
1810 // fields. |
|
1811 else if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored |
|
1812 int32_t s = subParse(text, pos, ch, count, |
|
1813 FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter); |
|
1814 |
|
1815 if (s == -pos-1) { |
|
1816 // era not present, in special cases allow this to continue |
|
1817 // from the position where the era was expected |
|
1818 s = pos; |
|
1819 |
|
1820 if (i+1 < fPattern.length()) { |
|
1821 // move to next pattern character |
|
1822 UChar ch = fPattern.charAt(i+1); |
|
1823 |
|
1824 // check for whitespace |
|
1825 if (PatternProps::isWhiteSpace(ch)) { |
|
1826 i++; |
|
1827 // Advance over run in pattern |
|
1828 while ((i+1)<fPattern.length() && |
|
1829 PatternProps::isWhiteSpace(fPattern.charAt(i+1))) { |
|
1830 ++i; |
|
1831 } |
|
1832 } |
|
1833 } |
|
1834 } |
|
1835 else if (s <= 0) { |
|
1836 status = U_PARSE_ERROR; |
|
1837 goto ExitParse; |
|
1838 } |
|
1839 pos = s; |
|
1840 } |
|
1841 } |
|
1842 |
|
1843 // Handle literal pattern characters. These are any |
|
1844 // quoted characters and non-alphabetic unquoted |
|
1845 // characters. |
|
1846 else { |
|
1847 |
|
1848 abutPat = -1; // End of any abutting fields |
|
1849 |
|
1850 if (! matchLiterals(fPattern, i, text, pos, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status))) { |
|
1851 status = U_PARSE_ERROR; |
|
1852 goto ExitParse; |
|
1853 } |
|
1854 } |
|
1855 } |
|
1856 |
|
1857 // Special hack for trailing "." after non-numeric field. |
|
1858 if (text.charAt(pos) == 0x2e && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) { |
|
1859 // only do if the last field is not numeric |
|
1860 if (isAfterNonNumericField(fPattern, fPattern.length())) { |
|
1861 pos++; // skip the extra "." |
|
1862 } |
|
1863 } |
|
1864 |
|
1865 // At this point the fields of Calendar have been set. Calendar |
|
1866 // will fill in default values for missing fields when the time |
|
1867 // is computed. |
|
1868 |
|
1869 parsePos.setIndex(pos); |
|
1870 |
|
1871 // This part is a problem: When we call parsedDate.after, we compute the time. |
|
1872 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year |
|
1873 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904. |
|
1874 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am |
|
1875 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am |
|
1876 // on that day. It is therefore parsed out to fields as 3:30 am. Then we |
|
1877 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is |
|
1878 // a Saturday, so it can have a 2:30 am -- and it should. [LIU] |
|
1879 /* |
|
1880 UDate parsedDate = calendar.getTime(); |
|
1881 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) { |
|
1882 calendar.add(Calendar.YEAR, 100); |
|
1883 parsedDate = calendar.getTime(); |
|
1884 } |
|
1885 */ |
|
1886 // Because of the above condition, save off the fields in case we need to readjust. |
|
1887 // The procedure we use here is not particularly efficient, but there is no other |
|
1888 // way to do this given the API restrictions present in Calendar. We minimize |
|
1889 // inefficiency by only performing this computation when it might apply, that is, |
|
1890 // when the two-digit year is equal to the start year, and thus might fall at the |
|
1891 // front or the back of the default century. This only works because we adjust |
|
1892 // the year correctly to start with in other cases -- see subParse(). |
|
1893 if (ambiguousYear[0] || tztype != UTZFMT_TIME_TYPE_UNKNOWN) // If this is true then the two-digit year == the default start year |
|
1894 { |
|
1895 // We need a copy of the fields, and we need to avoid triggering a call to |
|
1896 // complete(), which will recalculate the fields. Since we can't access |
|
1897 // the fields[] array in Calendar, we clone the entire object. This will |
|
1898 // stop working if Calendar.clone() is ever rewritten to call complete(). |
|
1899 Calendar *copy; |
|
1900 if (ambiguousYear[0]) { |
|
1901 copy = cal.clone(); |
|
1902 // Check for failed cloning. |
|
1903 if (copy == NULL) { |
|
1904 status = U_MEMORY_ALLOCATION_ERROR; |
|
1905 goto ExitParse; |
|
1906 } |
|
1907 UDate parsedDate = copy->getTime(status); |
|
1908 // {sfb} check internalGetDefaultCenturyStart |
|
1909 if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) { |
|
1910 // We can't use add here because that does a complete() first. |
|
1911 cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100); |
|
1912 } |
|
1913 delete copy; |
|
1914 } |
|
1915 |
|
1916 if (tztype != UTZFMT_TIME_TYPE_UNKNOWN) { |
|
1917 copy = cal.clone(); |
|
1918 // Check for failed cloning. |
|
1919 if (copy == NULL) { |
|
1920 status = U_MEMORY_ALLOCATION_ERROR; |
|
1921 goto ExitParse; |
|
1922 } |
|
1923 const TimeZone & tz = cal.getTimeZone(); |
|
1924 BasicTimeZone *btz = NULL; |
|
1925 |
|
1926 if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL |
|
1927 || dynamic_cast<const SimpleTimeZone *>(&tz) != NULL |
|
1928 || dynamic_cast<const RuleBasedTimeZone *>(&tz) != NULL |
|
1929 || dynamic_cast<const VTimeZone *>(&tz) != NULL) { |
|
1930 btz = (BasicTimeZone*)&tz; |
|
1931 } |
|
1932 |
|
1933 // Get local millis |
|
1934 copy->set(UCAL_ZONE_OFFSET, 0); |
|
1935 copy->set(UCAL_DST_OFFSET, 0); |
|
1936 UDate localMillis = copy->getTime(status); |
|
1937 |
|
1938 // Make sure parsed time zone type (Standard or Daylight) |
|
1939 // matches the rule used by the parsed time zone. |
|
1940 int32_t raw, dst; |
|
1941 if (btz != NULL) { |
|
1942 if (tztype == UTZFMT_TIME_TYPE_STANDARD) { |
|
1943 btz->getOffsetFromLocal(localMillis, |
|
1944 BasicTimeZone::kStandard, BasicTimeZone::kStandard, raw, dst, status); |
|
1945 } else { |
|
1946 btz->getOffsetFromLocal(localMillis, |
|
1947 BasicTimeZone::kDaylight, BasicTimeZone::kDaylight, raw, dst, status); |
|
1948 } |
|
1949 } else { |
|
1950 // No good way to resolve ambiguous time at transition, |
|
1951 // but following code work in most case. |
|
1952 tz.getOffset(localMillis, TRUE, raw, dst, status); |
|
1953 } |
|
1954 |
|
1955 // Now, compare the results with parsed type, either standard or daylight saving time |
|
1956 int32_t resolvedSavings = dst; |
|
1957 if (tztype == UTZFMT_TIME_TYPE_STANDARD) { |
|
1958 if (dst != 0) { |
|
1959 // Override DST_OFFSET = 0 in the result calendar |
|
1960 resolvedSavings = 0; |
|
1961 } |
|
1962 } else { // tztype == TZTYPE_DST |
|
1963 if (dst == 0) { |
|
1964 if (btz != NULL) { |
|
1965 UDate time = localMillis + raw; |
|
1966 // We use the nearest daylight saving time rule. |
|
1967 TimeZoneTransition beforeTrs, afterTrs; |
|
1968 UDate beforeT = time, afterT = time; |
|
1969 int32_t beforeSav = 0, afterSav = 0; |
|
1970 UBool beforeTrsAvail, afterTrsAvail; |
|
1971 |
|
1972 // Search for DST rule before or on the time |
|
1973 while (TRUE) { |
|
1974 beforeTrsAvail = btz->getPreviousTransition(beforeT, TRUE, beforeTrs); |
|
1975 if (!beforeTrsAvail) { |
|
1976 break; |
|
1977 } |
|
1978 beforeT = beforeTrs.getTime() - 1; |
|
1979 beforeSav = beforeTrs.getFrom()->getDSTSavings(); |
|
1980 if (beforeSav != 0) { |
|
1981 break; |
|
1982 } |
|
1983 } |
|
1984 |
|
1985 // Search for DST rule after the time |
|
1986 while (TRUE) { |
|
1987 afterTrsAvail = btz->getNextTransition(afterT, FALSE, afterTrs); |
|
1988 if (!afterTrsAvail) { |
|
1989 break; |
|
1990 } |
|
1991 afterT = afterTrs.getTime(); |
|
1992 afterSav = afterTrs.getTo()->getDSTSavings(); |
|
1993 if (afterSav != 0) { |
|
1994 break; |
|
1995 } |
|
1996 } |
|
1997 |
|
1998 if (beforeTrsAvail && afterTrsAvail) { |
|
1999 if (time - beforeT > afterT - time) { |
|
2000 resolvedSavings = afterSav; |
|
2001 } else { |
|
2002 resolvedSavings = beforeSav; |
|
2003 } |
|
2004 } else if (beforeTrsAvail && beforeSav != 0) { |
|
2005 resolvedSavings = beforeSav; |
|
2006 } else if (afterTrsAvail && afterSav != 0) { |
|
2007 resolvedSavings = afterSav; |
|
2008 } else { |
|
2009 resolvedSavings = btz->getDSTSavings(); |
|
2010 } |
|
2011 } else { |
|
2012 resolvedSavings = tz.getDSTSavings(); |
|
2013 } |
|
2014 if (resolvedSavings == 0) { |
|
2015 // final fallback |
|
2016 resolvedSavings = U_MILLIS_PER_HOUR; |
|
2017 } |
|
2018 } |
|
2019 } |
|
2020 cal.set(UCAL_ZONE_OFFSET, raw); |
|
2021 cal.set(UCAL_DST_OFFSET, resolvedSavings); |
|
2022 delete copy; |
|
2023 } |
|
2024 } |
|
2025 ExitParse: |
|
2026 // Set the parsed result if local calendar is used |
|
2027 // instead of the input calendar |
|
2028 if (U_SUCCESS(status) && workCal != &cal) { |
|
2029 cal.setTimeZone(workCal->getTimeZone()); |
|
2030 cal.setTime(workCal->getTime(status), status); |
|
2031 } |
|
2032 |
|
2033 if (numericLeapMonthFormatter != NULL) { |
|
2034 delete numericLeapMonthFormatter; |
|
2035 } |
|
2036 if (calClone != NULL) { |
|
2037 delete calClone; |
|
2038 } |
|
2039 |
|
2040 // If any Calendar calls failed, we pretend that we |
|
2041 // couldn't parse the string, when in reality this isn't quite accurate-- |
|
2042 // we did parse it; the Calendar calls just failed. |
|
2043 if (U_FAILURE(status)) { |
|
2044 parsePos.setErrorIndex(pos); |
|
2045 parsePos.setIndex(start); |
|
2046 } |
|
2047 } |
|
2048 |
|
2049 //---------------------------------------------------------------------- |
|
2050 |
|
2051 static UBool |
|
2052 newBestMatchWithOptionalDot(const UnicodeString &lcaseText, |
|
2053 const UnicodeString &data, |
|
2054 UnicodeString &bestMatchName, |
|
2055 int32_t &bestMatchLength); |
|
2056 |
|
2057 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text, |
|
2058 int32_t start, |
|
2059 UCalendarDateFields field, |
|
2060 const UnicodeString* data, |
|
2061 int32_t dataCount, |
|
2062 Calendar& cal) const |
|
2063 { |
|
2064 int32_t i = 0; |
|
2065 int32_t count = dataCount; |
|
2066 |
|
2067 // There may be multiple strings in the data[] array which begin with |
|
2068 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech). |
|
2069 // We keep track of the longest match, and return that. Note that this |
|
2070 // unfortunately requires us to test all array elements. |
|
2071 int32_t bestMatchLength = 0, bestMatch = -1; |
|
2072 UnicodeString bestMatchName; |
|
2073 |
|
2074 // {sfb} kludge to support case-insensitive comparison |
|
2075 // {markus 2002oct11} do not just use caseCompareBetween because we do not know |
|
2076 // the length of the match after case folding |
|
2077 // {alan 20040607} don't case change the whole string, since the length |
|
2078 // can change |
|
2079 // TODO we need a case-insensitive startsWith function |
|
2080 UnicodeString lcaseText; |
|
2081 text.extract(start, INT32_MAX, lcaseText); |
|
2082 lcaseText.foldCase(); |
|
2083 |
|
2084 for (; i < count; ++i) |
|
2085 { |
|
2086 // Always compare if we have no match yet; otherwise only compare |
|
2087 // against potentially better matches (longer strings). |
|
2088 |
|
2089 if (newBestMatchWithOptionalDot(lcaseText, data[i], bestMatchName, bestMatchLength)) { |
|
2090 bestMatch = i; |
|
2091 } |
|
2092 } |
|
2093 if (bestMatch >= 0) |
|
2094 { |
|
2095 cal.set(field, bestMatch * 3); |
|
2096 |
|
2097 // Once we have a match, we have to determine the length of the |
|
2098 // original source string. This will usually be == the length of |
|
2099 // the case folded string, but it may differ (e.g. sharp s). |
|
2100 |
|
2101 // Most of the time, the length will be the same as the length |
|
2102 // of the string from the locale data. Sometimes it will be |
|
2103 // different, in which case we will have to figure it out by |
|
2104 // adding a character at a time, until we have a match. We do |
|
2105 // this all in one loop, where we try 'len' first (at index |
|
2106 // i==0). |
|
2107 int32_t len = bestMatchName.length(); // 99+% of the time |
|
2108 int32_t n = text.length() - start; |
|
2109 for (i=0; i<=n; ++i) { |
|
2110 int32_t j=i; |
|
2111 if (i == 0) { |
|
2112 j = len; |
|
2113 } else if (i == len) { |
|
2114 continue; // already tried this when i was 0 |
|
2115 } |
|
2116 text.extract(start, j, lcaseText); |
|
2117 lcaseText.foldCase(); |
|
2118 if (bestMatchName == lcaseText) { |
|
2119 return start + j; |
|
2120 } |
|
2121 } |
|
2122 } |
|
2123 |
|
2124 return -start; |
|
2125 } |
|
2126 |
|
2127 //---------------------------------------------------------------------- |
|
2128 UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern, |
|
2129 int32_t &patternOffset, |
|
2130 const UnicodeString &text, |
|
2131 int32_t &textOffset, |
|
2132 UBool lenient) |
|
2133 { |
|
2134 UBool inQuote = FALSE; |
|
2135 UnicodeString literal; |
|
2136 int32_t i = patternOffset; |
|
2137 |
|
2138 // scan pattern looking for contiguous literal characters |
|
2139 for ( ; i < pattern.length(); i += 1) { |
|
2140 UChar ch = pattern.charAt(i); |
|
2141 |
|
2142 if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { // unquoted [A-Za-z] |
|
2143 break; |
|
2144 } |
|
2145 |
|
2146 if (ch == QUOTE) { |
|
2147 // Match a quote literal ('') inside OR outside of quotes |
|
2148 if ((i + 1) < pattern.length() && pattern.charAt(i + 1) == QUOTE) { |
|
2149 i += 1; |
|
2150 } else { |
|
2151 inQuote = !inQuote; |
|
2152 continue; |
|
2153 } |
|
2154 } |
|
2155 |
|
2156 literal += ch; |
|
2157 } |
|
2158 |
|
2159 // at this point, literal contains the literal text |
|
2160 // and i is the index of the next non-literal pattern character. |
|
2161 int32_t p; |
|
2162 int32_t t = textOffset; |
|
2163 |
|
2164 if (lenient) { |
|
2165 // trim leading, trailing whitespace from |
|
2166 // the literal text |
|
2167 literal.trim(); |
|
2168 |
|
2169 // ignore any leading whitespace in the text |
|
2170 while (t < text.length() && u_isWhitespace(text.charAt(t))) { |
|
2171 t += 1; |
|
2172 } |
|
2173 } |
|
2174 |
|
2175 for (p = 0; p < literal.length() && t < text.length();) { |
|
2176 UBool needWhitespace = FALSE; |
|
2177 |
|
2178 while (p < literal.length() && PatternProps::isWhiteSpace(literal.charAt(p))) { |
|
2179 needWhitespace = TRUE; |
|
2180 p += 1; |
|
2181 } |
|
2182 |
|
2183 if (needWhitespace) { |
|
2184 int32_t tStart = t; |
|
2185 |
|
2186 while (t < text.length()) { |
|
2187 UChar tch = text.charAt(t); |
|
2188 |
|
2189 if (!u_isUWhiteSpace(tch) && !PatternProps::isWhiteSpace(tch)) { |
|
2190 break; |
|
2191 } |
|
2192 |
|
2193 t += 1; |
|
2194 } |
|
2195 |
|
2196 // TODO: should we require internal spaces |
|
2197 // in lenient mode? (There won't be any |
|
2198 // leading or trailing spaces) |
|
2199 if (!lenient && t == tStart) { |
|
2200 // didn't find matching whitespace: |
|
2201 // an error in strict mode |
|
2202 return FALSE; |
|
2203 } |
|
2204 |
|
2205 // In strict mode, this run of whitespace |
|
2206 // may have been at the end. |
|
2207 if (p >= literal.length()) { |
|
2208 break; |
|
2209 } |
|
2210 } |
|
2211 |
|
2212 if (t >= text.length() || literal.charAt(p) != text.charAt(t)) { |
|
2213 // Ran out of text, or found a non-matching character: |
|
2214 // OK in lenient mode, an error in strict mode. |
|
2215 if (lenient) { |
|
2216 if (t == textOffset && text.charAt(t) == 0x2e && |
|
2217 isAfterNonNumericField(pattern, patternOffset)) { |
|
2218 // Lenient mode and the literal input text begins with a "." and |
|
2219 // we are after a non-numeric field: We skip the "." |
|
2220 ++t; |
|
2221 continue; // Do not update p. |
|
2222 } |
|
2223 break; |
|
2224 } |
|
2225 |
|
2226 return FALSE; |
|
2227 } |
|
2228 ++p; |
|
2229 ++t; |
|
2230 } |
|
2231 |
|
2232 // At this point if we're in strict mode we have a complete match. |
|
2233 // If we're in lenient mode we may have a partial match, or no |
|
2234 // match at all. |
|
2235 if (p <= 0) { |
|
2236 // no match. Pretend it matched a run of whitespace |
|
2237 // and ignorables in the text. |
|
2238 const UnicodeSet *ignorables = NULL; |
|
2239 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(pattern.charAt(i)); |
|
2240 if (patternCharIndex != UDAT_FIELD_COUNT) { |
|
2241 ignorables = SimpleDateFormatStaticSets::getIgnorables(patternCharIndex); |
|
2242 } |
|
2243 |
|
2244 for (t = textOffset; t < text.length(); t += 1) { |
|
2245 UChar ch = text.charAt(t); |
|
2246 |
|
2247 if (ignorables == NULL || !ignorables->contains(ch)) { |
|
2248 break; |
|
2249 } |
|
2250 } |
|
2251 } |
|
2252 |
|
2253 // if we get here, we've got a complete match. |
|
2254 patternOffset = i - 1; |
|
2255 textOffset = t; |
|
2256 |
|
2257 return TRUE; |
|
2258 } |
|
2259 |
|
2260 //---------------------------------------------------------------------- |
|
2261 |
|
2262 int32_t SimpleDateFormat::matchString(const UnicodeString& text, |
|
2263 int32_t start, |
|
2264 UCalendarDateFields field, |
|
2265 const UnicodeString* data, |
|
2266 int32_t dataCount, |
|
2267 const UnicodeString* monthPattern, |
|
2268 Calendar& cal) const |
|
2269 { |
|
2270 int32_t i = 0; |
|
2271 int32_t count = dataCount; |
|
2272 |
|
2273 if (field == UCAL_DAY_OF_WEEK) i = 1; |
|
2274 |
|
2275 // There may be multiple strings in the data[] array which begin with |
|
2276 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech). |
|
2277 // We keep track of the longest match, and return that. Note that this |
|
2278 // unfortunately requires us to test all array elements. |
|
2279 int32_t bestMatchLength = 0, bestMatch = -1; |
|
2280 UnicodeString bestMatchName; |
|
2281 int32_t isLeapMonth = 0; |
|
2282 |
|
2283 // {sfb} kludge to support case-insensitive comparison |
|
2284 // {markus 2002oct11} do not just use caseCompareBetween because we do not know |
|
2285 // the length of the match after case folding |
|
2286 // {alan 20040607} don't case change the whole string, since the length |
|
2287 // can change |
|
2288 // TODO we need a case-insensitive startsWith function |
|
2289 UnicodeString lcaseText; |
|
2290 text.extract(start, INT32_MAX, lcaseText); |
|
2291 lcaseText.foldCase(); |
|
2292 |
|
2293 for (; i < count; ++i) |
|
2294 { |
|
2295 // Always compare if we have no match yet; otherwise only compare |
|
2296 // against potentially better matches (longer strings). |
|
2297 |
|
2298 if (newBestMatchWithOptionalDot(lcaseText, data[i], bestMatchName, bestMatchLength)) { |
|
2299 bestMatch = i; |
|
2300 isLeapMonth = 0; |
|
2301 } |
|
2302 |
|
2303 if (monthPattern != NULL) { |
|
2304 UErrorCode status = U_ZERO_ERROR; |
|
2305 UnicodeString leapMonthName; |
|
2306 Formattable monthName((const UnicodeString&)(data[i])); |
|
2307 MessageFormat::format(*monthPattern, &monthName, 1, leapMonthName, status); |
|
2308 if (U_SUCCESS(status)) { |
|
2309 if (newBestMatchWithOptionalDot(lcaseText, leapMonthName, bestMatchName, bestMatchLength)) { |
|
2310 bestMatch = i; |
|
2311 isLeapMonth = 1; |
|
2312 } |
|
2313 } |
|
2314 } |
|
2315 } |
|
2316 if (bestMatch >= 0) |
|
2317 { |
|
2318 // Adjustment for Hebrew Calendar month Adar II |
|
2319 if (!strcmp(cal.getType(),"hebrew") && field==UCAL_MONTH && bestMatch==13) { |
|
2320 cal.set(field,6); |
|
2321 } |
|
2322 else { |
|
2323 if (field == UCAL_YEAR) { |
|
2324 bestMatch++; // only get here for cyclic year names, which match 1-based years 1-60 |
|
2325 } |
|
2326 cal.set(field, bestMatch); |
|
2327 } |
|
2328 if (monthPattern != NULL) { |
|
2329 cal.set(UCAL_IS_LEAP_MONTH, isLeapMonth); |
|
2330 } |
|
2331 |
|
2332 // Once we have a match, we have to determine the length of the |
|
2333 // original source string. This will usually be == the length of |
|
2334 // the case folded string, but it may differ (e.g. sharp s). |
|
2335 |
|
2336 // Most of the time, the length will be the same as the length |
|
2337 // of the string from the locale data. Sometimes it will be |
|
2338 // different, in which case we will have to figure it out by |
|
2339 // adding a character at a time, until we have a match. We do |
|
2340 // this all in one loop, where we try 'len' first (at index |
|
2341 // i==0). |
|
2342 int32_t len = bestMatchName.length(); // 99+% of the time |
|
2343 int32_t n = text.length() - start; |
|
2344 for (i=0; i<=n; ++i) { |
|
2345 int32_t j=i; |
|
2346 if (i == 0) { |
|
2347 j = len; |
|
2348 } else if (i == len) { |
|
2349 continue; // already tried this when i was 0 |
|
2350 } |
|
2351 text.extract(start, j, lcaseText); |
|
2352 lcaseText.foldCase(); |
|
2353 if (bestMatchName == lcaseText) { |
|
2354 return start + j; |
|
2355 } |
|
2356 } |
|
2357 } |
|
2358 |
|
2359 return -start; |
|
2360 } |
|
2361 |
|
2362 static UBool |
|
2363 newBestMatchWithOptionalDot(const UnicodeString &lcaseText, |
|
2364 const UnicodeString &data, |
|
2365 UnicodeString &bestMatchName, |
|
2366 int32_t &bestMatchLength) { |
|
2367 UnicodeString lcase; |
|
2368 lcase.fastCopyFrom(data).foldCase(); |
|
2369 int32_t length = lcase.length(); |
|
2370 if (length <= bestMatchLength) { |
|
2371 // data cannot provide a better match. |
|
2372 return FALSE; |
|
2373 } |
|
2374 |
|
2375 if (lcaseText.compareBetween(0, length, lcase, 0, length) == 0) { |
|
2376 // normal match |
|
2377 bestMatchName = lcase; |
|
2378 bestMatchLength = length; |
|
2379 return TRUE; |
|
2380 } |
|
2381 if (lcase.charAt(--length) == 0x2e) { |
|
2382 if (lcaseText.compareBetween(0, length, lcase, 0, length) == 0) { |
|
2383 // The input text matches the data except for data's trailing dot. |
|
2384 bestMatchName = lcase; |
|
2385 bestMatchName.truncate(length); |
|
2386 bestMatchLength = length; |
|
2387 return TRUE; |
|
2388 } |
|
2389 } |
|
2390 return FALSE; |
|
2391 } |
|
2392 |
|
2393 //---------------------------------------------------------------------- |
|
2394 |
|
2395 void |
|
2396 SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status) |
|
2397 { |
|
2398 parseAmbiguousDatesAsAfter(d, status); |
|
2399 } |
|
2400 |
|
2401 /** |
|
2402 * Private member function that converts the parsed date strings into |
|
2403 * timeFields. Returns -start (for ParsePosition) if failed. |
|
2404 * @param text the time text to be parsed. |
|
2405 * @param start where to start parsing. |
|
2406 * @param ch the pattern character for the date field text to be parsed. |
|
2407 * @param count the count of a pattern character. |
|
2408 * @return the new start position if matching succeeded; a negative number |
|
2409 * indicating matching failure, otherwise. |
|
2410 */ |
|
2411 int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count, |
|
2412 UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal, |
|
2413 int32_t patLoc, MessageFormat * numericLeapMonthFormatter) const |
|
2414 { |
|
2415 Formattable number; |
|
2416 int32_t value = 0; |
|
2417 int32_t i; |
|
2418 int32_t ps = 0; |
|
2419 UErrorCode status = U_ZERO_ERROR; |
|
2420 ParsePosition pos(0); |
|
2421 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch); |
|
2422 NumberFormat *currentNumberFormat; |
|
2423 UnicodeString temp; |
|
2424 UBool gotNumber = FALSE; |
|
2425 |
|
2426 #if defined (U_DEBUG_CAL) |
|
2427 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start); |
|
2428 #endif |
|
2429 |
|
2430 if (patternCharIndex == UDAT_FIELD_COUNT) { |
|
2431 return -start; |
|
2432 } |
|
2433 |
|
2434 currentNumberFormat = getNumberFormatByIndex(patternCharIndex); |
|
2435 UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; |
|
2436 UnicodeString hebr("hebr", 4, US_INV); |
|
2437 |
|
2438 if (numericLeapMonthFormatter != NULL) { |
|
2439 numericLeapMonthFormatter->setFormats((const Format **)¤tNumberFormat, 1); |
|
2440 } |
|
2441 UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0 || uprv_strcmp(cal.getType(),"dangi") == 0); |
|
2442 |
|
2443 // If there are any spaces here, skip over them. If we hit the end |
|
2444 // of the string, then fail. |
|
2445 for (;;) { |
|
2446 if (start >= text.length()) { |
|
2447 return -start; |
|
2448 } |
|
2449 UChar32 c = text.char32At(start); |
|
2450 if (!u_isUWhiteSpace(c) /*||*/ && !PatternProps::isWhiteSpace(c)) { |
|
2451 break; |
|
2452 } |
|
2453 start += U16_LENGTH(c); |
|
2454 } |
|
2455 pos.setIndex(start); |
|
2456 |
|
2457 // We handle a few special cases here where we need to parse |
|
2458 // a number value. We handle further, more generic cases below. We need |
|
2459 // to handle some of them here because some fields require extra processing on |
|
2460 // the parsed value. |
|
2461 if (patternCharIndex == UDAT_HOUR_OF_DAY1_FIELD || // k |
|
2462 patternCharIndex == UDAT_HOUR_OF_DAY0_FIELD || // H |
|
2463 patternCharIndex == UDAT_HOUR1_FIELD || // h |
|
2464 patternCharIndex == UDAT_HOUR0_FIELD || // K |
|
2465 (patternCharIndex == UDAT_DOW_LOCAL_FIELD && count <= 2) || // e |
|
2466 (patternCharIndex == UDAT_STANDALONE_DAY_FIELD && count <= 2) || // c |
|
2467 (patternCharIndex == UDAT_MONTH_FIELD && count <= 2) || // M |
|
2468 (patternCharIndex == UDAT_STANDALONE_MONTH_FIELD && count <= 2) || // L |
|
2469 (patternCharIndex == UDAT_QUARTER_FIELD && count <= 2) || // Q |
|
2470 (patternCharIndex == UDAT_STANDALONE_QUARTER_FIELD && count <= 2) || // q |
|
2471 patternCharIndex == UDAT_YEAR_FIELD || // y |
|
2472 patternCharIndex == UDAT_YEAR_WOY_FIELD || // Y |
|
2473 patternCharIndex == UDAT_YEAR_NAME_FIELD || // U (falls back to numeric) |
|
2474 (patternCharIndex == UDAT_ERA_FIELD && isChineseCalendar) || // G |
|
2475 patternCharIndex == UDAT_FRACTIONAL_SECOND_FIELD) // S |
|
2476 { |
|
2477 int32_t parseStart = pos.getIndex(); |
|
2478 // It would be good to unify this with the obeyCount logic below, |
|
2479 // but that's going to be difficult. |
|
2480 const UnicodeString* src; |
|
2481 |
|
2482 UBool parsedNumericLeapMonth = FALSE; |
|
2483 if (numericLeapMonthFormatter != NULL && (patternCharIndex == UDAT_MONTH_FIELD || patternCharIndex == UDAT_STANDALONE_MONTH_FIELD)) { |
|
2484 int32_t argCount; |
|
2485 Formattable * args = numericLeapMonthFormatter->parse(text, pos, argCount); |
|
2486 if (args != NULL && argCount == 1 && pos.getIndex() > parseStart && args[0].isNumeric()) { |
|
2487 parsedNumericLeapMonth = TRUE; |
|
2488 number.setLong(args[0].getLong()); |
|
2489 cal.set(UCAL_IS_LEAP_MONTH, 1); |
|
2490 delete[] args; |
|
2491 } else { |
|
2492 pos.setIndex(parseStart); |
|
2493 cal.set(UCAL_IS_LEAP_MONTH, 0); |
|
2494 } |
|
2495 } |
|
2496 |
|
2497 if (!parsedNumericLeapMonth) { |
|
2498 if (obeyCount) { |
|
2499 if ((start+count) > text.length()) { |
|
2500 return -start; |
|
2501 } |
|
2502 |
|
2503 text.extractBetween(0, start + count, temp); |
|
2504 src = &temp; |
|
2505 } else { |
|
2506 src = &text; |
|
2507 } |
|
2508 |
|
2509 parseInt(*src, number, pos, allowNegative,currentNumberFormat); |
|
2510 } |
|
2511 |
|
2512 int32_t txtLoc = pos.getIndex(); |
|
2513 |
|
2514 if (txtLoc > parseStart) { |
|
2515 value = number.getLong(); |
|
2516 gotNumber = TRUE; |
|
2517 |
|
2518 // suffix processing |
|
2519 if (value < 0 ) { |
|
2520 txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, TRUE); |
|
2521 if (txtLoc != pos.getIndex()) { |
|
2522 value *= -1; |
|
2523 } |
|
2524 } |
|
2525 else { |
|
2526 txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, FALSE); |
|
2527 } |
|
2528 |
|
2529 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) { |
|
2530 // Check the range of the value |
|
2531 int32_t bias = gFieldRangeBias[patternCharIndex]; |
|
2532 if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) { |
|
2533 return -start; |
|
2534 } |
|
2535 } |
|
2536 |
|
2537 pos.setIndex(txtLoc); |
|
2538 } |
|
2539 } |
|
2540 |
|
2541 // Make sure that we got a number if |
|
2542 // we want one, and didn't get one |
|
2543 // if we don't want one. |
|
2544 switch (patternCharIndex) { |
|
2545 case UDAT_HOUR_OF_DAY1_FIELD: |
|
2546 case UDAT_HOUR_OF_DAY0_FIELD: |
|
2547 case UDAT_HOUR1_FIELD: |
|
2548 case UDAT_HOUR0_FIELD: |
|
2549 // special range check for hours: |
|
2550 if (value < 0 || value > 24) { |
|
2551 return -start; |
|
2552 } |
|
2553 |
|
2554 // fall through to gotNumber check |
|
2555 |
|
2556 case UDAT_YEAR_FIELD: |
|
2557 case UDAT_YEAR_WOY_FIELD: |
|
2558 case UDAT_FRACTIONAL_SECOND_FIELD: |
|
2559 // these must be a number |
|
2560 if (! gotNumber) { |
|
2561 return -start; |
|
2562 } |
|
2563 |
|
2564 break; |
|
2565 |
|
2566 default: |
|
2567 // we check the rest of the fields below. |
|
2568 break; |
|
2569 } |
|
2570 |
|
2571 switch (patternCharIndex) { |
|
2572 case UDAT_ERA_FIELD: |
|
2573 if (isChineseCalendar) { |
|
2574 if (!gotNumber) { |
|
2575 return -start; |
|
2576 } |
|
2577 cal.set(UCAL_ERA, value); |
|
2578 return pos.getIndex(); |
|
2579 } |
|
2580 if (count == 5) { |
|
2581 ps = matchString(text, start, UCAL_ERA, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount, NULL, cal); |
|
2582 } else if (count == 4) { |
|
2583 ps = matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, NULL, cal); |
|
2584 } else { |
|
2585 ps = matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, NULL, cal); |
|
2586 } |
|
2587 |
|
2588 // check return position, if it equals -start, then matchString error |
|
2589 // special case the return code so we don't necessarily fail out until we |
|
2590 // verify no year information also |
|
2591 if (ps == -start) |
|
2592 ps--; |
|
2593 |
|
2594 return ps; |
|
2595 |
|
2596 case UDAT_YEAR_FIELD: |
|
2597 // If there are 3 or more YEAR pattern characters, this indicates |
|
2598 // that the year value is to be treated literally, without any |
|
2599 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise |
|
2600 // we made adjustments to place the 2-digit year in the proper |
|
2601 // century, for parsed strings from "00" to "99". Any other string |
|
2602 // is treated literally: "2250", "-1", "1", "002". |
|
2603 if (fDateOverride.compare(hebr)==0 && value < 1000) { |
|
2604 value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR; |
|
2605 } else if ((pos.getIndex() - start) == 2 && !isChineseCalendar |
|
2606 && u_isdigit(text.charAt(start)) |
|
2607 && u_isdigit(text.charAt(start+1))) |
|
2608 { |
|
2609 // only adjust year for patterns less than 3. |
|
2610 if(count < 3) { |
|
2611 // Assume for example that the defaultCenturyStart is 6/18/1903. |
|
2612 // This means that two-digit years will be forced into the range |
|
2613 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02 |
|
2614 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond |
|
2615 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the |
|
2616 // other fields specify a date before 6/18, or 1903 if they specify a |
|
2617 // date afterwards. As a result, 03 is an ambiguous year. All other |
|
2618 // two-digit years are unambiguous. |
|
2619 if(fHaveDefaultCentury) { // check if this formatter even has a pivot year |
|
2620 int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100; |
|
2621 ambiguousYear[0] = (value == ambiguousTwoDigitYear); |
|
2622 value += (fDefaultCenturyStartYear/100)*100 + |
|
2623 (value < ambiguousTwoDigitYear ? 100 : 0); |
|
2624 } |
|
2625 } |
|
2626 } |
|
2627 cal.set(UCAL_YEAR, value); |
|
2628 |
|
2629 // Delayed checking for adjustment of Hebrew month numbers in non-leap years. |
|
2630 if (saveHebrewMonth >= 0) { |
|
2631 HebrewCalendar *hc = (HebrewCalendar*)&cal; |
|
2632 if (!hc->isLeapYear(value) && saveHebrewMonth >= 6) { |
|
2633 cal.set(UCAL_MONTH,saveHebrewMonth); |
|
2634 } else { |
|
2635 cal.set(UCAL_MONTH,saveHebrewMonth-1); |
|
2636 } |
|
2637 saveHebrewMonth = -1; |
|
2638 } |
|
2639 return pos.getIndex(); |
|
2640 |
|
2641 case UDAT_YEAR_WOY_FIELD: |
|
2642 // Comment is the same as for UDAT_Year_FIELDs - look above |
|
2643 if (fDateOverride.compare(hebr)==0 && value < 1000) { |
|
2644 value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR; |
|
2645 } else if ((pos.getIndex() - start) == 2 |
|
2646 && u_isdigit(text.charAt(start)) |
|
2647 && u_isdigit(text.charAt(start+1)) |
|
2648 && fHaveDefaultCentury ) |
|
2649 { |
|
2650 int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100; |
|
2651 ambiguousYear[0] = (value == ambiguousTwoDigitYear); |
|
2652 value += (fDefaultCenturyStartYear/100)*100 + |
|
2653 (value < ambiguousTwoDigitYear ? 100 : 0); |
|
2654 } |
|
2655 cal.set(UCAL_YEAR_WOY, value); |
|
2656 return pos.getIndex(); |
|
2657 |
|
2658 case UDAT_YEAR_NAME_FIELD: |
|
2659 if (fSymbols->fShortYearNames != NULL) { |
|
2660 int32_t newStart = matchString(text, start, UCAL_YEAR, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount, NULL, cal); |
|
2661 if (newStart > 0) { |
|
2662 return newStart; |
|
2663 } |
|
2664 } |
|
2665 if (gotNumber && (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC,status) || value > fSymbols->fShortYearNamesCount)) { |
|
2666 cal.set(UCAL_YEAR, value); |
|
2667 return pos.getIndex(); |
|
2668 } |
|
2669 return -start; |
|
2670 |
|
2671 case UDAT_MONTH_FIELD: |
|
2672 case UDAT_STANDALONE_MONTH_FIELD: |
|
2673 if (gotNumber) // i.e., M or MM. |
|
2674 { |
|
2675 // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether |
|
2676 // 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 |
|
2677 // the year is parsed. |
|
2678 if (!strcmp(cal.getType(),"hebrew")) { |
|
2679 HebrewCalendar *hc = (HebrewCalendar*)&cal; |
|
2680 if (cal.isSet(UCAL_YEAR)) { |
|
2681 UErrorCode status = U_ZERO_ERROR; |
|
2682 if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6) { |
|
2683 cal.set(UCAL_MONTH, value); |
|
2684 } else { |
|
2685 cal.set(UCAL_MONTH, value - 1); |
|
2686 } |
|
2687 } else { |
|
2688 saveHebrewMonth = value; |
|
2689 } |
|
2690 } else { |
|
2691 // Don't want to parse the month if it is a string |
|
2692 // while pattern uses numeric style: M/MM, L/LL |
|
2693 // [We computed 'value' above.] |
|
2694 cal.set(UCAL_MONTH, value - 1); |
|
2695 } |
|
2696 return pos.getIndex(); |
|
2697 } else { |
|
2698 // count >= 3 // i.e., MMM/MMMM, LLL/LLLL |
|
2699 // Want to be able to parse both short and long forms. |
|
2700 // Try count == 4 first: |
|
2701 UnicodeString * wideMonthPat = NULL; |
|
2702 UnicodeString * shortMonthPat = NULL; |
|
2703 if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) { |
|
2704 if (patternCharIndex==UDAT_MONTH_FIELD) { |
|
2705 wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide]; |
|
2706 shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev]; |
|
2707 } else { |
|
2708 wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide]; |
|
2709 shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev]; |
|
2710 } |
|
2711 } |
|
2712 int32_t newStart = 0; |
|
2713 if (patternCharIndex==UDAT_MONTH_FIELD) { |
|
2714 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fMonths, fSymbols->fMonthsCount, wideMonthPat, cal); // try MMMM |
|
2715 if (newStart > 0) { |
|
2716 return newStart; |
|
2717 } |
|
2718 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fShortMonths, fSymbols->fShortMonthsCount, shortMonthPat, cal); // try MMM |
|
2719 } else { |
|
2720 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, wideMonthPat, cal); // try LLLL |
|
2721 if (newStart > 0) { |
|
2722 return newStart; |
|
2723 } |
|
2724 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, shortMonthPat, cal); // try LLL |
|
2725 } |
|
2726 if (newStart > 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) // currently we do not try to parse MMMMM/LLLLL: #8860 |
|
2727 return newStart; |
|
2728 // else we allowing parsing as number, below |
|
2729 } |
|
2730 break; |
|
2731 |
|
2732 case UDAT_HOUR_OF_DAY1_FIELD: |
|
2733 // [We computed 'value' above.] |
|
2734 if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1) |
|
2735 value = 0; |
|
2736 |
|
2737 // fall through to set field |
|
2738 |
|
2739 case UDAT_HOUR_OF_DAY0_FIELD: |
|
2740 cal.set(UCAL_HOUR_OF_DAY, value); |
|
2741 return pos.getIndex(); |
|
2742 |
|
2743 case UDAT_FRACTIONAL_SECOND_FIELD: |
|
2744 // Fractional seconds left-justify |
|
2745 i = pos.getIndex() - start; |
|
2746 if (i < 3) { |
|
2747 while (i < 3) { |
|
2748 value *= 10; |
|
2749 i++; |
|
2750 } |
|
2751 } else { |
|
2752 int32_t a = 1; |
|
2753 while (i > 3) { |
|
2754 a *= 10; |
|
2755 i--; |
|
2756 } |
|
2757 value /= a; |
|
2758 } |
|
2759 cal.set(UCAL_MILLISECOND, value); |
|
2760 return pos.getIndex(); |
|
2761 |
|
2762 case UDAT_DOW_LOCAL_FIELD: |
|
2763 if (gotNumber) // i.e., e or ee |
|
2764 { |
|
2765 // [We computed 'value' above.] |
|
2766 cal.set(UCAL_DOW_LOCAL, value); |
|
2767 return pos.getIndex(); |
|
2768 } |
|
2769 // else for eee-eeeee fall through to handling of EEE-EEEEE |
|
2770 // fall through, do not break here |
|
2771 case UDAT_DAY_OF_WEEK_FIELD: |
|
2772 { |
|
2773 // Want to be able to parse both short and long forms. |
|
2774 // Try count == 4 (EEEE) wide first: |
|
2775 int32_t newStart = 0; |
|
2776 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, |
|
2777 fSymbols->fWeekdays, fSymbols->fWeekdaysCount, NULL, cal)) > 0) |
|
2778 return newStart; |
|
2779 // EEEE wide failed, now try EEE abbreviated |
|
2780 else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, |
|
2781 fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, NULL, cal)) > 0) |
|
2782 return newStart; |
|
2783 // EEE abbreviated failed, now try EEEEEE short |
|
2784 else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, |
|
2785 fSymbols->fShorterWeekdays, fSymbols->fShorterWeekdaysCount, NULL, cal)) > 0) |
|
2786 return newStart; |
|
2787 // EEEEEE short failed, now try EEEEE narrow |
|
2788 else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, |
|
2789 fSymbols->fNarrowWeekdays, fSymbols->fNarrowWeekdaysCount, NULL, cal)) > 0) |
|
2790 return newStart; |
|
2791 else if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status) || patternCharIndex == UDAT_DAY_OF_WEEK_FIELD) |
|
2792 return newStart; |
|
2793 // else we allowing parsing as number, below |
|
2794 } |
|
2795 break; |
|
2796 |
|
2797 case UDAT_STANDALONE_DAY_FIELD: |
|
2798 { |
|
2799 if (gotNumber) // c or cc |
|
2800 { |
|
2801 // [We computed 'value' above.] |
|
2802 cal.set(UCAL_DOW_LOCAL, value); |
|
2803 return pos.getIndex(); |
|
2804 } |
|
2805 // Want to be able to parse both short and long forms. |
|
2806 // Try count == 4 (cccc) first: |
|
2807 int32_t newStart = 0; |
|
2808 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, |
|
2809 fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, NULL, cal)) > 0) |
|
2810 return newStart; |
|
2811 else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, |
|
2812 fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, NULL, cal)) > 0) |
|
2813 return newStart; |
|
2814 else if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, |
|
2815 fSymbols->fStandaloneShorterWeekdays, fSymbols->fStandaloneShorterWeekdaysCount, NULL, cal)) > 0) |
|
2816 return newStart; |
|
2817 else if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) |
|
2818 return newStart; |
|
2819 // else we allowing parsing as number, below |
|
2820 } |
|
2821 break; |
|
2822 |
|
2823 case UDAT_AM_PM_FIELD: |
|
2824 return matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, NULL, cal); |
|
2825 |
|
2826 case UDAT_HOUR1_FIELD: |
|
2827 // [We computed 'value' above.] |
|
2828 if (value == cal.getLeastMaximum(UCAL_HOUR)+1) |
|
2829 value = 0; |
|
2830 |
|
2831 // fall through to set field |
|
2832 |
|
2833 case UDAT_HOUR0_FIELD: |
|
2834 cal.set(UCAL_HOUR, value); |
|
2835 return pos.getIndex(); |
|
2836 |
|
2837 case UDAT_QUARTER_FIELD: |
|
2838 if (gotNumber) // i.e., Q or QQ. |
|
2839 { |
|
2840 // Don't want to parse the month if it is a string |
|
2841 // while pattern uses numeric style: Q or QQ. |
|
2842 // [We computed 'value' above.] |
|
2843 cal.set(UCAL_MONTH, (value - 1) * 3); |
|
2844 return pos.getIndex(); |
|
2845 } else { |
|
2846 // count >= 3 // i.e., QQQ or QQQQ |
|
2847 // Want to be able to parse both short and long forms. |
|
2848 // Try count == 4 first: |
|
2849 int32_t newStart = 0; |
|
2850 |
|
2851 if ((newStart = matchQuarterString(text, start, UCAL_MONTH, |
|
2852 fSymbols->fQuarters, fSymbols->fQuartersCount, cal)) > 0) |
|
2853 return newStart; |
|
2854 else if ((newStart = matchQuarterString(text, start, UCAL_MONTH, |
|
2855 fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal)) > 0) |
|
2856 return newStart; |
|
2857 else if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) |
|
2858 return newStart; |
|
2859 // else we allowing parsing as number, below |
|
2860 } |
|
2861 break; |
|
2862 |
|
2863 case UDAT_STANDALONE_QUARTER_FIELD: |
|
2864 if (gotNumber) // i.e., q or qq. |
|
2865 { |
|
2866 // Don't want to parse the month if it is a string |
|
2867 // while pattern uses numeric style: q or q. |
|
2868 // [We computed 'value' above.] |
|
2869 cal.set(UCAL_MONTH, (value - 1) * 3); |
|
2870 return pos.getIndex(); |
|
2871 } else { |
|
2872 // count >= 3 // i.e., qqq or qqqq |
|
2873 // Want to be able to parse both short and long forms. |
|
2874 // Try count == 4 first: |
|
2875 int32_t newStart = 0; |
|
2876 |
|
2877 if ((newStart = matchQuarterString(text, start, UCAL_MONTH, |
|
2878 fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount, cal)) > 0) |
|
2879 return newStart; |
|
2880 else if ((newStart = matchQuarterString(text, start, UCAL_MONTH, |
|
2881 fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal)) > 0) |
|
2882 return newStart; |
|
2883 else if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) |
|
2884 return newStart; |
|
2885 // else we allowing parsing as number, below |
|
2886 } |
|
2887 break; |
|
2888 |
|
2889 case UDAT_TIMEZONE_FIELD: // 'z' |
|
2890 { |
|
2891 UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN; |
|
2892 UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_SPECIFIC_SHORT : UTZFMT_STYLE_SPECIFIC_LONG; |
|
2893 TimeZone *tz = tzFormat()->parse(style, text, pos, &tzTimeType); |
|
2894 if (tz != NULL) { |
|
2895 ((SimpleDateFormat*)this)->tztype = tzTimeType; |
|
2896 cal.adoptTimeZone(tz); |
|
2897 return pos.getIndex(); |
|
2898 } |
|
2899 } |
|
2900 break; |
|
2901 case UDAT_TIMEZONE_RFC_FIELD: // 'Z' |
|
2902 { |
|
2903 UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN; |
|
2904 UTimeZoneFormatStyle style = (count < 4) ? |
|
2905 UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL : ((count == 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL: UTZFMT_STYLE_LOCALIZED_GMT); |
|
2906 TimeZone *tz = tzFormat()->parse(style, text, pos, &tzTimeType); |
|
2907 if (tz != NULL) { |
|
2908 ((SimpleDateFormat*)this)->tztype = tzTimeType; |
|
2909 cal.adoptTimeZone(tz); |
|
2910 return pos.getIndex(); |
|
2911 } |
|
2912 return -start; |
|
2913 } |
|
2914 case UDAT_TIMEZONE_GENERIC_FIELD: // 'v' |
|
2915 { |
|
2916 UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN; |
|
2917 UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_GENERIC_SHORT : UTZFMT_STYLE_GENERIC_LONG; |
|
2918 TimeZone *tz = tzFormat()->parse(style, text, pos, &tzTimeType); |
|
2919 if (tz != NULL) { |
|
2920 ((SimpleDateFormat*)this)->tztype = tzTimeType; |
|
2921 cal.adoptTimeZone(tz); |
|
2922 return pos.getIndex(); |
|
2923 } |
|
2924 return -start; |
|
2925 } |
|
2926 case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V' |
|
2927 { |
|
2928 UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN; |
|
2929 UTimeZoneFormatStyle style; |
|
2930 switch (count) { |
|
2931 case 1: |
|
2932 style = UTZFMT_STYLE_ZONE_ID_SHORT; |
|
2933 break; |
|
2934 case 2: |
|
2935 style = UTZFMT_STYLE_ZONE_ID; |
|
2936 break; |
|
2937 case 3: |
|
2938 style = UTZFMT_STYLE_EXEMPLAR_LOCATION; |
|
2939 break; |
|
2940 default: |
|
2941 style = UTZFMT_STYLE_GENERIC_LOCATION; |
|
2942 break; |
|
2943 } |
|
2944 TimeZone *tz = tzFormat()->parse(style, text, pos, &tzTimeType); |
|
2945 if (tz != NULL) { |
|
2946 ((SimpleDateFormat*)this)->tztype = tzTimeType; |
|
2947 cal.adoptTimeZone(tz); |
|
2948 return pos.getIndex(); |
|
2949 } |
|
2950 return -start; |
|
2951 } |
|
2952 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O' |
|
2953 { |
|
2954 UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN; |
|
2955 UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT : UTZFMT_STYLE_LOCALIZED_GMT; |
|
2956 TimeZone *tz = tzFormat()->parse(style, text, pos, &tzTimeType); |
|
2957 if (tz != NULL) { |
|
2958 ((SimpleDateFormat*)this)->tztype = tzTimeType; |
|
2959 cal.adoptTimeZone(tz); |
|
2960 return pos.getIndex(); |
|
2961 } |
|
2962 return -start; |
|
2963 } |
|
2964 case UDAT_TIMEZONE_ISO_FIELD: // 'X' |
|
2965 { |
|
2966 UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN; |
|
2967 UTimeZoneFormatStyle style; |
|
2968 switch (count) { |
|
2969 case 1: |
|
2970 style = UTZFMT_STYLE_ISO_BASIC_SHORT; |
|
2971 break; |
|
2972 case 2: |
|
2973 style = UTZFMT_STYLE_ISO_BASIC_FIXED; |
|
2974 break; |
|
2975 case 3: |
|
2976 style = UTZFMT_STYLE_ISO_EXTENDED_FIXED; |
|
2977 break; |
|
2978 case 4: |
|
2979 style = UTZFMT_STYLE_ISO_BASIC_FULL; |
|
2980 break; |
|
2981 default: |
|
2982 style = UTZFMT_STYLE_ISO_EXTENDED_FULL; |
|
2983 break; |
|
2984 } |
|
2985 TimeZone *tz = tzFormat()->parse(style, text, pos, &tzTimeType); |
|
2986 if (tz != NULL) { |
|
2987 ((SimpleDateFormat*)this)->tztype = tzTimeType; |
|
2988 cal.adoptTimeZone(tz); |
|
2989 return pos.getIndex(); |
|
2990 } |
|
2991 return -start; |
|
2992 } |
|
2993 case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x' |
|
2994 { |
|
2995 UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN; |
|
2996 UTimeZoneFormatStyle style; |
|
2997 switch (count) { |
|
2998 case 1: |
|
2999 style = UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT; |
|
3000 break; |
|
3001 case 2: |
|
3002 style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED; |
|
3003 break; |
|
3004 case 3: |
|
3005 style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED; |
|
3006 break; |
|
3007 case 4: |
|
3008 style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL; |
|
3009 break; |
|
3010 default: |
|
3011 style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL; |
|
3012 break; |
|
3013 } |
|
3014 TimeZone *tz = tzFormat()->parse(style, text, pos, &tzTimeType); |
|
3015 if (tz != NULL) { |
|
3016 ((SimpleDateFormat*)this)->tztype = tzTimeType; |
|
3017 cal.adoptTimeZone(tz); |
|
3018 return pos.getIndex(); |
|
3019 } |
|
3020 return -start; |
|
3021 } |
|
3022 |
|
3023 default: |
|
3024 // Handle "generic" fields |
|
3025 // this is now handled below, outside the switch block |
|
3026 break; |
|
3027 } |
|
3028 // Handle "generic" fields: |
|
3029 // switch default case now handled here (outside switch block) to allow |
|
3030 // parsing of some string fields as digits for lenient case |
|
3031 |
|
3032 int32_t parseStart = pos.getIndex(); |
|
3033 const UnicodeString* src; |
|
3034 if (obeyCount) { |
|
3035 if ((start+count) > text.length()) { |
|
3036 return -start; |
|
3037 } |
|
3038 text.extractBetween(0, start + count, temp); |
|
3039 src = &temp; |
|
3040 } else { |
|
3041 src = &text; |
|
3042 } |
|
3043 parseInt(*src, number, pos, allowNegative,currentNumberFormat); |
|
3044 if (pos.getIndex() != parseStart) { |
|
3045 int32_t value = number.getLong(); |
|
3046 |
|
3047 // Don't need suffix processing here (as in number processing at the beginning of the function); |
|
3048 // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes. |
|
3049 |
|
3050 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) { |
|
3051 // Check the range of the value |
|
3052 int32_t bias = gFieldRangeBias[patternCharIndex]; |
|
3053 if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) { |
|
3054 return -start; |
|
3055 } |
|
3056 } |
|
3057 |
|
3058 // For the following, need to repeat some of the "if (gotNumber)" code above: |
|
3059 // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD, |
|
3060 // UDAT_[STANDALONE_]QUARTER_FIELD |
|
3061 switch (patternCharIndex) { |
|
3062 case UDAT_MONTH_FIELD: |
|
3063 // See notes under UDAT_MONTH_FIELD case above |
|
3064 if (!strcmp(cal.getType(),"hebrew")) { |
|
3065 HebrewCalendar *hc = (HebrewCalendar*)&cal; |
|
3066 if (cal.isSet(UCAL_YEAR)) { |
|
3067 UErrorCode status = U_ZERO_ERROR; |
|
3068 if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6) { |
|
3069 cal.set(UCAL_MONTH, value); |
|
3070 } else { |
|
3071 cal.set(UCAL_MONTH, value - 1); |
|
3072 } |
|
3073 } else { |
|
3074 saveHebrewMonth = value; |
|
3075 } |
|
3076 } else { |
|
3077 cal.set(UCAL_MONTH, value - 1); |
|
3078 } |
|
3079 break; |
|
3080 case UDAT_STANDALONE_MONTH_FIELD: |
|
3081 cal.set(UCAL_MONTH, value - 1); |
|
3082 break; |
|
3083 case UDAT_DOW_LOCAL_FIELD: |
|
3084 case UDAT_STANDALONE_DAY_FIELD: |
|
3085 cal.set(UCAL_DOW_LOCAL, value); |
|
3086 break; |
|
3087 case UDAT_QUARTER_FIELD: |
|
3088 case UDAT_STANDALONE_QUARTER_FIELD: |
|
3089 cal.set(UCAL_MONTH, (value - 1) * 3); |
|
3090 break; |
|
3091 default: |
|
3092 cal.set(field, value); |
|
3093 break; |
|
3094 } |
|
3095 return pos.getIndex(); |
|
3096 } |
|
3097 return -start; |
|
3098 } |
|
3099 |
|
3100 /** |
|
3101 * Parse an integer using fNumberFormat. This method is semantically |
|
3102 * const, but actually may modify fNumberFormat. |
|
3103 */ |
|
3104 void SimpleDateFormat::parseInt(const UnicodeString& text, |
|
3105 Formattable& number, |
|
3106 ParsePosition& pos, |
|
3107 UBool allowNegative, |
|
3108 NumberFormat *fmt) const { |
|
3109 parseInt(text, number, -1, pos, allowNegative,fmt); |
|
3110 } |
|
3111 |
|
3112 /** |
|
3113 * Parse an integer using fNumberFormat up to maxDigits. |
|
3114 */ |
|
3115 void SimpleDateFormat::parseInt(const UnicodeString& text, |
|
3116 Formattable& number, |
|
3117 int32_t maxDigits, |
|
3118 ParsePosition& pos, |
|
3119 UBool allowNegative, |
|
3120 NumberFormat *fmt) const { |
|
3121 UnicodeString oldPrefix; |
|
3122 DecimalFormat* df = NULL; |
|
3123 if (!allowNegative && (df = dynamic_cast<DecimalFormat*>(fmt)) != NULL) { |
|
3124 df->getNegativePrefix(oldPrefix); |
|
3125 df->setNegativePrefix(UnicodeString(TRUE, SUPPRESS_NEGATIVE_PREFIX, -1)); |
|
3126 } |
|
3127 int32_t oldPos = pos.getIndex(); |
|
3128 fmt->parse(text, number, pos); |
|
3129 if (df != NULL) { |
|
3130 df->setNegativePrefix(oldPrefix); |
|
3131 } |
|
3132 |
|
3133 if (maxDigits > 0) { |
|
3134 // adjust the result to fit into |
|
3135 // the maxDigits and move the position back |
|
3136 int32_t nDigits = pos.getIndex() - oldPos; |
|
3137 if (nDigits > maxDigits) { |
|
3138 int32_t val = number.getLong(); |
|
3139 nDigits -= maxDigits; |
|
3140 while (nDigits > 0) { |
|
3141 val /= 10; |
|
3142 nDigits--; |
|
3143 } |
|
3144 pos.setIndex(oldPos + maxDigits); |
|
3145 number.setLong(val); |
|
3146 } |
|
3147 } |
|
3148 } |
|
3149 |
|
3150 //---------------------------------------------------------------------- |
|
3151 |
|
3152 void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern, |
|
3153 UnicodeString& translatedPattern, |
|
3154 const UnicodeString& from, |
|
3155 const UnicodeString& to, |
|
3156 UErrorCode& status) |
|
3157 { |
|
3158 // run through the pattern and convert any pattern symbols from the version |
|
3159 // in "from" to the corresponding character ion "to". This code takes |
|
3160 // quoted strings into account (it doesn't try to translate them), and it signals |
|
3161 // an error if a particular "pattern character" doesn't appear in "from". |
|
3162 // Depending on the values of "from" and "to" this can convert from generic |
|
3163 // to localized patterns or localized to generic. |
|
3164 if (U_FAILURE(status)) |
|
3165 return; |
|
3166 |
|
3167 translatedPattern.remove(); |
|
3168 UBool inQuote = FALSE; |
|
3169 for (int32_t i = 0; i < originalPattern.length(); ++i) { |
|
3170 UChar c = originalPattern[i]; |
|
3171 if (inQuote) { |
|
3172 if (c == QUOTE) |
|
3173 inQuote = FALSE; |
|
3174 } |
|
3175 else { |
|
3176 if (c == QUOTE) |
|
3177 inQuote = TRUE; |
|
3178 else if ((c >= 0x0061 /*'a'*/ && c <= 0x007A) /*'z'*/ |
|
3179 || (c >= 0x0041 /*'A'*/ && c <= 0x005A /*'Z'*/)) { |
|
3180 int32_t ci = from.indexOf(c); |
|
3181 if (ci == -1) { |
|
3182 status = U_INVALID_FORMAT_ERROR; |
|
3183 return; |
|
3184 } |
|
3185 c = to[ci]; |
|
3186 } |
|
3187 } |
|
3188 translatedPattern += c; |
|
3189 } |
|
3190 if (inQuote) { |
|
3191 status = U_INVALID_FORMAT_ERROR; |
|
3192 return; |
|
3193 } |
|
3194 } |
|
3195 |
|
3196 //---------------------------------------------------------------------- |
|
3197 |
|
3198 UnicodeString& |
|
3199 SimpleDateFormat::toPattern(UnicodeString& result) const |
|
3200 { |
|
3201 result = fPattern; |
|
3202 return result; |
|
3203 } |
|
3204 |
|
3205 //---------------------------------------------------------------------- |
|
3206 |
|
3207 UnicodeString& |
|
3208 SimpleDateFormat::toLocalizedPattern(UnicodeString& result, |
|
3209 UErrorCode& status) const |
|
3210 { |
|
3211 translatePattern(fPattern, result, |
|
3212 UnicodeString(DateFormatSymbols::getPatternUChars()), |
|
3213 fSymbols->fLocalPatternChars, status); |
|
3214 return result; |
|
3215 } |
|
3216 |
|
3217 //---------------------------------------------------------------------- |
|
3218 |
|
3219 void |
|
3220 SimpleDateFormat::applyPattern(const UnicodeString& pattern) |
|
3221 { |
|
3222 fPattern = pattern; |
|
3223 } |
|
3224 |
|
3225 //---------------------------------------------------------------------- |
|
3226 |
|
3227 void |
|
3228 SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern, |
|
3229 UErrorCode &status) |
|
3230 { |
|
3231 translatePattern(pattern, fPattern, |
|
3232 fSymbols->fLocalPatternChars, |
|
3233 UnicodeString(DateFormatSymbols::getPatternUChars()), status); |
|
3234 } |
|
3235 |
|
3236 //---------------------------------------------------------------------- |
|
3237 |
|
3238 const DateFormatSymbols* |
|
3239 SimpleDateFormat::getDateFormatSymbols() const |
|
3240 { |
|
3241 return fSymbols; |
|
3242 } |
|
3243 |
|
3244 //---------------------------------------------------------------------- |
|
3245 |
|
3246 void |
|
3247 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols* newFormatSymbols) |
|
3248 { |
|
3249 delete fSymbols; |
|
3250 fSymbols = newFormatSymbols; |
|
3251 } |
|
3252 |
|
3253 //---------------------------------------------------------------------- |
|
3254 void |
|
3255 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols) |
|
3256 { |
|
3257 delete fSymbols; |
|
3258 fSymbols = new DateFormatSymbols(newFormatSymbols); |
|
3259 } |
|
3260 |
|
3261 //---------------------------------------------------------------------- |
|
3262 const TimeZoneFormat* |
|
3263 SimpleDateFormat::getTimeZoneFormat(void) const { |
|
3264 return (const TimeZoneFormat*)tzFormat(); |
|
3265 } |
|
3266 |
|
3267 //---------------------------------------------------------------------- |
|
3268 void |
|
3269 SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat* timeZoneFormatToAdopt) |
|
3270 { |
|
3271 delete fTimeZoneFormat; |
|
3272 fTimeZoneFormat = timeZoneFormatToAdopt; |
|
3273 } |
|
3274 |
|
3275 //---------------------------------------------------------------------- |
|
3276 void |
|
3277 SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat& newTimeZoneFormat) |
|
3278 { |
|
3279 delete fTimeZoneFormat; |
|
3280 fTimeZoneFormat = new TimeZoneFormat(newTimeZoneFormat); |
|
3281 } |
|
3282 |
|
3283 //---------------------------------------------------------------------- |
|
3284 |
|
3285 |
|
3286 void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt) |
|
3287 { |
|
3288 UErrorCode status = U_ZERO_ERROR; |
|
3289 DateFormat::adoptCalendar(calendarToAdopt); |
|
3290 delete fSymbols; |
|
3291 fSymbols=NULL; |
|
3292 initializeSymbols(fLocale, fCalendar, status); // we need new symbols |
|
3293 initializeDefaultCentury(); // we need a new century (possibly) |
|
3294 } |
|
3295 |
|
3296 |
|
3297 //---------------------------------------------------------------------- |
|
3298 |
|
3299 |
|
3300 void SimpleDateFormat::setContext(UDisplayContext value, UErrorCode& status) |
|
3301 { |
|
3302 if (U_FAILURE(status)) |
|
3303 return; |
|
3304 if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) { |
|
3305 fCapitalizationContext = value; |
|
3306 } else { |
|
3307 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
3308 } |
|
3309 } |
|
3310 |
|
3311 |
|
3312 //---------------------------------------------------------------------- |
|
3313 |
|
3314 |
|
3315 UDisplayContext SimpleDateFormat::getContext(UDisplayContextType type, UErrorCode& status) const |
|
3316 { |
|
3317 if (U_FAILURE(status)) |
|
3318 return (UDisplayContext)0; |
|
3319 if (type != UDISPCTX_TYPE_CAPITALIZATION) { |
|
3320 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
3321 return (UDisplayContext)0; |
|
3322 } |
|
3323 return fCapitalizationContext; |
|
3324 } |
|
3325 |
|
3326 |
|
3327 //---------------------------------------------------------------------- |
|
3328 |
|
3329 |
|
3330 UBool |
|
3331 SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field) const { |
|
3332 return isFieldUnitIgnored(fPattern, field); |
|
3333 } |
|
3334 |
|
3335 |
|
3336 UBool |
|
3337 SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern, |
|
3338 UCalendarDateFields field) { |
|
3339 int32_t fieldLevel = fgCalendarFieldToLevel[field]; |
|
3340 int32_t level; |
|
3341 UChar ch; |
|
3342 UBool inQuote = FALSE; |
|
3343 UChar prevCh = 0; |
|
3344 int32_t count = 0; |
|
3345 |
|
3346 for (int32_t i = 0; i < pattern.length(); ++i) { |
|
3347 ch = pattern[i]; |
|
3348 if (ch != prevCh && count > 0) { |
|
3349 level = fgPatternCharToLevel[prevCh - PATTERN_CHAR_BASE]; |
|
3350 // the larger the level, the smaller the field unit. |
|
3351 if ( fieldLevel <= level ) { |
|
3352 return FALSE; |
|
3353 } |
|
3354 count = 0; |
|
3355 } |
|
3356 if (ch == QUOTE) { |
|
3357 if ((i+1) < pattern.length() && pattern[i+1] == QUOTE) { |
|
3358 ++i; |
|
3359 } else { |
|
3360 inQuote = ! inQuote; |
|
3361 } |
|
3362 } |
|
3363 else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) |
|
3364 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { |
|
3365 prevCh = ch; |
|
3366 ++count; |
|
3367 } |
|
3368 } |
|
3369 if ( count > 0 ) { |
|
3370 // last item |
|
3371 level = fgPatternCharToLevel[prevCh - PATTERN_CHAR_BASE]; |
|
3372 if ( fieldLevel <= level ) { |
|
3373 return FALSE; |
|
3374 } |
|
3375 } |
|
3376 return TRUE; |
|
3377 } |
|
3378 |
|
3379 //---------------------------------------------------------------------- |
|
3380 |
|
3381 const Locale& |
|
3382 SimpleDateFormat::getSmpFmtLocale(void) const { |
|
3383 return fLocale; |
|
3384 } |
|
3385 |
|
3386 //---------------------------------------------------------------------- |
|
3387 |
|
3388 int32_t |
|
3389 SimpleDateFormat::checkIntSuffix(const UnicodeString& text, int32_t start, |
|
3390 int32_t patLoc, UBool isNegative) const { |
|
3391 // local variables |
|
3392 UnicodeString suf; |
|
3393 int32_t patternMatch; |
|
3394 int32_t textPreMatch; |
|
3395 int32_t textPostMatch; |
|
3396 |
|
3397 // check that we are still in range |
|
3398 if ( (start > text.length()) || |
|
3399 (start < 0) || |
|
3400 (patLoc < 0) || |
|
3401 (patLoc > fPattern.length())) { |
|
3402 // out of range, don't advance location in text |
|
3403 return start; |
|
3404 } |
|
3405 |
|
3406 // get the suffix |
|
3407 DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fNumberFormat); |
|
3408 if (decfmt != NULL) { |
|
3409 if (isNegative) { |
|
3410 suf = decfmt->getNegativeSuffix(suf); |
|
3411 } |
|
3412 else { |
|
3413 suf = decfmt->getPositiveSuffix(suf); |
|
3414 } |
|
3415 } |
|
3416 |
|
3417 // check for suffix |
|
3418 if (suf.length() <= 0) { |
|
3419 return start; |
|
3420 } |
|
3421 |
|
3422 // check suffix will be encountered in the pattern |
|
3423 patternMatch = compareSimpleAffix(suf,fPattern,patLoc); |
|
3424 |
|
3425 // check if a suffix will be encountered in the text |
|
3426 textPreMatch = compareSimpleAffix(suf,text,start); |
|
3427 |
|
3428 // check if a suffix was encountered in the text |
|
3429 textPostMatch = compareSimpleAffix(suf,text,start-suf.length()); |
|
3430 |
|
3431 // check for suffix match |
|
3432 if ((textPreMatch >= 0) && (patternMatch >= 0) && (textPreMatch == patternMatch)) { |
|
3433 return start; |
|
3434 } |
|
3435 else if ((textPostMatch >= 0) && (patternMatch >= 0) && (textPostMatch == patternMatch)) { |
|
3436 return start - suf.length(); |
|
3437 } |
|
3438 |
|
3439 // should not get here |
|
3440 return start; |
|
3441 } |
|
3442 |
|
3443 //---------------------------------------------------------------------- |
|
3444 |
|
3445 int32_t |
|
3446 SimpleDateFormat::compareSimpleAffix(const UnicodeString& affix, |
|
3447 const UnicodeString& input, |
|
3448 int32_t pos) const { |
|
3449 int32_t start = pos; |
|
3450 for (int32_t i=0; i<affix.length(); ) { |
|
3451 UChar32 c = affix.char32At(i); |
|
3452 int32_t len = U16_LENGTH(c); |
|
3453 if (PatternProps::isWhiteSpace(c)) { |
|
3454 // We may have a pattern like: \u200F \u0020 |
|
3455 // and input text like: \u200F \u0020 |
|
3456 // Note that U+200F and U+0020 are Pattern_White_Space but only |
|
3457 // U+0020 is UWhiteSpace. So we have to first do a direct |
|
3458 // match of the run of Pattern_White_Space in the pattern, |
|
3459 // then match any extra characters. |
|
3460 UBool literalMatch = FALSE; |
|
3461 while (pos < input.length() && |
|
3462 input.char32At(pos) == c) { |
|
3463 literalMatch = TRUE; |
|
3464 i += len; |
|
3465 pos += len; |
|
3466 if (i == affix.length()) { |
|
3467 break; |
|
3468 } |
|
3469 c = affix.char32At(i); |
|
3470 len = U16_LENGTH(c); |
|
3471 if (!PatternProps::isWhiteSpace(c)) { |
|
3472 break; |
|
3473 } |
|
3474 } |
|
3475 |
|
3476 // Advance over run in pattern |
|
3477 i = skipPatternWhiteSpace(affix, i); |
|
3478 |
|
3479 // Advance over run in input text |
|
3480 // Must see at least one white space char in input, |
|
3481 // unless we've already matched some characters literally. |
|
3482 int32_t s = pos; |
|
3483 pos = skipUWhiteSpace(input, pos); |
|
3484 if (pos == s && !literalMatch) { |
|
3485 return -1; |
|
3486 } |
|
3487 |
|
3488 // If we skip UWhiteSpace in the input text, we need to skip it in the pattern. |
|
3489 // Otherwise, the previous lines may have skipped over text (such as U+00A0) that |
|
3490 // is also in the affix. |
|
3491 i = skipUWhiteSpace(affix, i); |
|
3492 } else { |
|
3493 if (pos < input.length() && |
|
3494 input.char32At(pos) == c) { |
|
3495 i += len; |
|
3496 pos += len; |
|
3497 } else { |
|
3498 return -1; |
|
3499 } |
|
3500 } |
|
3501 } |
|
3502 return pos - start; |
|
3503 } |
|
3504 |
|
3505 //---------------------------------------------------------------------- |
|
3506 |
|
3507 int32_t |
|
3508 SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) const { |
|
3509 const UChar* s = text.getBuffer(); |
|
3510 return (int32_t)(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s); |
|
3511 } |
|
3512 |
|
3513 //---------------------------------------------------------------------- |
|
3514 |
|
3515 int32_t |
|
3516 SimpleDateFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) const { |
|
3517 while (pos < text.length()) { |
|
3518 UChar32 c = text.char32At(pos); |
|
3519 if (!u_isUWhiteSpace(c)) { |
|
3520 break; |
|
3521 } |
|
3522 pos += U16_LENGTH(c); |
|
3523 } |
|
3524 return pos; |
|
3525 } |
|
3526 |
|
3527 //---------------------------------------------------------------------- |
|
3528 |
|
3529 // Lazy TimeZoneFormat instantiation, semantically const. |
|
3530 TimeZoneFormat * |
|
3531 SimpleDateFormat::tzFormat() const { |
|
3532 if (fTimeZoneFormat == NULL) { |
|
3533 umtx_lock(&LOCK); |
|
3534 { |
|
3535 if (fTimeZoneFormat == NULL) { |
|
3536 UErrorCode status = U_ZERO_ERROR; |
|
3537 TimeZoneFormat *tzfmt = TimeZoneFormat::createInstance(fLocale, status); |
|
3538 if (U_FAILURE(status)) { |
|
3539 return NULL; |
|
3540 } |
|
3541 |
|
3542 const_cast<SimpleDateFormat *>(this)->fTimeZoneFormat = tzfmt; |
|
3543 } |
|
3544 } |
|
3545 umtx_unlock(&LOCK); |
|
3546 } |
|
3547 return fTimeZoneFormat; |
|
3548 } |
|
3549 |
|
3550 U_NAMESPACE_END |
|
3551 |
|
3552 #endif /* #if !UCONFIG_NO_FORMATTING */ |
|
3553 |
|
3554 //eof |