File: | root/firefox-clang/intl/icu/source/i18n/smpdtfmt.cpp |
Warning: | line 2032, column 13 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | // © 2016 and later: Unicode, Inc. and others. | |||
2 | // License & terms of use: http://www.unicode.org/copyright.html | |||
3 | /* | |||
4 | ******************************************************************************* | |||
5 | * Copyright (C) 1997-2016, International Business Machines Corporation and * | |||
6 | * others. All Rights Reserved. * | |||
7 | ******************************************************************************* | |||
8 | * | |||
9 | * File SMPDTFMT.CPP | |||
10 | * | |||
11 | * Modification History: | |||
12 | * | |||
13 | * Date Name Description | |||
14 | * 02/19/97 aliu Converted from java. | |||
15 | * 03/31/97 aliu Modified extensively to work with 50 locales. | |||
16 | * 04/01/97 aliu Added support for centuries. | |||
17 | * 07/09/97 helena Made ParsePosition into a class. | |||
18 | * 07/21/98 stephen Added initializeDefaultCentury. | |||
19 | * Removed getZoneIndex (added in DateFormatSymbols) | |||
20 | * Removed subParseLong | |||
21 | * Removed chk | |||
22 | * 02/22/99 stephen Removed character literals for EBCDIC safety | |||
23 | * 10/14/99 aliu Updated 2-digit year parsing so that only "00" thru | |||
24 | * "99" are recognized. {j28 4182066} | |||
25 | * 11/15/99 weiv Added support for week of year/day of week format | |||
26 | ******************************************************************************** | |||
27 | */ | |||
28 | ||||
29 | #define ZID_KEY_MAX128 128 | |||
30 | ||||
31 | #include "unicode/utypes.h" | |||
32 | ||||
33 | #if !UCONFIG_NO_FORMATTING0 | |||
34 | #include "unicode/smpdtfmt.h" | |||
35 | #include "unicode/dtfmtsym.h" | |||
36 | #include "unicode/ures.h" | |||
37 | #include "unicode/msgfmt.h" | |||
38 | #include "unicode/calendar.h" | |||
39 | #include "unicode/gregocal.h" | |||
40 | #include "unicode/timezone.h" | |||
41 | #include "unicode/decimfmt.h" | |||
42 | #include "unicode/dcfmtsym.h" | |||
43 | #include "unicode/uchar.h" | |||
44 | #include "unicode/uniset.h" | |||
45 | #include "unicode/ustring.h" | |||
46 | #include "unicode/basictz.h" | |||
47 | #include "unicode/simpleformatter.h" | |||
48 | #include "unicode/simplenumberformatter.h" | |||
49 | #include "unicode/simpletz.h" | |||
50 | #include "unicode/rbtz.h" | |||
51 | #include "unicode/tzfmt.h" | |||
52 | #include "unicode/ucasemap.h" | |||
53 | #include "unicode/utf16.h" | |||
54 | #include "unicode/vtzone.h" | |||
55 | #include "unicode/udisplaycontext.h" | |||
56 | #include "unicode/brkiter.h" | |||
57 | #include "unicode/rbnf.h" | |||
58 | #include "unicode/dtptngen.h" | |||
59 | #include "uresimp.h" | |||
60 | #include "olsontz.h" | |||
61 | #include "patternprops.h" | |||
62 | #include "fphdlimp.h" | |||
63 | #include "hebrwcal.h" | |||
64 | #include "cstring.h" | |||
65 | #include "uassert.h" | |||
66 | #include "cmemory.h" | |||
67 | #include "umutex.h" | |||
68 | #include "mutex.h" | |||
69 | #include <float.h> | |||
70 | #include "smpdtfst.h" | |||
71 | #include "sharednumberformat.h" | |||
72 | #include "ucasemap_imp.h" | |||
73 | #include "ustr_imp.h" | |||
74 | #include "charstr.h" | |||
75 | #include "uvector.h" | |||
76 | #include "cstr.h" | |||
77 | #include "dayperiodrules.h" | |||
78 | #include "tznames_impl.h" // ZONE_NAME_U16_MAX | |||
79 | #include "number_utypes.h" | |||
80 | #include "chnsecal.h" | |||
81 | #include "dangical.h" | |||
82 | #include "japancal.h" | |||
83 | #include <typeinfo> | |||
84 | ||||
85 | #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) | |||
86 | #include <stdio.h> | |||
87 | #endif | |||
88 | ||||
89 | // ***************************************************************************** | |||
90 | // class SimpleDateFormat | |||
91 | // ***************************************************************************** | |||
92 | ||||
93 | U_NAMESPACE_BEGINnamespace icu_77 { | |||
94 | ||||
95 | /** | |||
96 | * Last-resort string to use for "GMT" when constructing time zone strings. | |||
97 | */ | |||
98 | // For time zones that have no names, use strings GMT+minutes and | |||
99 | // GMT-minutes. For instance, in France the time zone is GMT+60. | |||
100 | // Also accepted are GMT+H:MM or GMT-H:MM. | |||
101 | // Currently not being used | |||
102 | //static const char16_t gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT" | |||
103 | //static const char16_t gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+" | |||
104 | //static const char16_t gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-" | |||
105 | //static const char16_t gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */ | |||
106 | //static const char16_t gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */ | |||
107 | //static const char16_t gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */ | |||
108 | //static const char16_t gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */ | |||
109 | //static const char16_t gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */ | |||
110 | //static const char16_t gUt[] = {0x0055, 0x0054, 0x0000}; // "UT" | |||
111 | //static const char16_t gUtc[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT" | |||
112 | ||||
113 | typedef enum GmtPatSize { | |||
114 | kGmtLen = 3, | |||
115 | kGmtPatLen = 6, | |||
116 | kNegHmsLen = 9, | |||
117 | kNegHmLen = 6, | |||
118 | kPosHmsLen = 9, | |||
119 | kPosHmLen = 6, | |||
120 | kUtLen = 2, | |||
121 | kUtcLen = 3 | |||
122 | } GmtPatSize; | |||
123 | ||||
124 | // Stuff needed for numbering system overrides | |||
125 | ||||
126 | typedef enum OvrStrType { | |||
127 | kOvrStrDate = 0, | |||
128 | kOvrStrTime = 1, | |||
129 | kOvrStrBoth = 2 | |||
130 | } OvrStrType; | |||
131 | ||||
132 | static const UDateFormatField kDateFields[] = { | |||
133 | UDAT_YEAR_FIELD, | |||
134 | UDAT_MONTH_FIELD, | |||
135 | UDAT_DATE_FIELD, | |||
136 | UDAT_DAY_OF_YEAR_FIELD, | |||
137 | UDAT_DAY_OF_WEEK_IN_MONTH_FIELD, | |||
138 | UDAT_WEEK_OF_YEAR_FIELD, | |||
139 | UDAT_WEEK_OF_MONTH_FIELD, | |||
140 | UDAT_YEAR_WOY_FIELD, | |||
141 | UDAT_EXTENDED_YEAR_FIELD, | |||
142 | UDAT_JULIAN_DAY_FIELD, | |||
143 | UDAT_STANDALONE_DAY_FIELD, | |||
144 | UDAT_STANDALONE_MONTH_FIELD, | |||
145 | UDAT_QUARTER_FIELD, | |||
146 | UDAT_STANDALONE_QUARTER_FIELD, | |||
147 | UDAT_YEAR_NAME_FIELD, | |||
148 | UDAT_RELATED_YEAR_FIELD }; | |||
149 | static const int8_t kDateFieldsCount = 16; | |||
150 | ||||
151 | static const UDateFormatField kTimeFields[] = { | |||
152 | UDAT_HOUR_OF_DAY1_FIELD, | |||
153 | UDAT_HOUR_OF_DAY0_FIELD, | |||
154 | UDAT_MINUTE_FIELD, | |||
155 | UDAT_SECOND_FIELD, | |||
156 | UDAT_FRACTIONAL_SECOND_FIELD, | |||
157 | UDAT_HOUR1_FIELD, | |||
158 | UDAT_HOUR0_FIELD, | |||
159 | UDAT_MILLISECONDS_IN_DAY_FIELD, | |||
160 | UDAT_TIMEZONE_RFC_FIELD, | |||
161 | UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD }; | |||
162 | static const int8_t kTimeFieldsCount = 10; | |||
163 | ||||
164 | ||||
165 | // This is a pattern-of-last-resort used when we can't load a usable pattern out | |||
166 | // of a resource. | |||
167 | static const char16_t gDefaultPattern[] = | |||
168 | { | |||
169 | 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0 | |||
170 | }; /* "yMMdd hh:mm a" */ | |||
171 | ||||
172 | // This prefix is designed to NEVER MATCH real text, in order to | |||
173 | // suppress the parsing of negative numbers. Adjust as needed (if | |||
174 | // this becomes valid Unicode). | |||
175 | static const char16_t SUPPRESS_NEGATIVE_PREFIX[] = {0xAB00, 0}; | |||
176 | ||||
177 | /** | |||
178 | * These are the tags we expect to see in normal resource bundle files associated | |||
179 | * with a locale. | |||
180 | */ | |||
181 | static const char16_t QUOTE = 0x27; // Single quote | |||
182 | ||||
183 | /* | |||
184 | * The field range check bias for each UDateFormatField. | |||
185 | * The bias is added to the minimum and maximum values | |||
186 | * before they are compared to the parsed number. | |||
187 | * For example, the calendar stores zero-based month numbers | |||
188 | * but the parsed month numbers start at 1, so the bias is 1. | |||
189 | * | |||
190 | * A value of -1 means that the value is not checked. | |||
191 | */ | |||
192 | static const int32_t gFieldRangeBias[] = { | |||
193 | -1, // 'G' - UDAT_ERA_FIELD | |||
194 | -1, // 'y' - UDAT_YEAR_FIELD | |||
195 | 1, // 'M' - UDAT_MONTH_FIELD | |||
196 | 0, // 'd' - UDAT_DATE_FIELD | |||
197 | -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD | |||
198 | -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD | |||
199 | 0, // 'm' - UDAT_MINUTE_FIELD | |||
200 | 0, // 's' - UDAT_SECOND_FIELD | |||
201 | -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?) | |||
202 | -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?) | |||
203 | -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?) | |||
204 | -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?) | |||
205 | -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?) | |||
206 | -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?) | |||
207 | -1, // 'a' - UDAT_AM_PM_FIELD | |||
208 | -1, // 'h' - UDAT_HOUR1_FIELD | |||
209 | -1, // 'K' - UDAT_HOUR0_FIELD | |||
210 | -1, // 'z' - UDAT_TIMEZONE_FIELD | |||
211 | -1, // 'Y' - UDAT_YEAR_WOY_FIELD | |||
212 | -1, // 'e' - UDAT_DOW_LOCAL_FIELD | |||
213 | -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD | |||
214 | -1, // 'g' - UDAT_JULIAN_DAY_FIELD | |||
215 | -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD | |||
216 | -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD | |||
217 | -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD | |||
218 | 0, // 'c' - UDAT_STANDALONE_DAY_FIELD | |||
219 | 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD | |||
220 | -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?) | |||
221 | -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD | |||
222 | -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD | |||
223 | -1, // 'U' - UDAT_YEAR_NAME_FIELD | |||
224 | -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD | |||
225 | -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD | |||
226 | -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD | |||
227 | -1, // 'r' - UDAT_RELATED_YEAR_FIELD | |||
228 | #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR0 | |||
229 | -1, // ':' - UDAT_TIME_SEPARATOR_FIELD | |||
230 | #else | |||
231 | -1, // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD | |||
232 | #endif | |||
233 | }; | |||
234 | ||||
235 | // When calendar uses hebr numbering (i.e. he@calendar=hebrew), | |||
236 | // offset the years within the current millennium down to 1-999 | |||
237 | static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR = 5000; | |||
238 | static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR = 6000; | |||
239 | ||||
240 | /** | |||
241 | * Maximum range for detecting daylight offset of a time zone when parsed time zone | |||
242 | * string indicates it's daylight saving time, but the detected time zone does not | |||
243 | * observe daylight saving time at the parsed date. | |||
244 | */ | |||
245 | static const double MAX_DAYLIGHT_DETECTION_RANGE = 30*365*24*60*60*1000.0; | |||
246 | ||||
247 | static UMutex LOCK; | |||
248 | ||||
249 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)UClassID SimpleDateFormat::getStaticClassID() { static char classID = 0; return (UClassID)&classID; } UClassID SimpleDateFormat ::getDynamicClassID() const { return SimpleDateFormat::getStaticClassID (); } | |||
250 | ||||
251 | SimpleDateFormat::NSOverride::~NSOverride() { | |||
252 | if (snf != nullptr) { | |||
253 | snf->removeRef(); | |||
254 | } | |||
255 | } | |||
256 | ||||
257 | ||||
258 | void SimpleDateFormat::NSOverride::free() { | |||
259 | NSOverride *cur = this; | |||
260 | while (cur) { | |||
261 | NSOverride *next_temp = cur->next; | |||
262 | delete cur; | |||
263 | cur = next_temp; | |||
264 | } | |||
265 | } | |||
266 | ||||
267 | // no matter what the locale's default number format looked like, we want | |||
268 | // to modify it so that it doesn't use thousands separators, doesn't always | |||
269 | // show the decimal point, and recognizes integers only when parsing | |||
270 | static void fixNumberFormatForDates(NumberFormat &nf) { | |||
271 | nf.setGroupingUsed(false); | |||
272 | DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(&nf); | |||
273 | if (decfmt != nullptr) { | |||
274 | decfmt->setDecimalSeparatorAlwaysShown(false); | |||
275 | } | |||
276 | nf.setParseIntegerOnly(true); | |||
277 | nf.setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00" | |||
278 | } | |||
279 | ||||
280 | static const SharedNumberFormat *createSharedNumberFormat( | |||
281 | NumberFormat *nfToAdopt) { | |||
282 | fixNumberFormatForDates(*nfToAdopt); | |||
283 | const SharedNumberFormat *result = new SharedNumberFormat(nfToAdopt); | |||
284 | if (result == nullptr) { | |||
285 | delete nfToAdopt; | |||
286 | } | |||
287 | return result; | |||
288 | } | |||
289 | ||||
290 | static const SharedNumberFormat *createSharedNumberFormat( | |||
291 | const Locale &loc, UErrorCode &status) { | |||
292 | NumberFormat *nf = NumberFormat::createInstance(loc, status); | |||
293 | if (U_FAILURE(status)) { | |||
294 | return nullptr; | |||
295 | } | |||
296 | const SharedNumberFormat *result = createSharedNumberFormat(nf); | |||
297 | if (result == nullptr) { | |||
298 | status = U_MEMORY_ALLOCATION_ERROR; | |||
299 | } | |||
300 | return result; | |||
301 | } | |||
302 | ||||
303 | static const SharedNumberFormat **allocSharedNumberFormatters() { | |||
304 | const SharedNumberFormat** result = static_cast<const SharedNumberFormat**>( | |||
305 | uprv_mallocuprv_malloc_77(UDAT_FIELD_COUNT * sizeof(const SharedNumberFormat*))); | |||
306 | if (result == nullptr) { | |||
307 | return nullptr; | |||
308 | } | |||
309 | for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) { | |||
310 | result[i] = nullptr; | |||
311 | } | |||
312 | return result; | |||
313 | } | |||
314 | ||||
315 | static void freeSharedNumberFormatters(const SharedNumberFormat ** list) { | |||
316 | for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) { | |||
317 | SharedObject::clearPtr(list[i]); | |||
318 | } | |||
319 | uprv_freeuprv_free_77(list); | |||
320 | } | |||
321 | ||||
322 | const NumberFormat *SimpleDateFormat::getNumberFormatByIndex( | |||
323 | UDateFormatField index) const { | |||
324 | if (fSharedNumberFormatters == nullptr || | |||
325 | fSharedNumberFormatters[index] == nullptr) { | |||
326 | return fNumberFormat; | |||
327 | } | |||
328 | return &(**fSharedNumberFormatters[index]); | |||
329 | } | |||
330 | ||||
331 | //---------------------------------------------------------------------- | |||
332 | ||||
333 | SimpleDateFormat::~SimpleDateFormat() | |||
334 | { | |||
335 | delete fSymbols; | |||
336 | if (fSharedNumberFormatters) { | |||
337 | freeSharedNumberFormatters(fSharedNumberFormatters); | |||
338 | } | |||
339 | delete fTimeZoneFormat; | |||
340 | delete fSimpleNumberFormatter; | |||
341 | ||||
342 | #if !UCONFIG_NO_BREAK_ITERATION1 | |||
343 | delete fCapitalizationBrkIter; | |||
344 | #endif | |||
345 | } | |||
346 | ||||
347 | //---------------------------------------------------------------------- | |||
348 | ||||
349 | SimpleDateFormat::SimpleDateFormat(UErrorCode& status) | |||
350 | : fLocale(Locale::getDefault()) | |||
351 | { | |||
352 | initializeBooleanAttributes(); | |||
353 | construct(kShort, static_cast<EStyle>(kShort + kDateOffset), fLocale, status); | |||
354 | initializeDefaultCentury(); | |||
355 | } | |||
356 | ||||
357 | //---------------------------------------------------------------------- | |||
358 | ||||
359 | SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, | |||
360 | UErrorCode &status) | |||
361 | : fPattern(pattern), | |||
362 | fLocale(Locale::getDefault()) | |||
363 | { | |||
364 | fDateOverride.setToBogus(); | |||
365 | fTimeOverride.setToBogus(); | |||
366 | initializeBooleanAttributes(); | |||
367 | initializeCalendar(nullptr,fLocale,status); | |||
368 | fSymbols = DateFormatSymbols::createForLocale(fLocale, status); | |||
369 | initialize(fLocale, status); | |||
370 | initializeDefaultCentury(); | |||
371 | ||||
372 | } | |||
373 | //---------------------------------------------------------------------- | |||
374 | ||||
375 | SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, | |||
376 | const UnicodeString& override, | |||
377 | UErrorCode &status) | |||
378 | : fPattern(pattern), | |||
379 | fLocale(Locale::getDefault()) | |||
380 | { | |||
381 | fDateOverride.setTo(override); | |||
382 | fTimeOverride.setToBogus(); | |||
383 | initializeBooleanAttributes(); | |||
384 | initializeCalendar(nullptr,fLocale,status); | |||
385 | fSymbols = DateFormatSymbols::createForLocale(fLocale, status); | |||
386 | initialize(fLocale, status); | |||
387 | initializeDefaultCentury(); | |||
388 | ||||
389 | processOverrideString(fLocale,override,kOvrStrBoth,status); | |||
390 | ||||
391 | } | |||
392 | ||||
393 | //---------------------------------------------------------------------- | |||
394 | ||||
395 | SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, | |||
396 | const Locale& locale, | |||
397 | UErrorCode& status) | |||
398 | : fPattern(pattern), | |||
399 | fLocale(locale) | |||
400 | { | |||
401 | ||||
402 | fDateOverride.setToBogus(); | |||
403 | fTimeOverride.setToBogus(); | |||
404 | initializeBooleanAttributes(); | |||
405 | ||||
406 | initializeCalendar(nullptr,fLocale,status); | |||
407 | fSymbols = DateFormatSymbols::createForLocale(fLocale, status); | |||
408 | initialize(fLocale, status); | |||
409 | initializeDefaultCentury(); | |||
410 | } | |||
411 | ||||
412 | //---------------------------------------------------------------------- | |||
413 | ||||
414 | SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, | |||
415 | const UnicodeString& override, | |||
416 | const Locale& locale, | |||
417 | UErrorCode& status) | |||
418 | : fPattern(pattern), | |||
419 | fLocale(locale) | |||
420 | { | |||
421 | ||||
422 | fDateOverride.setTo(override); | |||
423 | fTimeOverride.setToBogus(); | |||
424 | initializeBooleanAttributes(); | |||
425 | ||||
426 | initializeCalendar(nullptr,fLocale,status); | |||
427 | fSymbols = DateFormatSymbols::createForLocale(fLocale, status); | |||
428 | initialize(fLocale, status); | |||
429 | initializeDefaultCentury(); | |||
430 | ||||
431 | processOverrideString(locale,override,kOvrStrBoth,status); | |||
432 | ||||
433 | } | |||
434 | ||||
435 | //---------------------------------------------------------------------- | |||
436 | ||||
437 | SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, | |||
438 | DateFormatSymbols* symbolsToAdopt, | |||
439 | UErrorCode& status) | |||
440 | : fPattern(pattern), | |||
441 | fLocale(Locale::getDefault()), | |||
442 | fSymbols(symbolsToAdopt) | |||
443 | { | |||
444 | ||||
445 | fDateOverride.setToBogus(); | |||
446 | fTimeOverride.setToBogus(); | |||
447 | initializeBooleanAttributes(); | |||
448 | ||||
449 | initializeCalendar(nullptr,fLocale,status); | |||
450 | initialize(fLocale, status); | |||
451 | initializeDefaultCentury(); | |||
452 | } | |||
453 | ||||
454 | //---------------------------------------------------------------------- | |||
455 | ||||
456 | SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, | |||
457 | const DateFormatSymbols& symbols, | |||
458 | UErrorCode& status) | |||
459 | : fPattern(pattern), | |||
460 | fLocale(Locale::getDefault()), | |||
461 | fSymbols(new DateFormatSymbols(symbols)) | |||
462 | { | |||
463 | ||||
464 | fDateOverride.setToBogus(); | |||
465 | fTimeOverride.setToBogus(); | |||
466 | initializeBooleanAttributes(); | |||
467 | ||||
468 | initializeCalendar(nullptr, fLocale, status); | |||
469 | initialize(fLocale, status); | |||
470 | initializeDefaultCentury(); | |||
471 | } | |||
472 | ||||
473 | //---------------------------------------------------------------------- | |||
474 | ||||
475 | // Not for public consumption; used by DateFormat | |||
476 | SimpleDateFormat::SimpleDateFormat(EStyle timeStyle, | |||
477 | EStyle dateStyle, | |||
478 | const Locale& locale, | |||
479 | UErrorCode& status) | |||
480 | : fLocale(locale) | |||
481 | { | |||
482 | initializeBooleanAttributes(); | |||
483 | construct(timeStyle, dateStyle, fLocale, status); | |||
484 | if(U_SUCCESS(status)) { | |||
485 | initializeDefaultCentury(); | |||
486 | } | |||
487 | } | |||
488 | ||||
489 | //---------------------------------------------------------------------- | |||
490 | ||||
491 | /** | |||
492 | * Not for public consumption; used by DateFormat. This constructor | |||
493 | * never fails. If the resource data is not available, it uses the | |||
494 | * the last resort symbols. | |||
495 | */ | |||
496 | SimpleDateFormat::SimpleDateFormat(const Locale& locale, | |||
497 | UErrorCode& status) | |||
498 | : fPattern(gDefaultPattern), | |||
499 | fLocale(locale) | |||
500 | { | |||
501 | if (U_FAILURE(status)) return; | |||
502 | initializeBooleanAttributes(); | |||
503 | initializeCalendar(nullptr, fLocale, status); | |||
504 | fSymbols = DateFormatSymbols::createForLocale(fLocale, status); | |||
505 | if (U_FAILURE(status)) | |||
506 | { | |||
507 | status = U_ZERO_ERROR; | |||
508 | delete fSymbols; | |||
509 | // This constructor doesn't fail; it uses last resort data | |||
510 | fSymbols = new DateFormatSymbols(status); | |||
511 | /* test for nullptr */ | |||
512 | if (fSymbols == nullptr) { | |||
513 | status = U_MEMORY_ALLOCATION_ERROR; | |||
514 | return; | |||
515 | } | |||
516 | } | |||
517 | ||||
518 | fDateOverride.setToBogus(); | |||
519 | fTimeOverride.setToBogus(); | |||
520 | ||||
521 | initialize(fLocale, status); | |||
522 | if(U_SUCCESS(status)) { | |||
523 | initializeDefaultCentury(); | |||
524 | } | |||
525 | } | |||
526 | ||||
527 | //---------------------------------------------------------------------- | |||
528 | ||||
529 | SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other) | |||
530 | : DateFormat(other), | |||
531 | fLocale(other.fLocale) | |||
532 | { | |||
533 | initializeBooleanAttributes(); | |||
534 | *this = other; | |||
535 | } | |||
536 | ||||
537 | //---------------------------------------------------------------------- | |||
538 | ||||
539 | SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other) | |||
540 | { | |||
541 | if (this == &other) { | |||
542 | return *this; | |||
543 | } | |||
544 | ||||
545 | // fSimpleNumberFormatter references fNumberFormatter, delete it | |||
546 | // before we call the = operator which may invalidate fNumberFormatter | |||
547 | delete fSimpleNumberFormatter; | |||
548 | fSimpleNumberFormatter = nullptr; | |||
549 | ||||
550 | DateFormat::operator=(other); | |||
551 | fDateOverride = other.fDateOverride; | |||
552 | fTimeOverride = other.fTimeOverride; | |||
553 | ||||
554 | delete fSymbols; | |||
555 | fSymbols = nullptr; | |||
556 | ||||
557 | if (other.fSymbols) | |||
558 | fSymbols = new DateFormatSymbols(*other.fSymbols); | |||
559 | ||||
560 | fDefaultCenturyStart = other.fDefaultCenturyStart; | |||
561 | fDefaultCenturyStartYear = other.fDefaultCenturyStartYear; | |||
562 | fHaveDefaultCentury = other.fHaveDefaultCentury; | |||
563 | ||||
564 | fPattern = other.fPattern; | |||
565 | fHasMinute = other.fHasMinute; | |||
566 | fHasSecond = other.fHasSecond; | |||
567 | ||||
568 | fLocale = other.fLocale; | |||
569 | ||||
570 | // TimeZoneFormat can now be set independently via setter. | |||
571 | // If it is nullptr, it will be lazily initialized from locale. | |||
572 | delete fTimeZoneFormat; | |||
573 | fTimeZoneFormat = nullptr; | |||
574 | TimeZoneFormat *otherTZFormat; | |||
575 | { | |||
576 | // Synchronization is required here, when accessing other.fTimeZoneFormat, | |||
577 | // because another thread may be concurrently executing other.tzFormat(), | |||
578 | // a logically const function that lazily creates other.fTimeZoneFormat. | |||
579 | // | |||
580 | // Without synchronization, reordered memory writes could allow us | |||
581 | // to see a non-null fTimeZoneFormat before the object itself was | |||
582 | // fully initialized. In case of a race, it doesn't matter whether | |||
583 | // we see a null or a fully initialized other.fTimeZoneFormat, | |||
584 | // only that we avoid seeing a partially initialized object. | |||
585 | // | |||
586 | // Once initialized, no const function can modify fTimeZoneFormat, | |||
587 | // meaning that once we have safely grabbed the other.fTimeZoneFormat | |||
588 | // pointer, continued synchronization is not required to use it. | |||
589 | Mutex m(&LOCK); | |||
590 | otherTZFormat = other.fTimeZoneFormat; | |||
591 | } | |||
592 | if (otherTZFormat) { | |||
593 | fTimeZoneFormat = new TimeZoneFormat(*otherTZFormat); | |||
594 | } | |||
595 | ||||
596 | #if !UCONFIG_NO_BREAK_ITERATION1 | |||
597 | if (other.fCapitalizationBrkIter != nullptr) { | |||
598 | fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone(); | |||
599 | } | |||
600 | #endif | |||
601 | ||||
602 | if (fSharedNumberFormatters != nullptr) { | |||
603 | freeSharedNumberFormatters(fSharedNumberFormatters); | |||
604 | fSharedNumberFormatters = nullptr; | |||
605 | } | |||
606 | if (other.fSharedNumberFormatters != nullptr) { | |||
607 | fSharedNumberFormatters = allocSharedNumberFormatters(); | |||
608 | if (fSharedNumberFormatters) { | |||
609 | for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) { | |||
610 | SharedObject::copyPtr( | |||
611 | other.fSharedNumberFormatters[i], | |||
612 | fSharedNumberFormatters[i]); | |||
613 | } | |||
614 | } | |||
615 | } | |||
616 | ||||
617 | UErrorCode localStatus = U_ZERO_ERROR; | |||
618 | // SimpleNumberFormatter does not have a copy constructor. Furthermore, | |||
619 | // it references data from an internal field, fNumberFormatter, | |||
620 | // so we must rematerialize that reference after copying over the number formatter. | |||
621 | initSimpleNumberFormatter(localStatus); | |||
622 | return *this; | |||
623 | } | |||
624 | ||||
625 | //---------------------------------------------------------------------- | |||
626 | ||||
627 | SimpleDateFormat* | |||
628 | SimpleDateFormat::clone() const | |||
629 | { | |||
630 | return new SimpleDateFormat(*this); | |||
631 | } | |||
632 | ||||
633 | //---------------------------------------------------------------------- | |||
634 | ||||
635 | bool | |||
636 | SimpleDateFormat::operator==(const Format& other) const | |||
637 | { | |||
638 | if (DateFormat::operator==(other)) { | |||
639 | // The DateFormat::operator== check for fCapitalizationContext equality above | |||
640 | // is sufficient to check equality of all derived context-related data. | |||
641 | // DateFormat::operator== guarantees following cast is safe | |||
642 | SimpleDateFormat* that = (SimpleDateFormat*)&other; | |||
643 | return (fPattern == that->fPattern && | |||
644 | fSymbols != nullptr && // Check for pathological object | |||
645 | that->fSymbols != nullptr && // Check for pathological object | |||
646 | *fSymbols == *that->fSymbols && | |||
647 | fHaveDefaultCentury == that->fHaveDefaultCentury && | |||
648 | fDefaultCenturyStart == that->fDefaultCenturyStart); | |||
649 | } | |||
650 | return false; | |||
651 | } | |||
652 | ||||
653 | //---------------------------------------------------------------------- | |||
654 | static const char16_t* timeSkeletons[4] = { | |||
655 | u"jmmsszzzz", // kFull | |||
656 | u"jmmssz", // kLong | |||
657 | u"jmmss", // kMedium | |||
658 | u"jmm", // kShort | |||
659 | }; | |||
660 | ||||
661 | void SimpleDateFormat::construct(EStyle timeStyle, | |||
662 | EStyle dateStyle, | |||
663 | const Locale& locale, | |||
664 | UErrorCode& status) | |||
665 | { | |||
666 | // called by several constructors to load pattern data from the resources | |||
667 | if (U_FAILURE(status)) return; | |||
668 | ||||
669 | // We will need the calendar to know what type of symbols to load. | |||
670 | initializeCalendar(nullptr, locale, status); | |||
671 | if (U_FAILURE(status)) return; | |||
672 | ||||
673 | // Load date time patterns directly from resources. | |||
674 | const char* cType = fCalendar ? fCalendar->getType() : nullptr; | |||
675 | LocalUResourceBundlePointer bundle(ures_openures_open_77(nullptr, locale.getBaseName(), &status)); | |||
676 | if (U_FAILURE(status)) return; | |||
677 | ||||
678 | UBool cTypeIsGregorian = true; | |||
679 | LocalUResourceBundlePointer dateTimePatterns; | |||
680 | if (cType != nullptr && uprv_strcmp(cType, "gregorian"):: strcmp(cType, "gregorian") != 0) { | |||
681 | CharString resourcePath("calendar/", status); | |||
682 | resourcePath.append(cType, status).append("/DateTimePatterns", status); | |||
683 | dateTimePatterns.adoptInstead( | |||
684 | ures_getByKeyWithFallbackures_getByKeyWithFallback_77(bundle.getAlias(), resourcePath.data(), | |||
685 | (UResourceBundle*)nullptr, &status)); | |||
686 | cTypeIsGregorian = false; | |||
687 | } | |||
688 | ||||
689 | // Check for "gregorian" fallback. | |||
690 | if (cTypeIsGregorian || status == U_MISSING_RESOURCE_ERROR) { | |||
691 | status = U_ZERO_ERROR; | |||
692 | dateTimePatterns.adoptInstead( | |||
693 | ures_getByKeyWithFallbackures_getByKeyWithFallback_77(bundle.getAlias(), | |||
694 | "calendar/gregorian/DateTimePatterns", | |||
695 | (UResourceBundle*)nullptr, &status)); | |||
696 | } | |||
697 | if (U_FAILURE(status)) return; | |||
698 | ||||
699 | LocalUResourceBundlePointer currentBundle; | |||
700 | ||||
701 | if (ures_getSizeures_getSize_77(dateTimePatterns.getAlias()) <= kDateTime) | |||
702 | { | |||
703 | status = U_INVALID_FORMAT_ERROR; | |||
704 | return; | |||
705 | } | |||
706 | ||||
707 | setLocaleIDs(ures_getLocaleByTypeures_getLocaleByType_77(dateTimePatterns.getAlias(), ULOC_VALID_LOCALE, &status), | |||
708 | ures_getLocaleByTypeures_getLocaleByType_77(dateTimePatterns.getAlias(), ULOC_ACTUAL_LOCALE, &status)); | |||
709 | ||||
710 | // create a symbols object from the locale | |||
711 | fSymbols = DateFormatSymbols::createForLocale(locale, status); | |||
712 | if (U_FAILURE(status)) return; | |||
713 | /* test for nullptr */ | |||
714 | if (fSymbols == nullptr) { | |||
715 | status = U_MEMORY_ALLOCATION_ERROR; | |||
716 | return; | |||
717 | } | |||
718 | ||||
719 | const char16_t *resStr,*ovrStr; | |||
720 | int32_t resStrLen,ovrStrLen = 0; | |||
721 | fDateOverride.setToBogus(); | |||
722 | fTimeOverride.setToBogus(); | |||
723 | ||||
724 | UnicodeString timePattern; | |||
725 | if (timeStyle >= kFull && timeStyle <= kShort) { | |||
726 | bool hasRgOrHcSubtag = false; | |||
727 | // also use DTPG if the locale has the "rg" or "hc" ("hours") subtag-- even if the overriding region | |||
728 | // or hour cycle is the same as the one we get by default, we go through the DateTimePatternGenerator | |||
729 | UErrorCode dummyErr1 = U_ZERO_ERROR, dummyErr2 = U_ZERO_ERROR; | |||
730 | if (locale.getKeywordValue("rg", nullptr, 0, dummyErr1) > 0 || locale.getKeywordValue("hours", nullptr, 0, dummyErr2) > 0) { | |||
731 | hasRgOrHcSubtag = true; | |||
732 | } | |||
733 | ||||
734 | const char* baseLocID = locale.getBaseName(); | |||
735 | if (baseLocID != nullptr && uprv_strcmp(baseLocID,"und"):: strcmp(baseLocID, "und")!=0) { | |||
736 | UErrorCode useStatus = U_ZERO_ERROR; | |||
737 | Locale baseLoc(baseLocID); | |||
738 | Locale validLoc(getLocale(ULOC_VALID_LOCALE, useStatus)); | |||
739 | if (hasRgOrHcSubtag || (U_SUCCESS(useStatus) && validLoc!=baseLoc)) { | |||
740 | bool useDTPG = hasRgOrHcSubtag; | |||
741 | const char* baseReg = baseLoc.getCountry(); // empty string if no region | |||
742 | if ((baseReg != nullptr && baseReg[0] != 0 && | |||
743 | uprv_strncmp(baseReg,validLoc.getCountry(),ULOC_COUNTRY_CAPACITY):: strncmp(baseReg, validLoc.getCountry(), 4)!=0) | |||
744 | || uprv_strncmp(baseLoc.getLanguage(),validLoc.getLanguage(),ULOC_LANG_CAPACITY):: strncmp(baseLoc.getLanguage(), validLoc.getLanguage(), 12)!=0) { | |||
745 | // use DTPG if | |||
746 | // * baseLoc has a region and validLoc does not have the same one (or has none), OR | |||
747 | // * validLoc has a different language code than baseLoc | |||
748 | // * the original locale has the rg or hc subtag | |||
749 | useDTPG = true; | |||
750 | } | |||
751 | if (useDTPG) { | |||
752 | // The standard time formats may have the wrong time cycle, because: | |||
753 | // the valid locale differs in important ways (region, language) from | |||
754 | // the base locale. | |||
755 | // We could *also* check whether they do actually have a mismatch with | |||
756 | // the time cycle preferences for the region, but that is a lot more | |||
757 | // work for little or no additional benefit, since just going ahead | |||
758 | // and always synthesizing the time format as per the following should | |||
759 | // create a locale-appropriate pattern with cycle that matches the | |||
760 | // region preferences anyway. | |||
761 | LocalPointer<DateTimePatternGenerator> dtpg(DateTimePatternGenerator::createInstanceNoStdPat(locale, useStatus)); | |||
762 | if (U_SUCCESS(useStatus)) { | |||
763 | UnicodeString timeSkeleton(true, timeSkeletons[timeStyle], -1); | |||
764 | timePattern = dtpg->getBestPattern(timeSkeleton, useStatus); | |||
765 | } | |||
766 | } | |||
767 | } | |||
768 | } | |||
769 | } | |||
770 | ||||
771 | // if the pattern should include both date and time information, use the date/time | |||
772 | // pattern string as a guide to tell use how to glue together the appropriate date | |||
773 | // and time pattern strings. | |||
774 | if ((timeStyle != kNone) && (dateStyle != kNone)) | |||
775 | { | |||
776 | UnicodeString tempus1(timePattern); | |||
777 | if (tempus1.length() == 0) { | |||
778 | currentBundle.adoptInstead( | |||
779 | ures_getByIndexures_getByIndex_77(dateTimePatterns.getAlias(), static_cast<int32_t>(timeStyle), nullptr, &status)); | |||
780 | if (U_FAILURE(status)) { | |||
781 | status = U_INVALID_FORMAT_ERROR; | |||
782 | return; | |||
783 | } | |||
784 | switch (ures_getTypeures_getType_77(currentBundle.getAlias())) { | |||
785 | case URES_STRING: { | |||
786 | resStr = ures_getStringures_getString_77(currentBundle.getAlias(), &resStrLen, &status); | |||
787 | break; | |||
788 | } | |||
789 | case URES_ARRAY: { | |||
790 | resStr = ures_getStringByIndexures_getStringByIndex_77(currentBundle.getAlias(), 0, &resStrLen, &status); | |||
791 | ovrStr = ures_getStringByIndexures_getStringByIndex_77(currentBundle.getAlias(), 1, &ovrStrLen, &status); | |||
792 | fTimeOverride.setTo(true, ovrStr, ovrStrLen); | |||
793 | break; | |||
794 | } | |||
795 | default: { | |||
796 | status = U_INVALID_FORMAT_ERROR; | |||
797 | return; | |||
798 | } | |||
799 | } | |||
800 | ||||
801 | tempus1.setTo(true, resStr, resStrLen); | |||
802 | } | |||
803 | ||||
804 | currentBundle.adoptInstead( | |||
805 | ures_getByIndexures_getByIndex_77(dateTimePatterns.getAlias(), static_cast<int32_t>(dateStyle), nullptr, &status)); | |||
806 | if (U_FAILURE(status)) { | |||
807 | status = U_INVALID_FORMAT_ERROR; | |||
808 | return; | |||
809 | } | |||
810 | switch (ures_getTypeures_getType_77(currentBundle.getAlias())) { | |||
811 | case URES_STRING: { | |||
812 | resStr = ures_getStringures_getString_77(currentBundle.getAlias(), &resStrLen, &status); | |||
813 | break; | |||
814 | } | |||
815 | case URES_ARRAY: { | |||
816 | resStr = ures_getStringByIndexures_getStringByIndex_77(currentBundle.getAlias(), 0, &resStrLen, &status); | |||
817 | ovrStr = ures_getStringByIndexures_getStringByIndex_77(currentBundle.getAlias(), 1, &ovrStrLen, &status); | |||
818 | fDateOverride.setTo(true, ovrStr, ovrStrLen); | |||
819 | break; | |||
820 | } | |||
821 | default: { | |||
822 | status = U_INVALID_FORMAT_ERROR; | |||
823 | return; | |||
824 | } | |||
825 | } | |||
826 | ||||
827 | UnicodeString tempus2(true, resStr, resStrLen); | |||
828 | ||||
829 | // Currently, for compatibility with pre-CLDR-42 data, we default to the "atTime" | |||
830 | // combining patterns. Depending on guidance in CLDR 42 spec and on DisplayOptions, | |||
831 | // we may change this. | |||
832 | LocalUResourceBundlePointer dateAtTimePatterns; | |||
833 | if (!cTypeIsGregorian) { | |||
834 | CharString resourcePath("calendar/", status); | |||
835 | resourcePath.append(cType, status).append("/DateTimePatterns%atTime", status); | |||
836 | dateAtTimePatterns.adoptInstead( | |||
837 | ures_getByKeyWithFallbackures_getByKeyWithFallback_77(bundle.getAlias(), resourcePath.data(), | |||
838 | nullptr, &status)); | |||
839 | } | |||
840 | if (cTypeIsGregorian || status == U_MISSING_RESOURCE_ERROR) { | |||
841 | status = U_ZERO_ERROR; | |||
842 | dateAtTimePatterns.adoptInstead( | |||
843 | ures_getByKeyWithFallbackures_getByKeyWithFallback_77(bundle.getAlias(), | |||
844 | "calendar/gregorian/DateTimePatterns%atTime", | |||
845 | nullptr, &status)); | |||
846 | } | |||
847 | if (U_SUCCESS(status) && ures_getSizeures_getSize_77(dateAtTimePatterns.getAlias()) >= 4) { | |||
848 | resStr = ures_getStringByIndexures_getStringByIndex_77(dateAtTimePatterns.getAlias(), dateStyle - kDateOffset, &resStrLen, &status); | |||
849 | } else { | |||
850 | status = U_ZERO_ERROR; | |||
851 | int32_t glueIndex = kDateTime; | |||
852 | int32_t patternsSize = ures_getSizeures_getSize_77(dateTimePatterns.getAlias()); | |||
853 | if (patternsSize >= (kDateTimeOffset + kShort + 1)) { | |||
854 | // Get proper date time format | |||
855 | glueIndex = static_cast<int32_t>(kDateTimeOffset + (dateStyle - kDateOffset)); | |||
856 | } | |||
857 | ||||
858 | resStr = ures_getStringByIndexures_getStringByIndex_77(dateTimePatterns.getAlias(), glueIndex, &resStrLen, &status); | |||
859 | } | |||
860 | SimpleFormatter(UnicodeString(true, resStr, resStrLen), 2, 2, status). | |||
861 | format(tempus1, tempus2, fPattern, status); | |||
862 | } | |||
863 | // if the pattern includes just time data or just date date, load the appropriate | |||
864 | // pattern string from the resources | |||
865 | // setTo() - see DateFormatSymbols::assignArray comments | |||
866 | else if (timeStyle != kNone) { | |||
867 | fPattern.setTo(timePattern); | |||
868 | if (fPattern.length() == 0) { | |||
869 | currentBundle.adoptInstead( | |||
870 | ures_getByIndexures_getByIndex_77(dateTimePatterns.getAlias(), static_cast<int32_t>(timeStyle), nullptr, &status)); | |||
871 | if (U_FAILURE(status)) { | |||
872 | status = U_INVALID_FORMAT_ERROR; | |||
873 | return; | |||
874 | } | |||
875 | switch (ures_getTypeures_getType_77(currentBundle.getAlias())) { | |||
876 | case URES_STRING: { | |||
877 | resStr = ures_getStringures_getString_77(currentBundle.getAlias(), &resStrLen, &status); | |||
878 | break; | |||
879 | } | |||
880 | case URES_ARRAY: { | |||
881 | resStr = ures_getStringByIndexures_getStringByIndex_77(currentBundle.getAlias(), 0, &resStrLen, &status); | |||
882 | ovrStr = ures_getStringByIndexures_getStringByIndex_77(currentBundle.getAlias(), 1, &ovrStrLen, &status); | |||
883 | fDateOverride.setTo(true, ovrStr, ovrStrLen); | |||
884 | break; | |||
885 | } | |||
886 | default: { | |||
887 | status = U_INVALID_FORMAT_ERROR; | |||
888 | return; | |||
889 | } | |||
890 | } | |||
891 | fPattern.setTo(true, resStr, resStrLen); | |||
892 | } | |||
893 | } | |||
894 | else if (dateStyle != kNone) { | |||
895 | currentBundle.adoptInstead( | |||
896 | ures_getByIndexures_getByIndex_77(dateTimePatterns.getAlias(), static_cast<int32_t>(dateStyle), nullptr, &status)); | |||
897 | if (U_FAILURE(status)) { | |||
898 | status = U_INVALID_FORMAT_ERROR; | |||
899 | return; | |||
900 | } | |||
901 | switch (ures_getTypeures_getType_77(currentBundle.getAlias())) { | |||
902 | case URES_STRING: { | |||
903 | resStr = ures_getStringures_getString_77(currentBundle.getAlias(), &resStrLen, &status); | |||
904 | break; | |||
905 | } | |||
906 | case URES_ARRAY: { | |||
907 | resStr = ures_getStringByIndexures_getStringByIndex_77(currentBundle.getAlias(), 0, &resStrLen, &status); | |||
908 | ovrStr = ures_getStringByIndexures_getStringByIndex_77(currentBundle.getAlias(), 1, &ovrStrLen, &status); | |||
909 | fDateOverride.setTo(true, ovrStr, ovrStrLen); | |||
910 | break; | |||
911 | } | |||
912 | default: { | |||
913 | status = U_INVALID_FORMAT_ERROR; | |||
914 | return; | |||
915 | } | |||
916 | } | |||
917 | fPattern.setTo(true, resStr, resStrLen); | |||
918 | } | |||
919 | ||||
920 | // and if it includes _neither_, that's an error | |||
921 | else | |||
922 | status = U_INVALID_FORMAT_ERROR; | |||
923 | ||||
924 | // finally, finish initializing by creating a Calendar and a NumberFormat | |||
925 | initialize(locale, status); | |||
926 | } | |||
927 | ||||
928 | //---------------------------------------------------------------------- | |||
929 | ||||
930 | Calendar* | |||
931 | SimpleDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status) | |||
932 | { | |||
933 | if(!U_FAILURE(status)) { | |||
934 | fCalendar = Calendar::createInstance( | |||
935 | adoptZone ? adoptZone : TimeZone::forLocaleOrDefault(locale), locale, status); | |||
936 | } | |||
937 | return fCalendar; | |||
938 | } | |||
939 | ||||
940 | void | |||
941 | SimpleDateFormat::initialize(const Locale& locale, | |||
942 | UErrorCode& status) | |||
943 | { | |||
944 | if (U_FAILURE(status)) return; | |||
945 | ||||
946 | parsePattern(); // Need this before initNumberFormatters(), to set fHasHanYearChar | |||
947 | ||||
948 | // Simple-minded hack to force Gannen year numbering for ja@calendar=japanese | |||
949 | // if format is non-numeric (includes 年) and fDateOverride is not already specified. | |||
950 | // Now this does get updated if applyPattern subsequently changes the pattern type. | |||
951 | if (fDateOverride.isBogus() && fHasHanYearChar && | |||
952 | fCalendar != nullptr && | |||
953 | typeid(*fCalendar) == typeid(JapaneseCalendar) && | |||
954 | uprv_strcmp(fLocale.getLanguage(),"ja"):: strcmp(fLocale.getLanguage(), "ja") == 0) { | |||
955 | fDateOverride.setTo(u"y=jpanyear", -1); | |||
956 | } | |||
957 | ||||
958 | // We don't need to check that the row count is >= 1, since all 2d arrays have at | |||
959 | // least one row | |||
960 | fNumberFormat = NumberFormat::createInstance(locale, status); | |||
961 | if (fNumberFormat != nullptr && U_SUCCESS(status)) | |||
962 | { | |||
963 | fixNumberFormatForDates(*fNumberFormat); | |||
964 | //fNumberFormat->setLenient(true); // Java uses a custom DateNumberFormat to format/parse | |||
965 | ||||
966 | initNumberFormatters(locale, status); | |||
967 | initSimpleNumberFormatter(status); | |||
968 | ||||
969 | } | |||
970 | else if (U_SUCCESS(status)) | |||
971 | { | |||
972 | status = U_MISSING_RESOURCE_ERROR; | |||
973 | } | |||
974 | } | |||
975 | ||||
976 | /* Initialize the fields we use to disambiguate ambiguous years. Separate | |||
977 | * so we can call it from readObject(). | |||
978 | */ | |||
979 | void SimpleDateFormat::initializeDefaultCentury() | |||
980 | { | |||
981 | if(fCalendar) { | |||
982 | fHaveDefaultCentury = fCalendar->haveDefaultCentury(); | |||
983 | if(fHaveDefaultCentury) { | |||
984 | fDefaultCenturyStart = fCalendar->defaultCenturyStart(); | |||
985 | fDefaultCenturyStartYear = fCalendar->defaultCenturyStartYear(); | |||
986 | } else { | |||
987 | fDefaultCenturyStart = DBL_MIN2.2250738585072014e-308; | |||
988 | fDefaultCenturyStartYear = -1; | |||
989 | } | |||
990 | } | |||
991 | } | |||
992 | ||||
993 | /* | |||
994 | * Initialize the boolean attributes. Separate so we can call it from all constructors. | |||
995 | */ | |||
996 | void SimpleDateFormat::initializeBooleanAttributes() | |||
997 | { | |||
998 | UErrorCode status = U_ZERO_ERROR; | |||
999 | ||||
1000 | setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status); | |||
1001 | setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); | |||
1002 | setBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, true, status); | |||
1003 | setBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, true, status); | |||
1004 | } | |||
1005 | ||||
1006 | /* Define one-century window into which to disambiguate dates using | |||
1007 | * two-digit years. Make public in JDK 1.2. | |||
1008 | */ | |||
1009 | void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status) | |||
1010 | { | |||
1011 | if(U_FAILURE(status)) { | |||
1012 | return; | |||
1013 | } | |||
1014 | if(!fCalendar) { | |||
1015 | status = U_ILLEGAL_ARGUMENT_ERROR; | |||
1016 | return; | |||
1017 | } | |||
1018 | ||||
1019 | fCalendar->setTime(startDate, status); | |||
1020 | if(U_SUCCESS(status)) { | |||
1021 | fHaveDefaultCentury = true; | |||
1022 | fDefaultCenturyStart = startDate; | |||
1023 | fDefaultCenturyStartYear = fCalendar->get(UCAL_YEAR, status); | |||
1024 | } | |||
1025 | } | |||
1026 | ||||
1027 | //---------------------------------------------------------------------- | |||
1028 | ||||
1029 | UnicodeString& | |||
1030 | SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const | |||
1031 | { | |||
1032 | UErrorCode status = U_ZERO_ERROR; | |||
1033 | FieldPositionOnlyHandler handler(pos); | |||
1034 | return _format(cal, appendTo, handler, status); | |||
1035 | } | |||
1036 | ||||
1037 | //---------------------------------------------------------------------- | |||
1038 | ||||
1039 | UnicodeString& | |||
1040 | SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, | |||
1041 | FieldPositionIterator* posIter, UErrorCode& status) const | |||
1042 | { | |||
1043 | FieldPositionIteratorHandler handler(posIter, status); | |||
1044 | return _format(cal, appendTo, handler, status); | |||
1045 | } | |||
1046 | ||||
1047 | //---------------------------------------------------------------------- | |||
1048 | ||||
1049 | UnicodeString& | |||
1050 | SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, | |||
1051 | FieldPositionHandler& handler, UErrorCode& status) const | |||
1052 | { | |||
1053 | if ( U_FAILURE(status) ) { | |||
1054 | return appendTo; | |||
1055 | } | |||
1056 | Calendar* workCal = &cal; | |||
1057 | Calendar* calClone = nullptr; | |||
1058 | if (&cal != fCalendar && typeid(cal) != typeid(*fCalendar)) { | |||
1059 | // Different calendar type | |||
1060 | // We use the time and time zone from the input calendar, but | |||
1061 | // do not use the input calendar for field calculation. | |||
1062 | calClone = fCalendar->clone(); | |||
1063 | if (calClone != nullptr) { | |||
1064 | UDate t = cal.getTime(status); | |||
1065 | calClone->setTime(t, status); | |||
1066 | calClone->setTimeZone(cal.getTimeZone()); | |||
1067 | workCal = calClone; | |||
1068 | } else { | |||
1069 | status = U_MEMORY_ALLOCATION_ERROR; | |||
1070 | return appendTo; | |||
1071 | } | |||
1072 | } | |||
1073 | ||||
1074 | UBool inQuote = false; | |||
1075 | char16_t prevCh = 0; | |||
1076 | int32_t count = 0; | |||
1077 | int32_t fieldNum = 0; | |||
1078 | UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); | |||
1079 | ||||
1080 | // loop through the pattern string character by character | |||
1081 | int32_t patternLength = fPattern.length(); | |||
1082 | for (int32_t i = 0; i < patternLength && U_SUCCESS(status); ++i) { | |||
1083 | char16_t ch = fPattern[i]; | |||
1084 | ||||
1085 | // Use subFormat() to format a repeated pattern character | |||
1086 | // when a different pattern or non-pattern character is seen | |||
1087 | if (ch != prevCh && count > 0) { | |||
1088 | subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, | |||
1089 | prevCh, handler, *workCal, status); | |||
1090 | count = 0; | |||
1091 | } | |||
1092 | if (ch == QUOTE) { | |||
1093 | // Consecutive single quotes are a single quote literal, | |||
1094 | // either outside of quotes or between quotes | |||
1095 | if ((i+1) < patternLength && fPattern[i+1] == QUOTE) { | |||
1096 | appendTo += QUOTE; | |||
1097 | ++i; | |||
1098 | } else { | |||
1099 | inQuote = ! inQuote; | |||
1100 | } | |||
1101 | } | |||
1102 | else if (!inQuote && isSyntaxChar(ch)) { | |||
1103 | // ch is a date-time pattern character to be interpreted | |||
1104 | // by subFormat(); count the number of times it is repeated | |||
1105 | prevCh = ch; | |||
1106 | ++count; | |||
1107 | } | |||
1108 | else { | |||
1109 | // Append quoted characters and unquoted non-pattern characters | |||
1110 | appendTo += ch; | |||
1111 | } | |||
1112 | } | |||
1113 | ||||
1114 | // Format the last item in the pattern, if any | |||
1115 | if (count > 0) { | |||
1116 | subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, | |||
1117 | prevCh, handler, *workCal, status); | |||
1118 | } | |||
1119 | ||||
1120 | delete calClone; | |||
1121 | ||||
1122 | return appendTo; | |||
1123 | } | |||
1124 | ||||
1125 | //---------------------------------------------------------------------- | |||
1126 | ||||
1127 | /* Map calendar field into calendar field level. | |||
1128 | * the larger the level, the smaller the field unit. | |||
1129 | * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10, | |||
1130 | * UCAL_MONTH level is 20. | |||
1131 | * NOTE: if new fields adds in, the table needs to update. | |||
1132 | */ | |||
1133 | const int32_t | |||
1134 | SimpleDateFormat::fgCalendarFieldToLevel[] = | |||
1135 | { | |||
1136 | /*GyM*/ 0, 10, 20, | |||
1137 | /*wW*/ 20, 30, | |||
1138 | /*dDEF*/ 30, 20, 30, 30, | |||
1139 | /*ahHm*/ 40, 50, 50, 60, | |||
1140 | /*sS*/ 70, 80, | |||
1141 | /*z?Y*/ 0, 0, 10, | |||
1142 | /*eug*/ 30, 10, 0, | |||
1143 | /*A?.*/ 40, 0, 0 | |||
1144 | }; | |||
1145 | ||||
1146 | int32_t SimpleDateFormat::getLevelFromChar(char16_t ch) { | |||
1147 | // Map date field LETTER into calendar field level. | |||
1148 | // the larger the level, the smaller the field unit. | |||
1149 | // NOTE: if new fields adds in, the table needs to update. | |||
1150 | static const int32_t mapCharToLevel[] = { | |||
1151 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |||
1152 | // | |||
1153 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |||
1154 | // ! " # $ % & ' ( ) * + , - . / | |||
1155 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |||
1156 | #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR0 | |||
1157 | // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? | |||
1158 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, | |||
1159 | #else | |||
1160 | // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? | |||
1161 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |||
1162 | #endif | |||
1163 | // @ A B C D E F G H I J K L M N O | |||
1164 | -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0, | |||
1165 | // P Q R S T U V W X Y Z [ \ ] ^ _ | |||
1166 | -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1, | |||
1167 | // ` a b c d e f g h i j k l m n o | |||
1168 | -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, 0, 60, -1, -1, | |||
1169 | // p q r s t u v w x y z { | } ~ | |||
1170 | -1, 20, 10, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1 | |||
1171 | }; | |||
1172 | ||||
1173 | return ch < UPRV_LENGTHOF(mapCharToLevel)(int32_t)(sizeof(mapCharToLevel)/sizeof((mapCharToLevel)[0])) ? mapCharToLevel[ch] : -1; | |||
1174 | } | |||
1175 | ||||
1176 | UBool SimpleDateFormat::isSyntaxChar(char16_t ch) { | |||
1177 | static const UBool mapCharToIsSyntax[] = { | |||
1178 | // | |||
1179 | false, false, false, false, false, false, false, false, | |||
1180 | // | |||
1181 | false, false, false, false, false, false, false, false, | |||
1182 | // | |||
1183 | false, false, false, false, false, false, false, false, | |||
1184 | // | |||
1185 | false, false, false, false, false, false, false, false, | |||
1186 | // ! " # $ % & ' | |||
1187 | false, false, false, false, false, false, false, false, | |||
1188 | // ( ) * + , - . / | |||
1189 | false, false, false, false, false, false, false, false, | |||
1190 | // 0 1 2 3 4 5 6 7 | |||
1191 | false, false, false, false, false, false, false, false, | |||
1192 | #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR0 | |||
1193 | // 8 9 : ; < = > ? | |||
1194 | false, false, true, false, false, false, false, false, | |||
1195 | #else | |||
1196 | // 8 9 : ; < = > ? | |||
1197 | false, false, false, false, false, false, false, false, | |||
1198 | #endif | |||
1199 | // @ A B C D E F G | |||
1200 | false, true, true, true, true, true, true, true, | |||
1201 | // H I J K L M N O | |||
1202 | true, true, true, true, true, true, true, true, | |||
1203 | // P Q R S T U V W | |||
1204 | true, true, true, true, true, true, true, true, | |||
1205 | // X Y Z [ \ ] ^ _ | |||
1206 | true, true, true, false, false, false, false, false, | |||
1207 | // ` a b c d e f g | |||
1208 | false, true, true, true, true, true, true, true, | |||
1209 | // h i j k l m n o | |||
1210 | true, true, true, true, true, true, true, true, | |||
1211 | // p q r s t u v w | |||
1212 | true, true, true, true, true, true, true, true, | |||
1213 | // x y z { | } ~ | |||
1214 | true, true, true, false, false, false, false, false | |||
1215 | }; | |||
1216 | ||||
1217 | return ch < UPRV_LENGTHOF(mapCharToIsSyntax)(int32_t)(sizeof(mapCharToIsSyntax)/sizeof((mapCharToIsSyntax )[0])) ? mapCharToIsSyntax[ch] : false; | |||
1218 | } | |||
1219 | ||||
1220 | // Map index into pattern character string to Calendar field number. | |||
1221 | const UCalendarDateFields | |||
1222 | SimpleDateFormat::fgPatternIndexToCalendarField[] = | |||
1223 | { | |||
1224 | /*GyM*/ UCAL_ERA, UCAL_YEAR, UCAL_MONTH, | |||
1225 | /*dkH*/ UCAL_DATE, UCAL_HOUR_OF_DAY, UCAL_HOUR_OF_DAY, | |||
1226 | /*msS*/ UCAL_MINUTE, UCAL_SECOND, UCAL_MILLISECOND, | |||
1227 | /*EDF*/ UCAL_DAY_OF_WEEK, UCAL_DAY_OF_YEAR, UCAL_DAY_OF_WEEK_IN_MONTH, | |||
1228 | /*wWa*/ UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, UCAL_AM_PM, | |||
1229 | /*hKz*/ UCAL_HOUR, UCAL_HOUR, UCAL_ZONE_OFFSET, | |||
1230 | /*Yeu*/ UCAL_YEAR_WOY, UCAL_DOW_LOCAL, UCAL_EXTENDED_YEAR, | |||
1231 | /*gAZ*/ UCAL_JULIAN_DAY, UCAL_MILLISECONDS_IN_DAY, UCAL_ZONE_OFFSET, | |||
1232 | /*v*/ UCAL_ZONE_OFFSET, | |||
1233 | /*c*/ UCAL_DOW_LOCAL, | |||
1234 | /*L*/ UCAL_MONTH, | |||
1235 | /*Q*/ UCAL_MONTH, | |||
1236 | /*q*/ UCAL_MONTH, | |||
1237 | /*V*/ UCAL_ZONE_OFFSET, | |||
1238 | /*U*/ UCAL_YEAR, | |||
1239 | /*O*/ UCAL_ZONE_OFFSET, | |||
1240 | /*Xx*/ UCAL_ZONE_OFFSET, UCAL_ZONE_OFFSET, | |||
1241 | /*r*/ UCAL_EXTENDED_YEAR, | |||
1242 | /*bB*/ UCAL_FIELD_COUNT, UCAL_FIELD_COUNT, // no mappings to calendar fields | |||
1243 | #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR0 | |||
1244 | /*:*/ UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */ | |||
1245 | #else | |||
1246 | /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */ | |||
1247 | #endif | |||
1248 | }; | |||
1249 | ||||
1250 | // Map index into pattern character string to DateFormat field number | |||
1251 | const UDateFormatField | |||
1252 | SimpleDateFormat::fgPatternIndexToDateFormatField[] = { | |||
1253 | /*GyM*/ UDAT_ERA_FIELD, UDAT_YEAR_FIELD, UDAT_MONTH_FIELD, | |||
1254 | /*dkH*/ UDAT_DATE_FIELD, UDAT_HOUR_OF_DAY1_FIELD, UDAT_HOUR_OF_DAY0_FIELD, | |||
1255 | /*msS*/ UDAT_MINUTE_FIELD, UDAT_SECOND_FIELD, UDAT_FRACTIONAL_SECOND_FIELD, | |||
1256 | /*EDF*/ UDAT_DAY_OF_WEEK_FIELD, UDAT_DAY_OF_YEAR_FIELD, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD, | |||
1257 | /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD, UDAT_WEEK_OF_MONTH_FIELD, UDAT_AM_PM_FIELD, | |||
1258 | /*hKz*/ UDAT_HOUR1_FIELD, UDAT_HOUR0_FIELD, UDAT_TIMEZONE_FIELD, | |||
1259 | /*Yeu*/ UDAT_YEAR_WOY_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_EXTENDED_YEAR_FIELD, | |||
1260 | /*gAZ*/ UDAT_JULIAN_DAY_FIELD, UDAT_MILLISECONDS_IN_DAY_FIELD, UDAT_TIMEZONE_RFC_FIELD, | |||
1261 | /*v*/ UDAT_TIMEZONE_GENERIC_FIELD, | |||
1262 | /*c*/ UDAT_STANDALONE_DAY_FIELD, | |||
1263 | /*L*/ UDAT_STANDALONE_MONTH_FIELD, | |||
1264 | /*Q*/ UDAT_QUARTER_FIELD, | |||
1265 | /*q*/ UDAT_STANDALONE_QUARTER_FIELD, | |||
1266 | /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD, | |||
1267 | /*U*/ UDAT_YEAR_NAME_FIELD, | |||
1268 | /*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD, | |||
1269 | /*Xx*/ UDAT_TIMEZONE_ISO_FIELD, UDAT_TIMEZONE_ISO_LOCAL_FIELD, | |||
1270 | /*r*/ UDAT_RELATED_YEAR_FIELD, | |||
1271 | /*bB*/ UDAT_AM_PM_MIDNIGHT_NOON_FIELD, UDAT_FLEXIBLE_DAY_PERIOD_FIELD, | |||
1272 | #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR0 | |||
1273 | /*:*/ UDAT_TIME_SEPARATOR_FIELD, | |||
1274 | #else | |||
1275 | /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UDAT_TIME_SEPARATOR_FIELD, | |||
1276 | #endif | |||
1277 | }; | |||
1278 | ||||
1279 | //---------------------------------------------------------------------- | |||
1280 | ||||
1281 | /** | |||
1282 | * Append symbols[value] to dst. Make sure the array index is not out | |||
1283 | * of bounds. | |||
1284 | */ | |||
1285 | static inline void | |||
1286 | _appendSymbol(UnicodeString& dst, | |||
1287 | int32_t value, | |||
1288 | const UnicodeString* symbols, | |||
1289 | int32_t symbolsCount) { | |||
1290 | U_ASSERT(0 <= value && value < symbolsCount)(static_cast <bool> (0 <= value && value < symbolsCount) ? void (0) : __assert_fail ("0 <= value && value < symbolsCount" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); | |||
1291 | if (0 <= value && value < symbolsCount) { | |||
1292 | dst += symbols[value]; | |||
1293 | } | |||
1294 | } | |||
1295 | ||||
1296 | static inline void | |||
1297 | _appendSymbolWithMonthPattern(UnicodeString& dst, int32_t value, const UnicodeString* symbols, int32_t symbolsCount, | |||
1298 | const UnicodeString* monthPattern, UErrorCode& status) { | |||
1299 | U_ASSERT(0 <= value && value < symbolsCount)(static_cast <bool> (0 <= value && value < symbolsCount) ? void (0) : __assert_fail ("0 <= value && value < symbolsCount" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); | |||
1300 | if (0 <= value && value < symbolsCount) { | |||
1301 | if (monthPattern == nullptr) { | |||
1302 | dst += symbols[value]; | |||
1303 | } else { | |||
1304 | SimpleFormatter(*monthPattern, 1, 1, status).format(symbols[value], dst, status); | |||
1305 | } | |||
1306 | } | |||
1307 | } | |||
1308 | ||||
1309 | //---------------------------------------------------------------------- | |||
1310 | ||||
1311 | void | |||
1312 | SimpleDateFormat::initSimpleNumberFormatter(UErrorCode &status) { | |||
1313 | if (U_FAILURE(status)) { | |||
1314 | return; | |||
1315 | } | |||
1316 | const auto* df = dynamic_cast<const DecimalFormat*>(fNumberFormat); | |||
1317 | if (df == nullptr) { | |||
1318 | return; | |||
1319 | } | |||
1320 | const DecimalFormatSymbols* syms = df->getDecimalFormatSymbols(); | |||
1321 | if (syms == nullptr) { | |||
1322 | return; | |||
1323 | } | |||
1324 | fSimpleNumberFormatter = new number::SimpleNumberFormatter( | |||
1325 | number::SimpleNumberFormatter::forLocaleAndSymbolsAndGroupingStrategy( | |||
1326 | fLocale, *syms, UNUM_GROUPING_OFF, status | |||
1327 | ) | |||
1328 | ); | |||
1329 | if (fSimpleNumberFormatter == nullptr) { | |||
1330 | status = U_MEMORY_ALLOCATION_ERROR; | |||
1331 | } | |||
1332 | } | |||
1333 | ||||
1334 | void | |||
1335 | SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) { | |||
1336 | if (U_FAILURE(status)) { | |||
1337 | return; | |||
1338 | } | |||
1339 | if ( fDateOverride.isBogus() && fTimeOverride.isBogus() ) { | |||
1340 | return; | |||
1341 | } | |||
1342 | umtx_lockumtx_lock_77(&LOCK); | |||
1343 | if (fSharedNumberFormatters == nullptr) { | |||
1344 | fSharedNumberFormatters = allocSharedNumberFormatters(); | |||
1345 | if (fSharedNumberFormatters == nullptr) { | |||
1346 | status = U_MEMORY_ALLOCATION_ERROR; | |||
1347 | } | |||
1348 | } | |||
1349 | umtx_unlockumtx_unlock_77(&LOCK); | |||
1350 | ||||
1351 | if (U_FAILURE(status)) { | |||
1352 | return; | |||
1353 | } | |||
1354 | ||||
1355 | processOverrideString(locale,fDateOverride,kOvrStrDate,status); | |||
1356 | processOverrideString(locale,fTimeOverride,kOvrStrTime,status); | |||
1357 | } | |||
1358 | ||||
1359 | void | |||
1360 | SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeString &str, int8_t type, UErrorCode &status) { | |||
1361 | if (str.isBogus() || U_FAILURE(status)) { | |||
1362 | return; | |||
1363 | } | |||
1364 | ||||
1365 | int32_t start = 0; | |||
1366 | int32_t len; | |||
1367 | UnicodeString nsName; | |||
1368 | UnicodeString ovrField; | |||
1369 | UBool moreToProcess = true; | |||
1370 | NSOverride *overrideList = nullptr; | |||
1371 | ||||
1372 | while (moreToProcess) { | |||
1373 | int32_t delimiterPosition = str.indexOf(static_cast<char16_t>(ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE0x3B), start); | |||
1374 | if (delimiterPosition == -1) { | |||
1375 | moreToProcess = false; | |||
1376 | len = str.length() - start; | |||
1377 | } else { | |||
1378 | len = delimiterPosition - start; | |||
1379 | } | |||
1380 | UnicodeString currentString(str,start,len); | |||
1381 | int32_t equalSignPosition = currentString.indexOf(static_cast<char16_t>(ULOC_KEYWORD_ASSIGN_UNICODE0x3D), 0); | |||
1382 | if (equalSignPosition == -1) { // Simple override string such as "hebrew" | |||
1383 | nsName.setTo(currentString); | |||
1384 | ovrField.setToBogus(); | |||
1385 | } else { // Field specific override string such as "y=hebrew" | |||
1386 | nsName.setTo(currentString,equalSignPosition+1); | |||
1387 | ovrField.setTo(currentString,0,1); // We just need the first character. | |||
1388 | } | |||
1389 | ||||
1390 | int32_t nsNameHash = nsName.hashCode(); | |||
1391 | // See if the numbering system is in the override list, if not, then add it. | |||
1392 | NSOverride *curr = overrideList; | |||
1393 | const SharedNumberFormat *snf = nullptr; | |||
1394 | UBool found = false; | |||
1395 | while ( curr && !found ) { | |||
1396 | if ( curr->hash == nsNameHash ) { | |||
1397 | snf = curr->snf; | |||
1398 | found = true; | |||
1399 | } | |||
1400 | curr = curr->next; | |||
1401 | } | |||
1402 | ||||
1403 | if (!found) { | |||
1404 | LocalPointer<NSOverride> cur(new NSOverride); | |||
1405 | if (!cur.isNull()) { | |||
1406 | char kw[ULOC_KEYWORD_AND_VALUES_CAPACITY100]; | |||
1407 | uprv_strcpy(kw,"numbers="):: strcpy(kw, "numbers="); | |||
1408 | nsName.extract(0,len,kw+8,ULOC_KEYWORD_AND_VALUES_CAPACITY100-8,US_INVicu::UnicodeString::kInvariant); | |||
1409 | ||||
1410 | Locale ovrLoc(locale.getLanguage(),locale.getCountry(),locale.getVariant(),kw); | |||
1411 | cur->hash = nsNameHash; | |||
1412 | cur->next = overrideList; | |||
1413 | SharedObject::copyPtr( | |||
1414 | createSharedNumberFormat(ovrLoc, status), cur->snf); | |||
1415 | if (U_FAILURE(status)) { | |||
1416 | if (overrideList) { | |||
1417 | overrideList->free(); | |||
1418 | } | |||
1419 | return; | |||
1420 | } | |||
1421 | snf = cur->snf; | |||
1422 | overrideList = cur.orphan(); | |||
1423 | } else { | |||
1424 | status = U_MEMORY_ALLOCATION_ERROR; | |||
1425 | if (overrideList) { | |||
1426 | overrideList->free(); | |||
1427 | } | |||
1428 | return; | |||
1429 | } | |||
1430 | } | |||
1431 | ||||
1432 | // Now that we have an appropriate number formatter, fill in the appropriate spaces in the | |||
1433 | // number formatters table. | |||
1434 | if (ovrField.isBogus()) { | |||
1435 | switch (type) { | |||
1436 | case kOvrStrDate: | |||
1437 | case kOvrStrBoth: { | |||
1438 | for ( int8_t i=0 ; i<kDateFieldsCount; i++ ) { | |||
1439 | SharedObject::copyPtr(snf, fSharedNumberFormatters[kDateFields[i]]); | |||
1440 | } | |||
1441 | if (type==kOvrStrDate) { | |||
1442 | break; | |||
1443 | } | |||
1444 | U_FALLTHROUGH[[clang::fallthrough]]; | |||
1445 | } | |||
1446 | case kOvrStrTime : { | |||
1447 | for ( int8_t i=0 ; i<kTimeFieldsCount; i++ ) { | |||
1448 | SharedObject::copyPtr(snf, fSharedNumberFormatters[kTimeFields[i]]); | |||
1449 | } | |||
1450 | break; | |||
1451 | } | |||
1452 | } | |||
1453 | } else { | |||
1454 | // if the pattern character is unrecognized, signal an error and bail out | |||
1455 | UDateFormatField patternCharIndex = | |||
1456 | DateFormatSymbols::getPatternCharIndex(ovrField.charAt(0)); | |||
1457 | if (patternCharIndex == UDAT_FIELD_COUNT) { | |||
1458 | status = U_INVALID_FORMAT_ERROR; | |||
1459 | if (overrideList) { | |||
1460 | overrideList->free(); | |||
1461 | } | |||
1462 | return; | |||
1463 | } | |||
1464 | SharedObject::copyPtr(snf, fSharedNumberFormatters[patternCharIndex]); | |||
1465 | } | |||
1466 | ||||
1467 | start = delimiterPosition + 1; | |||
1468 | } | |||
1469 | if (overrideList) { | |||
1470 | overrideList->free(); | |||
1471 | } | |||
1472 | } | |||
1473 | ||||
1474 | //--------------------------------------------------------------------- | |||
1475 | void | |||
1476 | SimpleDateFormat::subFormat(UnicodeString &appendTo, | |||
1477 | char16_t ch, | |||
1478 | int32_t count, | |||
1479 | UDisplayContext capitalizationContext, | |||
1480 | int32_t fieldNum, | |||
1481 | char16_t fieldToOutput, | |||
1482 | FieldPositionHandler& handler, | |||
1483 | Calendar& cal, | |||
1484 | UErrorCode& status) const | |||
1485 | { | |||
1486 | static const int32_t maxIntCount = 10; | |||
1487 | static const UnicodeString hebr(u"hebr"); | |||
1488 | ||||
1489 | if (U_FAILURE(status)) { | |||
| ||||
1490 | return; | |||
1491 | } | |||
1492 | ||||
1493 | // this function gets called by format() to produce the appropriate substitution | |||
1494 | // text for an individual pattern symbol (e.g., "HH" or "yyyy") | |||
1495 | ||||
1496 | UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch); | |||
1497 | int32_t beginOffset = appendTo.length(); | |||
1498 | DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType = DateFormatSymbols::kCapContextUsageOther; | |||
1499 | ||||
1500 | // if the pattern character is unrecognized, signal an error and dump out | |||
1501 | if (patternCharIndex == UDAT_FIELD_COUNT) | |||
1502 | { | |||
1503 | if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored | |||
1504 | status = U_INVALID_FORMAT_ERROR; | |||
1505 | } | |||
1506 | return; | |||
1507 | } | |||
1508 | ||||
1509 | UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; | |||
1510 | int32_t value = 0; | |||
1511 | // Don't get value unless it is useful | |||
1512 | if (field < UCAL_FIELD_COUNT) { | |||
1513 | value = (patternCharIndex != UDAT_RELATED_YEAR_FIELD)? cal.get(field, status): cal.getRelatedYear(status); | |||
1514 | if (U_FAILURE(status)) { | |||
1515 | return; | |||
1516 | } | |||
1517 | } | |||
1518 | ||||
1519 | const NumberFormat *currentNumberFormat = getNumberFormatByIndex(patternCharIndex); | |||
1520 | if (currentNumberFormat == nullptr) { | |||
1521 | status = U_INTERNAL_PROGRAM_ERROR; | |||
1522 | return; | |||
1523 | } | |||
1524 | ||||
1525 | switch (patternCharIndex) { | |||
1526 | ||||
1527 | // for any "G" symbol, write out the appropriate era string | |||
1528 | // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name | |||
1529 | case UDAT_ERA_FIELD: | |||
1530 | { | |||
1531 | const char* type = cal.getType(); | |||
1532 | if (strcmp(type, "chinese") == 0 || | |||
1533 | strcmp(type, "dangi") == 0) { | |||
1534 | zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, 9); // as in ICU4J | |||
1535 | } else { | |||
1536 | if (count == 5) { | |||
1537 | _appendSymbol(appendTo, value, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount); | |||
1538 | capContextUsageType = DateFormatSymbols::kCapContextUsageEraNarrow; | |||
1539 | } else if (count == 4) { | |||
1540 | _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount); | |||
1541 | capContextUsageType = DateFormatSymbols::kCapContextUsageEraWide; | |||
1542 | } else { | |||
1543 | _appendSymbol(appendTo, value, fSymbols->fEras, fSymbols->fErasCount); | |||
1544 | capContextUsageType = DateFormatSymbols::kCapContextUsageEraAbbrev; | |||
1545 | } | |||
1546 | } | |||
1547 | } | |||
1548 | break; | |||
1549 | ||||
1550 | case UDAT_YEAR_NAME_FIELD: | |||
1551 | if (fSymbols->fShortYearNames != nullptr && value <= fSymbols->fShortYearNamesCount) { | |||
1552 | // the Calendar YEAR field runs 1 through 60 for cyclic years | |||
1553 | _appendSymbol(appendTo, value - 1, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount); | |||
1554 | break; | |||
1555 | } | |||
1556 | // else fall through to numeric year handling, do not break here | |||
1557 | U_FALLTHROUGH[[clang::fallthrough]]; | |||
1558 | ||||
1559 | // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits | |||
1560 | // NEW: UTS#35: | |||
1561 | //Year y yy yyy yyyy yyyyy | |||
1562 | //AD 1 1 01 001 0001 00001 | |||
1563 | //AD 12 12 12 012 0012 00012 | |||
1564 | //AD 123 123 23 123 0123 00123 | |||
1565 | //AD 1234 1234 34 1234 1234 01234 | |||
1566 | //AD 12345 12345 45 12345 12345 12345 | |||
1567 | case UDAT_YEAR_FIELD: | |||
1568 | case UDAT_YEAR_WOY_FIELD: | |||
1569 | if (fDateOverride.compare(hebr)==0 && value>HEBREW_CAL_CUR_MILLENIUM_START_YEAR && value<HEBREW_CAL_CUR_MILLENIUM_END_YEAR) { | |||
1570 | value-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR; | |||
1571 | } | |||
1572 | if(count == 2) | |||
1573 | zeroPaddingNumber(currentNumberFormat, appendTo, value, 2, 2); | |||
1574 | else | |||
1575 | zeroPaddingNumber(currentNumberFormat, appendTo, value, count, maxIntCount); | |||
1576 | break; | |||
1577 | ||||
1578 | // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month | |||
1579 | // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the | |||
1580 | // appropriate number of digits | |||
1581 | // for "MMMMM"/"LLLLL", use the narrow form | |||
1582 | case UDAT_MONTH_FIELD: | |||
1583 | case UDAT_STANDALONE_MONTH_FIELD: | |||
1584 | if (typeid(cal) == typeid(HebrewCalendar)) { | |||
1585 | if (HebrewCalendar::isLeapYear(cal.get(UCAL_YEAR,status)) && value == 6 && count >= 3 ) | |||
1586 | value = 13; // Show alternate form for Adar II in leap years in Hebrew calendar. | |||
1587 | if (!HebrewCalendar::isLeapYear(cal.get(UCAL_YEAR,status)) && value >= 6 && count < 3 ) | |||
1588 | value--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7. | |||
1589 | } | |||
1590 | { | |||
1591 | int32_t isLeapMonth = (fSymbols->fLeapMonthPatterns != nullptr && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount)? | |||
1592 | cal.get(UCAL_IS_LEAP_MONTH, status): 0; | |||
1593 | // should consolidate the next section by using arrays of pointers & counts for the right symbols... | |||
1594 | if (count == 5) { | |||
1595 | if (patternCharIndex == UDAT_MONTH_FIELD) { | |||
1596 | _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fNarrowMonths, fSymbols->fNarrowMonthsCount, | |||
1597 | (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatNarrow]): nullptr, status); | |||
1598 | } else { | |||
1599 | _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneNarrowMonths, fSymbols->fStandaloneNarrowMonthsCount, | |||
1600 | (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow]): nullptr, status); | |||
1601 | } | |||
1602 | capContextUsageType = DateFormatSymbols::kCapContextUsageMonthNarrow; | |||
1603 | } else if (count == 4) { | |||
1604 | if (patternCharIndex == UDAT_MONTH_FIELD) { | |||
1605 | _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fMonths, fSymbols->fMonthsCount, | |||
1606 | (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide]): nullptr, status); | |||
1607 | capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat; | |||
1608 | } else { | |||
1609 | _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, | |||
1610 | (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide]): nullptr, status); | |||
1611 | capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone; | |||
1612 | } | |||
1613 | } else if (count == 3) { | |||
1614 | if (patternCharIndex == UDAT_MONTH_FIELD) { | |||
1615 | _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fShortMonths, fSymbols->fShortMonthsCount, | |||
1616 | (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev]): nullptr, status); | |||
1617 | capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat; | |||
1618 | } else { | |||
1619 | _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, | |||
1620 | (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev]): nullptr, status); | |||
1621 | capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone; | |||
1622 | } | |||
1623 | } else { | |||
1624 | UnicodeString monthNumber; | |||
1625 | zeroPaddingNumber(currentNumberFormat,monthNumber, value + 1, count, maxIntCount); | |||
1626 | _appendSymbolWithMonthPattern(appendTo, 0, &monthNumber, 1, | |||
1627 | (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric]): nullptr, status); | |||
1628 | } | |||
1629 | } | |||
1630 | break; | |||
1631 | ||||
1632 | // for "k" and "kk", write out the hour, adjusting midnight to appear as "24" | |||
1633 | case UDAT_HOUR_OF_DAY1_FIELD: | |||
1634 | if (value == 0) | |||
1635 | zeroPaddingNumber(currentNumberFormat,appendTo, cal.getMaximum(UCAL_HOUR_OF_DAY) + 1, count, maxIntCount); | |||
1636 | else | |||
1637 | zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); | |||
1638 | break; | |||
1639 | ||||
1640 | case UDAT_FRACTIONAL_SECOND_FIELD: | |||
1641 | // Fractional seconds left-justify | |||
1642 | { | |||
1643 | int32_t minDigits = (count > 3) ? 3 : count; | |||
1644 | if (count == 1) { | |||
1645 | value /= 100; | |||
1646 | } else if (count == 2) { | |||
1647 | value /= 10; | |||
1648 | } | |||
1649 | zeroPaddingNumber(currentNumberFormat, appendTo, value, minDigits, maxIntCount); | |||
1650 | if (count > 3) { | |||
1651 | zeroPaddingNumber(currentNumberFormat, appendTo, 0, count - 3, maxIntCount); | |||
1652 | } | |||
1653 | } | |||
1654 | break; | |||
1655 | ||||
1656 | // for "ee" or "e", use local numeric day-of-the-week | |||
1657 | // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name | |||
1658 | // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name | |||
1659 | // for "EEEE" or "eeee", write out the wide day-of-the-week name | |||
1660 | // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name | |||
1661 | case UDAT_DOW_LOCAL_FIELD: | |||
1662 | if ( count < 3 ) { | |||
1663 | zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); | |||
1664 | break; | |||
1665 | } | |||
1666 | // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week, | |||
1667 | // we want standard day-of-week, so first fix value to work for EEEEE-EEE. | |||
1668 | value = cal.get(UCAL_DAY_OF_WEEK, status); | |||
1669 | if (U_FAILURE(status)) { | |||
1670 | return; | |||
1671 | } | |||
1672 | // fall through, do not break here | |||
1673 | U_FALLTHROUGH[[clang::fallthrough]]; | |||
1674 | case UDAT_DAY_OF_WEEK_FIELD: | |||
1675 | if (count == 5) { | |||
1676 | _appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays, | |||
1677 | fSymbols->fNarrowWeekdaysCount); | |||
1678 | capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow; | |||
1679 | } else if (count == 4) { | |||
1680 | _appendSymbol(appendTo, value, fSymbols->fWeekdays, | |||
1681 | fSymbols->fWeekdaysCount); | |||
1682 | capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat; | |||
1683 | } else if (count == 6) { | |||
1684 | _appendSymbol(appendTo, value, fSymbols->fShorterWeekdays, | |||
1685 | fSymbols->fShorterWeekdaysCount); | |||
1686 | capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat; | |||
1687 | } else { | |||
1688 | _appendSymbol(appendTo, value, fSymbols->fShortWeekdays, | |||
1689 | fSymbols->fShortWeekdaysCount); | |||
1690 | capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat; | |||
1691 | } | |||
1692 | break; | |||
1693 | ||||
1694 | // for "ccc", write out the abbreviated day-of-the-week name | |||
1695 | // for "cccc", write out the wide day-of-the-week name | |||
1696 | // for "ccccc", use the narrow day-of-the-week name | |||
1697 | // for "ccccc", use the short day-of-the-week name | |||
1698 | case UDAT_STANDALONE_DAY_FIELD: | |||
1699 | if ( count < 3 ) { | |||
1700 | zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, maxIntCount); | |||
1701 | break; | |||
1702 | } | |||
1703 | // fall through to alpha DOW handling, but for that we don't want local day-of-week, | |||
1704 | // we want standard day-of-week, so first fix value. | |||
1705 | value = cal.get(UCAL_DAY_OF_WEEK, status); | |||
1706 | if (U_FAILURE(status)) { | |||
1707 | return; | |||
1708 | } | |||
1709 | if (count == 5) { | |||
1710 | _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowWeekdays, | |||
1711 | fSymbols->fStandaloneNarrowWeekdaysCount); | |||
1712 | capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow; | |||
1713 | } else if (count == 4) { | |||
1714 | _appendSymbol(appendTo, value, fSymbols->fStandaloneWeekdays, | |||
1715 | fSymbols->fStandaloneWeekdaysCount); | |||
1716 | capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone; | |||
1717 | } else if (count == 6) { | |||
1718 | _appendSymbol(appendTo, value, fSymbols->fStandaloneShorterWeekdays, | |||
1719 | fSymbols->fStandaloneShorterWeekdaysCount); | |||
1720 | capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone; | |||
1721 | } else { // count == 3 | |||
1722 | _appendSymbol(appendTo, value, fSymbols->fStandaloneShortWeekdays, | |||
1723 | fSymbols->fStandaloneShortWeekdaysCount); | |||
1724 | capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone; | |||
1725 | } | |||
1726 | break; | |||
1727 | ||||
1728 | // for "a" symbol, write out the whole AM/PM string | |||
1729 | case UDAT_AM_PM_FIELD: | |||
1730 | if (count < 5) { | |||
1731 | _appendSymbol(appendTo, value, fSymbols->fAmPms, | |||
1732 | fSymbols->fAmPmsCount); | |||
1733 | } else { | |||
1734 | _appendSymbol(appendTo, value, fSymbols->fNarrowAmPms, | |||
1735 | fSymbols->fNarrowAmPmsCount); | |||
1736 | } | |||
1737 | break; | |||
1738 | ||||
1739 | // if we see pattern character for UDAT_TIME_SEPARATOR_FIELD (none currently defined), | |||
1740 | // write out the time separator string. Leave support in for future definition. | |||
1741 | case UDAT_TIME_SEPARATOR_FIELD: | |||
1742 | { | |||
1743 | UnicodeString separator; | |||
1744 | appendTo += fSymbols->getTimeSeparatorString(separator); | |||
1745 | } | |||
1746 | break; | |||
1747 | ||||
1748 | // for "h" and "hh", write out the hour, adjusting noon and midnight to show up | |||
1749 | // as "12" | |||
1750 | case UDAT_HOUR1_FIELD: | |||
1751 | if (value == 0) | |||
1752 | zeroPaddingNumber(currentNumberFormat,appendTo, cal.getLeastMaximum(UCAL_HOUR) + 1, count, maxIntCount); | |||
1753 | else | |||
1754 | zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); | |||
1755 | break; | |||
1756 | ||||
1757 | case UDAT_TIMEZONE_FIELD: // 'z' | |||
1758 | case UDAT_TIMEZONE_RFC_FIELD: // 'Z' | |||
1759 | case UDAT_TIMEZONE_GENERIC_FIELD: // 'v' | |||
1760 | case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V' | |||
1761 | case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O' | |||
1762 | case UDAT_TIMEZONE_ISO_FIELD: // 'X' | |||
1763 | case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x' | |||
1764 | { | |||
1765 | char16_t zsbuf[ZONE_NAME_U16_MAX128]; | |||
1766 | UnicodeString zoneString(zsbuf, 0, UPRV_LENGTHOF(zsbuf)(int32_t)(sizeof(zsbuf)/sizeof((zsbuf)[0]))); | |||
1767 | const TimeZone& tz = cal.getTimeZone(); | |||
1768 | UDate date = cal.getTime(status); | |||
1769 | const TimeZoneFormat *tzfmt = tzFormat(status); | |||
1770 | if (U_SUCCESS(status)) { | |||
1771 | switch (patternCharIndex) { | |||
1772 | case UDAT_TIMEZONE_FIELD: | |||
1773 | if (count < 4) { | |||
1774 | // "z", "zz", "zzz" | |||
1775 | tzfmt->format(UTZFMT_STYLE_SPECIFIC_SHORT, tz, date, zoneString); | |||
1776 | capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort; | |||
1777 | } else { | |||
1778 | // "zzzz" or longer | |||
1779 | tzfmt->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, date, zoneString); | |||
1780 | capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong; | |||
1781 | } | |||
1782 | break; | |||
1783 | case UDAT_TIMEZONE_RFC_FIELD: | |||
1784 | if (count < 4) { | |||
1785 | // "Z" | |||
1786 | tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString); | |||
1787 | } else if (count == 5) { | |||
1788 | // "ZZZZZ" | |||
1789 | tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString); | |||
1790 | } else { | |||
1791 | // "ZZ", "ZZZ", "ZZZZ" | |||
1792 | tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString); | |||
1793 | } | |||
1794 | break; | |||
1795 | case UDAT_TIMEZONE_GENERIC_FIELD: | |||
1796 | if (count == 1) { | |||
1797 | // "v" | |||
1798 | tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, tz, date, zoneString); | |||
1799 | capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort; | |||
1800 | } else if (count == 4) { | |||
1801 | // "vvvv" | |||
1802 | tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, tz, date, zoneString); | |||
1803 | capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong; | |||
1804 | } | |||
1805 | break; | |||
1806 | case UDAT_TIMEZONE_SPECIAL_FIELD: | |||
1807 | if (count == 1) { | |||
1808 | // "V" | |||
1809 | tzfmt->format(UTZFMT_STYLE_ZONE_ID_SHORT, tz, date, zoneString); | |||
1810 | } else if (count == 2) { | |||
1811 | // "VV" | |||
1812 | tzfmt->format(UTZFMT_STYLE_ZONE_ID, tz, date, zoneString); | |||
1813 | } else if (count == 3) { | |||
1814 | // "VVV" | |||
1815 | tzfmt->format(UTZFMT_STYLE_EXEMPLAR_LOCATION, tz, date, zoneString); | |||
1816 | } else if (count == 4) { | |||
1817 | // "VVVV" | |||
1818 | tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, tz, date, zoneString); | |||
1819 | capContextUsageType = DateFormatSymbols::kCapContextUsageZoneLong; | |||
1820 | } | |||
1821 | break; | |||
1822 | case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: | |||
1823 | if (count == 1) { | |||
1824 | // "O" | |||
1825 | tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT, tz, date, zoneString); | |||
1826 | } else if (count == 4) { | |||
1827 | // "OOOO" | |||
1828 | tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString); | |||
1829 | } | |||
1830 | break; | |||
1831 | case UDAT_TIMEZONE_ISO_FIELD: | |||
1832 | if (count == 1) { | |||
1833 | // "X" | |||
1834 | tzfmt->format(UTZFMT_STYLE_ISO_BASIC_SHORT, tz, date, zoneString); | |||
1835 | } else if (count == 2) { | |||
1836 | // "XX" | |||
1837 | tzfmt->format(UTZFMT_STYLE_ISO_BASIC_FIXED, tz, date, zoneString); | |||
1838 | } else if (count == 3) { | |||
1839 | // "XXX" | |||
1840 | tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED, tz, date, zoneString); | |||
1841 | } else if (count == 4) { | |||
1842 | // "XXXX" | |||
1843 | tzfmt->format(UTZFMT_STYLE_ISO_BASIC_FULL, tz, date, zoneString); | |||
1844 | } else if (count == 5) { | |||
1845 | // "XXXXX" | |||
1846 | tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString); | |||
1847 | } | |||
1848 | break; | |||
1849 | case UDAT_TIMEZONE_ISO_LOCAL_FIELD: | |||
1850 | if (count == 1) { | |||
1851 | // "x" | |||
1852 | tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT, tz, date, zoneString); | |||
1853 | } else if (count == 2) { | |||
1854 | // "xx" | |||
1855 | tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED, tz, date, zoneString); | |||
1856 | } else if (count == 3) { | |||
1857 | // "xxx" | |||
1858 | tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED, tz, date, zoneString); | |||
1859 | } else if (count == 4) { | |||
1860 | // "xxxx" | |||
1861 | tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString); | |||
1862 | } else if (count == 5) { | |||
1863 | // "xxxxx" | |||
1864 | tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL, tz, date, zoneString); | |||
1865 | } | |||
1866 | break; | |||
1867 | default: | |||
1868 | UPRV_UNREACHABLE_EXITabort(); | |||
1869 | } | |||
1870 | } | |||
1871 | appendTo += zoneString; | |||
1872 | } | |||
1873 | break; | |||
1874 | ||||
1875 | case UDAT_QUARTER_FIELD: | |||
1876 | if (count >= 5) | |||
1877 | _appendSymbol(appendTo, value/3, fSymbols->fNarrowQuarters, | |||
1878 | fSymbols->fNarrowQuartersCount); | |||
1879 | else if (count == 4) | |||
1880 | _appendSymbol(appendTo, value/3, fSymbols->fQuarters, | |||
1881 | fSymbols->fQuartersCount); | |||
1882 | else if (count == 3) | |||
1883 | _appendSymbol(appendTo, value/3, fSymbols->fShortQuarters, | |||
1884 | fSymbols->fShortQuartersCount); | |||
1885 | else | |||
1886 | zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount); | |||
1887 | break; | |||
1888 | ||||
1889 | case UDAT_STANDALONE_QUARTER_FIELD: | |||
1890 | if (count >= 5) | |||
1891 | _appendSymbol(appendTo, value/3, fSymbols->fStandaloneNarrowQuarters, | |||
1892 | fSymbols->fStandaloneNarrowQuartersCount); | |||
1893 | else if (count == 4) | |||
1894 | _appendSymbol(appendTo, value/3, fSymbols->fStandaloneQuarters, | |||
1895 | fSymbols->fStandaloneQuartersCount); | |||
1896 | else if (count == 3) | |||
1897 | _appendSymbol(appendTo, value/3, fSymbols->fStandaloneShortQuarters, | |||
1898 | fSymbols->fStandaloneShortQuartersCount); | |||
1899 | else | |||
1900 | zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount); | |||
1901 | break; | |||
1902 | ||||
1903 | case UDAT_AM_PM_MIDNIGHT_NOON_FIELD: | |||
1904 | { | |||
1905 | const UnicodeString *toAppend = nullptr; | |||
1906 | int32_t hour = cal.get(UCAL_HOUR_OF_DAY, status); | |||
1907 | ||||
1908 | // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day. | |||
1909 | // For ICU 57 output of "midnight" is temporarily suppressed. | |||
1910 | ||||
1911 | // For "midnight" and "noon": | |||
1912 | // Time, as displayed, must be exactly noon or midnight. | |||
1913 | // This means minutes and seconds, if present, must be zero. | |||
1914 | if ((/*hour == 0 ||*/ hour == 12) && | |||
1915 | (!fHasMinute || cal.get(UCAL_MINUTE, status) == 0) && | |||
1916 | (!fHasSecond || cal.get(UCAL_SECOND, status) == 0)) { | |||
1917 | // Stealing am/pm value to use as our array index. | |||
1918 | // It works out: am/midnight are both 0, pm/noon are both 1, | |||
1919 | // 12 am is 12 midnight, and 12 pm is 12 noon. | |||
1920 | int32_t val = cal.get(UCAL_AM_PM, status); | |||
1921 | ||||
1922 | if (count <= 3) { | |||
1923 | toAppend = &fSymbols->fAbbreviatedDayPeriods[val]; | |||
1924 | } else if (count == 4 || count > 5) { | |||
1925 | toAppend = &fSymbols->fWideDayPeriods[val]; | |||
1926 | } else { // count == 5 | |||
1927 | toAppend = &fSymbols->fNarrowDayPeriods[val]; | |||
1928 | } | |||
1929 | } | |||
1930 | ||||
1931 | // toAppend is nullptr if time isn't exactly midnight or noon (as displayed). | |||
1932 | // toAppend is bogus if time is midnight or noon, but no localized string exists. | |||
1933 | // In either case, fall back to am/pm. | |||
1934 | if (toAppend == nullptr || toAppend->isBogus()) { | |||
1935 | // Reformat with identical arguments except ch, now changed to 'a'. | |||
1936 | // We are passing a different fieldToOutput because we want to add | |||
1937 | // 'b' to field position. This makes this fallback stable when | |||
1938 | // there is a data change on locales. | |||
1939 | subFormat(appendTo, u'a', count, capitalizationContext, fieldNum, u'b', handler, cal, status); | |||
1940 | return; | |||
1941 | } else { | |||
1942 | appendTo += *toAppend; | |||
1943 | } | |||
1944 | ||||
1945 | break; | |||
1946 | } | |||
1947 | ||||
1948 | case UDAT_FLEXIBLE_DAY_PERIOD_FIELD: | |||
1949 | { | |||
1950 | // TODO: Maybe fetch the DayperiodRules during initialization (instead of at the first | |||
1951 | // loading of an instance) if a relevant pattern character (b or B) is used. | |||
1952 | const DayPeriodRules *ruleSet = DayPeriodRules::getInstance(this->getSmpFmtLocale(), status); | |||
1953 | if (U_FAILURE(status)) { | |||
1954 | // Data doesn't conform to spec, therefore loading failed. | |||
1955 | break; | |||
1956 | } | |||
1957 | if (ruleSet == nullptr) { | |||
1958 | // Data doesn't exist for the locale we're looking for. | |||
1959 | // Falling back to am/pm. | |||
1960 | // We are passing a different fieldToOutput because we want to add | |||
1961 | // 'B' to field position. This makes this fallback stable when | |||
1962 | // there is a data change on locales. | |||
1963 | subFormat(appendTo, u'a', count, capitalizationContext, fieldNum, u'B', handler, cal, status); | |||
1964 | return; | |||
1965 | } | |||
1966 | ||||
1967 | // Get current display time. | |||
1968 | int32_t hour = cal.get(UCAL_HOUR_OF_DAY, status); | |||
1969 | int32_t minute = 0; | |||
1970 | if (fHasMinute) { | |||
1971 | minute = cal.get(UCAL_MINUTE, status); | |||
1972 | } | |||
1973 | int32_t second = 0; | |||
1974 | if (fHasSecond) { | |||
1975 | second = cal.get(UCAL_SECOND, status); | |||
1976 | } | |||
1977 | ||||
1978 | // Determine day period. | |||
1979 | DayPeriodRules::DayPeriod periodType; | |||
1980 | if (hour == 0 && minute == 0 && second == 0 && ruleSet->hasMidnight()) { | |||
1981 | periodType = DayPeriodRules::DAYPERIOD_MIDNIGHT; | |||
1982 | } else if (hour == 12 && minute == 0 && second == 0 && ruleSet->hasNoon()) { | |||
1983 | periodType = DayPeriodRules::DAYPERIOD_NOON; | |||
1984 | } else { | |||
1985 | periodType = ruleSet->getDayPeriodForHour(hour); | |||
1986 | } | |||
1987 | ||||
1988 | // Rule set exists, therefore periodType can't be UNKNOWN. | |||
1989 | // Get localized string. | |||
1990 | U_ASSERT(periodType != DayPeriodRules::DAYPERIOD_UNKNOWN)(static_cast <bool> (periodType != DayPeriodRules::DAYPERIOD_UNKNOWN ) ? void (0) : __assert_fail ("periodType != DayPeriodRules::DAYPERIOD_UNKNOWN" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); | |||
1991 | UnicodeString *toAppend = nullptr; | |||
1992 | int32_t index; | |||
1993 | ||||
1994 | // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day. | |||
1995 | // For ICU 57 output of "midnight" is temporarily suppressed. | |||
1996 | ||||
1997 | if (periodType != DayPeriodRules::DAYPERIOD_AM && | |||
1998 | periodType != DayPeriodRules::DAYPERIOD_PM && | |||
1999 | periodType != DayPeriodRules::DAYPERIOD_MIDNIGHT) { | |||
2000 | index = static_cast<int32_t>(periodType); | |||
2001 | if (count <= 3) { | |||
2002 | toAppend = &fSymbols->fAbbreviatedDayPeriods[index]; // i.e. short | |||
2003 | } else if (count == 4 || count > 5) { | |||
2004 | toAppend = &fSymbols->fWideDayPeriods[index]; | |||
2005 | } else { // count == 5 | |||
2006 | toAppend = &fSymbols->fNarrowDayPeriods[index]; | |||
2007 | } | |||
2008 | } | |||
2009 | ||||
2010 | // Fallback schedule: | |||
2011 | // Midnight/Noon -> General Periods -> AM/PM. | |||
2012 | ||||
2013 | // Midnight/Noon -> General Periods. | |||
2014 | if ((toAppend == nullptr || toAppend->isBogus()) && | |||
2015 | (periodType
| |||
2016 | periodType == DayPeriodRules::DAYPERIOD_NOON)) { | |||
2017 | periodType = ruleSet->getDayPeriodForHour(hour); | |||
2018 | index = static_cast<int32_t>(periodType); | |||
2019 | ||||
2020 | if (count <= 3) { | |||
2021 | toAppend = &fSymbols->fAbbreviatedDayPeriods[index]; // i.e. short | |||
2022 | } else if (count == 4 || count > 5) { | |||
2023 | toAppend = &fSymbols->fWideDayPeriods[index]; | |||
2024 | } else { // count == 5 | |||
2025 | toAppend = &fSymbols->fNarrowDayPeriods[index]; | |||
2026 | } | |||
2027 | } | |||
2028 | ||||
2029 | // General Periods -> AM/PM. | |||
2030 | if (periodType
| |||
2031 | periodType
| |||
2032 | toAppend->isBogus()) { | |||
| ||||
2033 | // We are passing a different fieldToOutput because we want to add | |||
2034 | // 'B' to field position iterator. This makes this fallback stable when | |||
2035 | // there is a data change on locales. | |||
2036 | subFormat(appendTo, u'a', count, capitalizationContext, fieldNum, u'B', handler, cal, status); | |||
2037 | return; | |||
2038 | } | |||
2039 | else { | |||
2040 | appendTo += *toAppend; | |||
2041 | } | |||
2042 | ||||
2043 | break; | |||
2044 | } | |||
2045 | ||||
2046 | // all of the other pattern symbols can be formatted as simple numbers with | |||
2047 | // appropriate zero padding | |||
2048 | default: | |||
2049 | zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount); | |||
2050 | break; | |||
2051 | } | |||
2052 | #if !UCONFIG_NO_BREAK_ITERATION1 | |||
2053 | // if first field, check to see whether we need to and are able to titlecase it | |||
2054 | if (fieldNum == 0 && fCapitalizationBrkIter != nullptr && appendTo.length() > beginOffset && | |||
2055 | u_isloweru_islower_77(appendTo.char32At(beginOffset))) { | |||
2056 | UBool titlecase = false; | |||
2057 | switch (capitalizationContext) { | |||
2058 | case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE: | |||
2059 | titlecase = true; | |||
2060 | break; | |||
2061 | case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU: | |||
2062 | titlecase = fSymbols->fCapitalization[capContextUsageType][0]; | |||
2063 | break; | |||
2064 | case UDISPCTX_CAPITALIZATION_FOR_STANDALONE: | |||
2065 | titlecase = fSymbols->fCapitalization[capContextUsageType][1]; | |||
2066 | break; | |||
2067 | default: | |||
2068 | // titlecase = false; | |||
2069 | break; | |||
2070 | } | |||
2071 | if (titlecase) { | |||
2072 | BreakIterator* const mutableCapitalizationBrkIter = fCapitalizationBrkIter->clone(); | |||
2073 | UnicodeString firstField(appendTo, beginOffset); | |||
2074 | firstField.toTitle(mutableCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE0x100 | U_TITLECASE_NO_BREAK_ADJUSTMENT0x200); | |||
2075 | appendTo.replaceBetween(beginOffset, appendTo.length(), firstField); | |||
2076 | delete mutableCapitalizationBrkIter; | |||
2077 | } | |||
2078 | } | |||
2079 | #endif | |||
2080 | ||||
2081 | handler.addAttribute(DateFormatSymbols::getPatternCharIndex(fieldToOutput), beginOffset, appendTo.length()); | |||
2082 | } | |||
2083 | ||||
2084 | //---------------------------------------------------------------------- | |||
2085 | ||||
2086 | void SimpleDateFormat::adoptNumberFormat(NumberFormat *formatToAdopt) { | |||
2087 | // Null out the fast formatter, it references fNumberFormat which we're | |||
2088 | // about to invalidate | |||
2089 | delete fSimpleNumberFormatter; | |||
2090 | fSimpleNumberFormatter = nullptr; | |||
2091 | ||||
2092 | fixNumberFormatForDates(*formatToAdopt); | |||
2093 | delete fNumberFormat; | |||
2094 | fNumberFormat = formatToAdopt; | |||
2095 | ||||
2096 | // We successfully set the default number format. Now delete the overrides | |||
2097 | // (can't fail). | |||
2098 | if (fSharedNumberFormatters) { | |||
2099 | freeSharedNumberFormatters(fSharedNumberFormatters); | |||
2100 | fSharedNumberFormatters = nullptr; | |||
2101 | } | |||
2102 | ||||
2103 | // Recompute fSimpleNumberFormatter if necessary | |||
2104 | UErrorCode localStatus = U_ZERO_ERROR; | |||
2105 | initSimpleNumberFormatter(localStatus); | |||
2106 | } | |||
2107 | ||||
2108 | void SimpleDateFormat::adoptNumberFormat(const UnicodeString& fields, NumberFormat *formatToAdopt, UErrorCode &status){ | |||
2109 | fixNumberFormatForDates(*formatToAdopt); | |||
2110 | LocalPointer<NumberFormat> fmt(formatToAdopt); | |||
2111 | if (U_FAILURE(status)) { | |||
2112 | return; | |||
2113 | } | |||
2114 | ||||
2115 | // We must ensure fSharedNumberFormatters is allocated. | |||
2116 | if (fSharedNumberFormatters == nullptr) { | |||
2117 | fSharedNumberFormatters = allocSharedNumberFormatters(); | |||
2118 | if (fSharedNumberFormatters == nullptr) { | |||
2119 | status = U_MEMORY_ALLOCATION_ERROR; | |||
2120 | return; | |||
2121 | } | |||
2122 | } | |||
2123 | const SharedNumberFormat *newFormat = createSharedNumberFormat(fmt.orphan()); | |||
2124 | if (newFormat == nullptr) { | |||
2125 | status = U_MEMORY_ALLOCATION_ERROR; | |||
2126 | return; | |||
2127 | } | |||
2128 | for (int i=0; i<fields.length(); i++) { | |||
2129 | char16_t field = fields.charAt(i); | |||
2130 | // if the pattern character is unrecognized, signal an error and bail out | |||
2131 | UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(field); | |||
2132 | if (patternCharIndex == UDAT_FIELD_COUNT) { | |||
2133 | status = U_INVALID_FORMAT_ERROR; | |||
2134 | newFormat->deleteIfZeroRefCount(); | |||
2135 | return; | |||
2136 | } | |||
2137 | ||||
2138 | // Set the number formatter in the table | |||
2139 | SharedObject::copyPtr( | |||
2140 | newFormat, fSharedNumberFormatters[patternCharIndex]); | |||
2141 | } | |||
2142 | newFormat->deleteIfZeroRefCount(); | |||
2143 | } | |||
2144 | ||||
2145 | const NumberFormat * | |||
2146 | SimpleDateFormat::getNumberFormatForField(char16_t field) const { | |||
2147 | UDateFormatField index = DateFormatSymbols::getPatternCharIndex(field); | |||
2148 | if (index == UDAT_FIELD_COUNT) { | |||
2149 | return nullptr; | |||
2150 | } | |||
2151 | return getNumberFormatByIndex(index); | |||
2152 | } | |||
2153 | ||||
2154 | //---------------------------------------------------------------------- | |||
2155 | void | |||
2156 | SimpleDateFormat::zeroPaddingNumber( | |||
2157 | const NumberFormat *currentNumberFormat, | |||
2158 | UnicodeString &appendTo, | |||
2159 | int32_t value, int32_t minDigits, int32_t maxDigits) const | |||
2160 | { | |||
2161 | ||||
2162 | if (currentNumberFormat == fNumberFormat && fSimpleNumberFormatter) { | |||
2163 | // Can use fast path | |||
2164 | // We create UFormattedNumberData ourselves to avoid a heap allocation | |||
2165 | // and corresponding free. Set the pointer to null afterwards to prevent | |||
2166 | // the implementation from attempting to free it. | |||
2167 | UErrorCode localStatus = U_ZERO_ERROR; | |||
2168 | number::impl::UFormattedNumberData data; | |||
2169 | data.quantity.setToLong(value); | |||
2170 | number::SimpleNumber number(&data, localStatus); | |||
2171 | number.setMinimumIntegerDigits(minDigits, localStatus); | |||
2172 | number.setMaximumIntegerDigits(maxDigits, localStatus); | |||
2173 | ||||
2174 | number::FormattedNumber result = fSimpleNumberFormatter->format(std::move(number), localStatus); | |||
2175 | if (U_FAILURE(localStatus)) { | |||
2176 | result.fData = nullptr; | |||
2177 | return; | |||
2178 | } | |||
2179 | UnicodeStringAppendable appendable(appendTo); | |||
2180 | result.appendTo(appendable, localStatus); | |||
2181 | result.fData = nullptr; | |||
2182 | return; | |||
2183 | } | |||
2184 | ||||
2185 | // Check for RBNF (no clone necessary) | |||
2186 | const auto* rbnf = dynamic_cast<const RuleBasedNumberFormat*>(currentNumberFormat); | |||
2187 | if (rbnf != nullptr) { | |||
2188 | FieldPosition pos(FieldPosition::DONT_CARE); | |||
2189 | rbnf->format(value, appendTo, pos); // 3rd arg is there to speed up processing | |||
2190 | return; | |||
2191 | } | |||
2192 | ||||
2193 | // Fall back to slow path (clone and mutate the NumberFormat) | |||
2194 | if (currentNumberFormat != nullptr) { | |||
2195 | FieldPosition pos(FieldPosition::DONT_CARE); | |||
2196 | LocalPointer<NumberFormat> nf(currentNumberFormat->clone()); | |||
2197 | nf->setMinimumIntegerDigits(minDigits); | |||
2198 | nf->setMaximumIntegerDigits(maxDigits); | |||
2199 | nf->format(value, appendTo, pos); // 3rd arg is there to speed up processing | |||
2200 | } | |||
2201 | } | |||
2202 | ||||
2203 | //---------------------------------------------------------------------- | |||
2204 | ||||
2205 | /** | |||
2206 | * Return true if the given format character, occurring count | |||
2207 | * times, represents a numeric field. | |||
2208 | */ | |||
2209 | UBool SimpleDateFormat::isNumeric(char16_t formatChar, int32_t count) { | |||
2210 | return DateFormatSymbols::isNumericPatternChar(formatChar, count); | |||
2211 | } | |||
2212 | ||||
2213 | UBool | |||
2214 | SimpleDateFormat::isAtNumericField(const UnicodeString &pattern, int32_t patternOffset) { | |||
2215 | if (patternOffset >= pattern.length()) { | |||
2216 | // not at any field | |||
2217 | return false; | |||
2218 | } | |||
2219 | char16_t ch = pattern.charAt(patternOffset); | |||
2220 | UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch); | |||
2221 | if (f == UDAT_FIELD_COUNT) { | |||
2222 | // not at any field | |||
2223 | return false; | |||
2224 | } | |||
2225 | int32_t i = patternOffset; | |||
2226 | while (pattern.charAt(++i) == ch) {} | |||
2227 | return DateFormatSymbols::isNumericField(f, i - patternOffset); | |||
2228 | } | |||
2229 | ||||
2230 | UBool | |||
2231 | SimpleDateFormat::isAfterNonNumericField(const UnicodeString &pattern, int32_t patternOffset) { | |||
2232 | if (patternOffset <= 0) { | |||
2233 | // not after any field | |||
2234 | return false; | |||
2235 | } | |||
2236 | char16_t ch = pattern.charAt(--patternOffset); | |||
2237 | UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch); | |||
2238 | if (f == UDAT_FIELD_COUNT) { | |||
2239 | // not after any field | |||
2240 | return false; | |||
2241 | } | |||
2242 | int32_t i = patternOffset; | |||
2243 | while (pattern.charAt(--i) == ch) {} | |||
2244 | return !DateFormatSymbols::isNumericField(f, patternOffset - i); | |||
2245 | } | |||
2246 | ||||
2247 | void | |||
2248 | SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& parsePos) const | |||
2249 | { | |||
2250 | UErrorCode status = U_ZERO_ERROR; | |||
2251 | int32_t pos = parsePos.getIndex(); | |||
2252 | if(parsePos.getIndex() < 0) { | |||
2253 | parsePos.setErrorIndex(0); | |||
2254 | return; | |||
2255 | } | |||
2256 | int32_t start = pos; | |||
2257 | ||||
2258 | // Hold the day period until everything else is parsed, because we need | |||
2259 | // the hour to interpret time correctly. | |||
2260 | int32_t dayPeriodInt = -1; | |||
2261 | ||||
2262 | UBool ambiguousYear[] = { false }; | |||
2263 | int32_t saveHebrewMonth = -1; | |||
2264 | int32_t count = 0; | |||
2265 | UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN; | |||
2266 | ||||
2267 | // For parsing abutting numeric fields. 'abutPat' is the | |||
2268 | // offset into 'pattern' of the first of 2 or more abutting | |||
2269 | // numeric fields. 'abutStart' is the offset into 'text' | |||
2270 | // where parsing the fields begins. 'abutPass' starts off as 0 | |||
2271 | // and increments each time we try to parse the fields. | |||
2272 | int32_t abutPat = -1; // If >=0, we are in a run of abutting numeric fields | |||
2273 | int32_t abutStart = 0; | |||
2274 | int32_t abutPass = 0; | |||
2275 | UBool inQuote = false; | |||
2276 | ||||
2277 | MessageFormat * numericLeapMonthFormatter = nullptr; | |||
2278 | ||||
2279 | Calendar* calClone = nullptr; | |||
2280 | Calendar *workCal = &cal; | |||
2281 | if (&cal != fCalendar && typeid(cal) != typeid(*fCalendar)) { | |||
2282 | // Different calendar type | |||
2283 | // We use the time/zone from the input calendar, but | |||
2284 | // do not use the input calendar for field calculation. | |||
2285 | calClone = fCalendar->clone(); | |||
2286 | if (calClone != nullptr) { | |||
2287 | calClone->setTime(cal.getTime(status),status); | |||
2288 | if (U_FAILURE(status)) { | |||
2289 | goto ExitParse; | |||
2290 | } | |||
2291 | calClone->setTimeZone(cal.getTimeZone()); | |||
2292 | workCal = calClone; | |||
2293 | } else { | |||
2294 | status = U_MEMORY_ALLOCATION_ERROR; | |||
2295 | goto ExitParse; | |||
2296 | } | |||
2297 | } | |||
2298 | ||||
2299 | if (fSymbols->fLeapMonthPatterns != nullptr && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) { | |||
2300 | numericLeapMonthFormatter = new MessageFormat(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric], fLocale, status); | |||
2301 | if (numericLeapMonthFormatter == nullptr) { | |||
2302 | status = U_MEMORY_ALLOCATION_ERROR; | |||
2303 | goto ExitParse; | |||
2304 | } else if (U_FAILURE(status)) { | |||
2305 | goto ExitParse; // this will delete numericLeapMonthFormatter | |||
2306 | } | |||
2307 | } | |||
2308 | ||||
2309 | for (int32_t i=0; i<fPattern.length(); ++i) { | |||
2310 | char16_t ch = fPattern.charAt(i); | |||
2311 | ||||
2312 | // Handle alphabetic field characters. | |||
2313 | if (!inQuote && isSyntaxChar(ch)) { | |||
2314 | int32_t fieldPat = i; | |||
2315 | ||||
2316 | // Count the length of this field specifier | |||
2317 | count = 1; | |||
2318 | while ((i+1)<fPattern.length() && | |||
2319 | fPattern.charAt(i+1) == ch) { | |||
2320 | ++count; | |||
2321 | ++i; | |||
2322 | } | |||
2323 | ||||
2324 | if (isNumeric(ch, count)) { | |||
2325 | if (abutPat < 0) { | |||
2326 | // Determine if there is an abutting numeric field. | |||
2327 | // Record the start of a set of abutting numeric fields. | |||
2328 | if (isAtNumericField(fPattern, i + 1)) { | |||
2329 | abutPat = fieldPat; | |||
2330 | abutStart = pos; | |||
2331 | abutPass = 0; | |||
2332 | } | |||
2333 | } | |||
2334 | } else { | |||
2335 | abutPat = -1; // End of any abutting fields | |||
2336 | } | |||
2337 | ||||
2338 | // Handle fields within a run of abutting numeric fields. Take | |||
2339 | // the pattern "HHmmss" as an example. We will try to parse | |||
2340 | // 2/2/2 characters of the input text, then if that fails, | |||
2341 | // 1/2/2. We only adjust the width of the leftmost field; the | |||
2342 | // others remain fixed. This allows "123456" => 12:34:56, but | |||
2343 | // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we | |||
2344 | // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2. | |||
2345 | if (abutPat >= 0) { | |||
2346 | // If we are at the start of a run of abutting fields, then | |||
2347 | // shorten this field in each pass. If we can't shorten | |||
2348 | // this field any more, then the parse of this set of | |||
2349 | // abutting numeric fields has failed. | |||
2350 | if (fieldPat == abutPat) { | |||
2351 | count -= abutPass++; | |||
2352 | if (count == 0) { | |||
2353 | status = U_PARSE_ERROR; | |||
2354 | goto ExitParse; | |||
2355 | } | |||
2356 | } | |||
2357 | ||||
2358 | pos = subParse(text, pos, ch, count, | |||
2359 | true, false, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType); | |||
2360 | ||||
2361 | // If the parse fails anywhere in the run, back up to the | |||
2362 | // start of the run and retry. | |||
2363 | if (pos < 0) { | |||
2364 | i = abutPat - 1; | |||
2365 | pos = abutStart; | |||
2366 | continue; | |||
2367 | } | |||
2368 | } | |||
2369 | ||||
2370 | // Handle non-numeric fields and non-abutting numeric | |||
2371 | // fields. | |||
2372 | else if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored | |||
2373 | int32_t s = subParse(text, pos, ch, count, | |||
2374 | false, true, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, &dayPeriodInt); | |||
2375 | ||||
2376 | if (s == -pos-1) { | |||
2377 | // era not present, in special cases allow this to continue | |||
2378 | // from the position where the era was expected | |||
2379 | s = pos; | |||
2380 | ||||
2381 | if (i+1 < fPattern.length()) { | |||
2382 | // move to next pattern character | |||
2383 | char16_t c = fPattern.charAt(i+1); | |||
2384 | ||||
2385 | // check for whitespace | |||
2386 | if (PatternProps::isWhiteSpace(c)) { | |||
2387 | i++; | |||
2388 | // Advance over run in pattern | |||
2389 | while ((i+1)<fPattern.length() && | |||
2390 | PatternProps::isWhiteSpace(fPattern.charAt(i+1))) { | |||
2391 | ++i; | |||
2392 | } | |||
2393 | } | |||
2394 | } | |||
2395 | } | |||
2396 | else if (s <= 0) { | |||
2397 | status = U_PARSE_ERROR; | |||
2398 | goto ExitParse; | |||
2399 | } | |||
2400 | pos = s; | |||
2401 | } | |||
2402 | } | |||
2403 | ||||
2404 | // Handle literal pattern characters. These are any | |||
2405 | // quoted characters and non-alphabetic unquoted | |||
2406 | // characters. | |||
2407 | else { | |||
2408 | ||||
2409 | abutPat = -1; // End of any abutting fields | |||
2410 | ||||
2411 | if (! matchLiterals(fPattern, i, text, pos, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status), getBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, status), isLenient())) { | |||
2412 | status = U_PARSE_ERROR; | |||
2413 | goto ExitParse; | |||
2414 | } | |||
2415 | } | |||
2416 | } | |||
2417 | ||||
2418 | // Special hack for trailing "." after non-numeric field. | |||
2419 | if (text.charAt(pos) == 0x2e && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) { | |||
2420 | // only do if the last field is not numeric | |||
2421 | if (isAfterNonNumericField(fPattern, fPattern.length())) { | |||
2422 | pos++; // skip the extra "." | |||
2423 | } | |||
2424 | } | |||
2425 | ||||
2426 | // If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm. | |||
2427 | if (dayPeriodInt >= 0) { | |||
2428 | DayPeriodRules::DayPeriod dayPeriod = static_cast<DayPeriodRules::DayPeriod>(dayPeriodInt); | |||
2429 | const DayPeriodRules *ruleSet = DayPeriodRules::getInstance(this->getSmpFmtLocale(), status); | |||
2430 | ||||
2431 | if (!cal.isSet(UCAL_HOUR) && !cal.isSet(UCAL_HOUR_OF_DAY)) { | |||
2432 | // If hour is not set, set time to the midpoint of current day period, overwriting | |||
2433 | // minutes if it's set. | |||
2434 | double midPoint = ruleSet->getMidPointForDayPeriod(dayPeriod, status); | |||
2435 | ||||
2436 | // If we can't get midPoint we do nothing. | |||
2437 | if (U_SUCCESS(status)) { | |||
2438 | // Truncate midPoint toward zero to get the hour. | |||
2439 | // Any leftover means it was a half-hour. | |||
2440 | int32_t midPointHour = static_cast<int32_t>(midPoint); | |||
2441 | int32_t midPointMinute = (midPoint - midPointHour) > 0 ? 30 : 0; | |||
2442 | ||||
2443 | // No need to set am/pm because hour-of-day is set last therefore takes precedence. | |||
2444 | cal.set(UCAL_HOUR_OF_DAY, midPointHour); | |||
2445 | cal.set(UCAL_MINUTE, midPointMinute); | |||
2446 | } | |||
2447 | } else { | |||
2448 | int hourOfDay; | |||
2449 | ||||
2450 | if (cal.isSet(UCAL_HOUR_OF_DAY)) { // Hour is parsed in 24-hour format. | |||
2451 | hourOfDay = cal.get(UCAL_HOUR_OF_DAY, status); | |||
2452 | } else { // Hour is parsed in 12-hour format. | |||
2453 | hourOfDay = cal.get(UCAL_HOUR, status); | |||
2454 | // cal.get() turns 12 to 0 for 12-hour time; change 0 to 12 | |||
2455 | // so 0 unambiguously means a 24-hour time from above. | |||
2456 | if (hourOfDay == 0) { hourOfDay = 12; } | |||
2457 | } | |||
2458 | U_ASSERT(0 <= hourOfDay && hourOfDay <= 23)(static_cast <bool> (0 <= hourOfDay && hourOfDay <= 23) ? void (0) : __assert_fail ("0 <= hourOfDay && hourOfDay <= 23" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); | |||
2459 | ||||
2460 | ||||
2461 | // If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format. | |||
2462 | if (hourOfDay == 0 || (13 <= hourOfDay && hourOfDay <= 23)) { | |||
2463 | // Make hour-of-day take precedence over (hour + am/pm) by setting it again. | |||
2464 | cal.set(UCAL_HOUR_OF_DAY, hourOfDay); | |||
2465 | } else { | |||
2466 | // We have a 12-hour time and need to choose between am and pm. | |||
2467 | // Behave as if dayPeriod spanned 6 hours each way from its center point. | |||
2468 | // This will parse correctly for consistent time + period (e.g. 10 at night) as | |||
2469 | // well as provide a reasonable recovery for inconsistent time + period (e.g. | |||
2470 | // 9 in the afternoon). | |||
2471 | ||||
2472 | // Assume current time is in the AM. | |||
2473 | // - Change 12 back to 0 for easier handling of 12am. | |||
2474 | // - Append minutes as fractional hours because e.g. 8:15 and 8:45 could be parsed | |||
2475 | // into different half-days if center of dayPeriod is at 14:30. | |||
2476 | // - cal.get(MINUTE) will return 0 if MINUTE is unset, which works. | |||
2477 | if (hourOfDay == 12) { hourOfDay = 0; } | |||
2478 | double currentHour = hourOfDay + (cal.get(UCAL_MINUTE, status)) / 60.0; | |||
2479 | double midPointHour = ruleSet->getMidPointForDayPeriod(dayPeriod, status); | |||
2480 | ||||
2481 | if (U_SUCCESS(status)) { | |||
2482 | double hoursAheadMidPoint = currentHour - midPointHour; | |||
2483 | ||||
2484 | // Assume current time is in the AM. | |||
2485 | if (-6 <= hoursAheadMidPoint && hoursAheadMidPoint < 6) { | |||
2486 | // Assumption holds; set time as such. | |||
2487 | cal.set(UCAL_AM_PM, 0); | |||
2488 | } else { | |||
2489 | cal.set(UCAL_AM_PM, 1); | |||
2490 | } | |||
2491 | } | |||
2492 | } | |||
2493 | } | |||
2494 | } | |||
2495 | ||||
2496 | // At this point the fields of Calendar have been set. Calendar | |||
2497 | // will fill in default values for missing fields when the time | |||
2498 | // is computed. | |||
2499 | ||||
2500 | parsePos.setIndex(pos); | |||
2501 | ||||
2502 | // This part is a problem: When we call parsedDate.after, we compute the time. | |||
2503 | // Take the date April 3 2004 at 2:30 am. When this is first set up, the year | |||
2504 | // will be wrong if we're parsing a 2-digit year pattern. It will be 1904. | |||
2505 | // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am | |||
2506 | // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am | |||
2507 | // on that day. It is therefore parsed out to fields as 3:30 am. Then we | |||
2508 | // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is | |||
2509 | // a Saturday, so it can have a 2:30 am -- and it should. [LIU] | |||
2510 | /* | |||
2511 | UDate parsedDate = calendar.getTime(); | |||
2512 | if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) { | |||
2513 | calendar.add(Calendar.YEAR, 100); | |||
2514 | parsedDate = calendar.getTime(); | |||
2515 | } | |||
2516 | */ | |||
2517 | // Because of the above condition, save off the fields in case we need to readjust. | |||
2518 | // The procedure we use here is not particularly efficient, but there is no other | |||
2519 | // way to do this given the API restrictions present in Calendar. We minimize | |||
2520 | // inefficiency by only performing this computation when it might apply, that is, | |||
2521 | // when the two-digit year is equal to the start year, and thus might fall at the | |||
2522 | // front or the back of the default century. This only works because we adjust | |||
2523 | // the year correctly to start with in other cases -- see subParse(). | |||
2524 | if (ambiguousYear[0] || tzTimeType != UTZFMT_TIME_TYPE_UNKNOWN) // If this is true then the two-digit year == the default start year | |||
2525 | { | |||
2526 | // We need a copy of the fields, and we need to avoid triggering a call to | |||
2527 | // complete(), which will recalculate the fields. Since we can't access | |||
2528 | // the fields[] array in Calendar, we clone the entire object. This will | |||
2529 | // stop working if Calendar.clone() is ever rewritten to call complete(). | |||
2530 | Calendar *copy; | |||
2531 | if (ambiguousYear[0]) { | |||
2532 | copy = cal.clone(); | |||
2533 | // Check for failed cloning. | |||
2534 | if (copy == nullptr) { | |||
2535 | status = U_MEMORY_ALLOCATION_ERROR; | |||
2536 | goto ExitParse; | |||
2537 | } | |||
2538 | UDate parsedDate = copy->getTime(status); | |||
2539 | // {sfb} check internalGetDefaultCenturyStart | |||
2540 | if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) { | |||
2541 | // We can't use add here because that does a complete() first. | |||
2542 | cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100); | |||
2543 | } | |||
2544 | delete copy; | |||
2545 | } | |||
2546 | ||||
2547 | if (tzTimeType != UTZFMT_TIME_TYPE_UNKNOWN) { | |||
2548 | copy = cal.clone(); | |||
2549 | // Check for failed cloning. | |||
2550 | if (copy == nullptr) { | |||
2551 | status = U_MEMORY_ALLOCATION_ERROR; | |||
2552 | goto ExitParse; | |||
2553 | } | |||
2554 | const TimeZone & tz = cal.getTimeZone(); | |||
2555 | BasicTimeZone *btz = nullptr; | |||
2556 | ||||
2557 | if (dynamic_cast<const OlsonTimeZone *>(&tz) != nullptr | |||
2558 | || dynamic_cast<const SimpleTimeZone *>(&tz) != nullptr | |||
2559 | || dynamic_cast<const RuleBasedTimeZone *>(&tz) != nullptr | |||
2560 | || dynamic_cast<const VTimeZone *>(&tz) != nullptr) { | |||
2561 | btz = (BasicTimeZone*)&tz; | |||
2562 | } | |||
2563 | ||||
2564 | // Get local millis | |||
2565 | copy->set(UCAL_ZONE_OFFSET, 0); | |||
2566 | copy->set(UCAL_DST_OFFSET, 0); | |||
2567 | UDate localMillis = copy->getTime(status); | |||
2568 | ||||
2569 | // Make sure parsed time zone type (Standard or Daylight) | |||
2570 | // matches the rule used by the parsed time zone. | |||
2571 | int32_t raw, dst; | |||
2572 | if (btz != nullptr) { | |||
2573 | if (tzTimeType == UTZFMT_TIME_TYPE_STANDARD) { | |||
2574 | btz->getOffsetFromLocal(localMillis, | |||
2575 | UCAL_TZ_LOCAL_STANDARD_FORMER, UCAL_TZ_LOCAL_STANDARD_LATTER, raw, dst, status); | |||
2576 | } else { | |||
2577 | btz->getOffsetFromLocal(localMillis, | |||
2578 | UCAL_TZ_LOCAL_DAYLIGHT_FORMER, UCAL_TZ_LOCAL_DAYLIGHT_LATTER, raw, dst, status); | |||
2579 | } | |||
2580 | } else { | |||
2581 | // No good way to resolve ambiguous time at transition, | |||
2582 | // but following code work in most case. | |||
2583 | tz.getOffset(localMillis, true, raw, dst, status); | |||
2584 | } | |||
2585 | ||||
2586 | // Now, compare the results with parsed type, either standard or daylight saving time | |||
2587 | int32_t resolvedSavings = dst; | |||
2588 | if (tzTimeType == UTZFMT_TIME_TYPE_STANDARD) { | |||
2589 | if (dst != 0) { | |||
2590 | // Override DST_OFFSET = 0 in the result calendar | |||
2591 | resolvedSavings = 0; | |||
2592 | } | |||
2593 | } else { // tztype == TZTYPE_DST | |||
2594 | if (dst == 0) { | |||
2595 | if (btz != nullptr) { | |||
2596 | // This implementation resolves daylight saving time offset | |||
2597 | // closest rule after the given time. | |||
2598 | UDate baseTime = localMillis + raw; | |||
2599 | UDate time = baseTime; | |||
2600 | UDate limit = baseTime + MAX_DAYLIGHT_DETECTION_RANGE; | |||
2601 | TimeZoneTransition trs; | |||
2602 | UBool trsAvail; | |||
2603 | ||||
2604 | // Search for DST rule after the given time | |||
2605 | while (time < limit) { | |||
2606 | trsAvail = btz->getNextTransition(time, false, trs); | |||
2607 | if (!trsAvail) { | |||
2608 | break; | |||
2609 | } | |||
2610 | resolvedSavings = trs.getTo()->getDSTSavings(); | |||
2611 | if (resolvedSavings != 0) { | |||
2612 | break; | |||
2613 | } | |||
2614 | time = trs.getTime(); | |||
2615 | } | |||
2616 | ||||
2617 | if (resolvedSavings == 0) { | |||
2618 | // If no DST rule after the given time was found, search for | |||
2619 | // DST rule before. | |||
2620 | time = baseTime; | |||
2621 | limit = baseTime - MAX_DAYLIGHT_DETECTION_RANGE; | |||
2622 | while (time > limit) { | |||
2623 | trsAvail = btz->getPreviousTransition(time, true, trs); | |||
2624 | if (!trsAvail) { | |||
2625 | break; | |||
2626 | } | |||
2627 | resolvedSavings = trs.getFrom()->getDSTSavings(); | |||
2628 | if (resolvedSavings != 0) { | |||
2629 | break; | |||
2630 | } | |||
2631 | time = trs.getTime() - 1; | |||
2632 | } | |||
2633 | ||||
2634 | if (resolvedSavings == 0) { | |||
2635 | resolvedSavings = btz->getDSTSavings(); | |||
2636 | } | |||
2637 | } | |||
2638 | } else { | |||
2639 | resolvedSavings = tz.getDSTSavings(); | |||
2640 | } | |||
2641 | if (resolvedSavings == 0) { | |||
2642 | // final fallback | |||
2643 | resolvedSavings = U_MILLIS_PER_HOUR(3600000); | |||
2644 | } | |||
2645 | } | |||
2646 | } | |||
2647 | cal.set(UCAL_ZONE_OFFSET, raw); | |||
2648 | cal.set(UCAL_DST_OFFSET, resolvedSavings); | |||
2649 | delete copy; | |||
2650 | } | |||
2651 | } | |||
2652 | ExitParse: | |||
2653 | // Set the parsed result if local calendar is used | |||
2654 | // instead of the input calendar | |||
2655 | if (U_SUCCESS(status) && workCal != &cal) { | |||
2656 | cal.setTimeZone(workCal->getTimeZone()); | |||
2657 | cal.setTime(workCal->getTime(status), status); | |||
2658 | } | |||
2659 | ||||
2660 | delete numericLeapMonthFormatter; | |||
2661 | delete calClone; | |||
2662 | ||||
2663 | // If any Calendar calls failed, we pretend that we | |||
2664 | // couldn't parse the string, when in reality this isn't quite accurate-- | |||
2665 | // we did parse it; the Calendar calls just failed. | |||
2666 | if (U_FAILURE(status)) { | |||
2667 | parsePos.setErrorIndex(pos); | |||
2668 | parsePos.setIndex(start); | |||
2669 | } | |||
2670 | } | |||
2671 | ||||
2672 | //---------------------------------------------------------------------- | |||
2673 | ||||
2674 | static int32_t | |||
2675 | matchStringWithOptionalDot(const UnicodeString &text, | |||
2676 | int32_t index, | |||
2677 | const UnicodeString &data); | |||
2678 | ||||
2679 | int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text, | |||
2680 | int32_t start, | |||
2681 | UCalendarDateFields field, | |||
2682 | const UnicodeString* data, | |||
2683 | int32_t dataCount, | |||
2684 | Calendar& cal) const | |||
2685 | { | |||
2686 | int32_t i = 0; | |||
2687 | int32_t count = dataCount; | |||
2688 | ||||
2689 | // There may be multiple strings in the data[] array which begin with | |||
2690 | // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech). | |||
2691 | // We keep track of the longest match, and return that. Note that this | |||
2692 | // unfortunately requires us to test all array elements. | |||
2693 | int32_t bestMatchLength = 0, bestMatch = -1; | |||
2694 | UnicodeString bestMatchName; | |||
2695 | ||||
2696 | for (; i < count; ++i) { | |||
2697 | int32_t matchLength = 0; | |||
2698 | if ((matchLength = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) { | |||
2699 | bestMatchLength = matchLength; | |||
2700 | bestMatch = i; | |||
2701 | } | |||
2702 | } | |||
2703 | ||||
2704 | if (bestMatch >= 0) { | |||
2705 | cal.set(field, bestMatch * 3); | |||
2706 | return start + bestMatchLength; | |||
2707 | } | |||
2708 | ||||
2709 | return -start; | |||
2710 | } | |||
2711 | ||||
2712 | int32_t SimpleDateFormat::matchDayPeriodStrings(const UnicodeString& text, int32_t start, | |||
2713 | const UnicodeString* data, int32_t dataCount, | |||
2714 | int32_t &dayPeriod) const | |||
2715 | { | |||
2716 | ||||
2717 | int32_t bestMatchLength = 0, bestMatch = -1; | |||
2718 | ||||
2719 | for (int32_t i = 0; i < dataCount; ++i) { | |||
2720 | int32_t matchLength = 0; | |||
2721 | if ((matchLength = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) { | |||
2722 | bestMatchLength = matchLength; | |||
2723 | bestMatch = i; | |||
2724 | } | |||
2725 | } | |||
2726 | ||||
2727 | if (bestMatch >= 0) { | |||
2728 | dayPeriod = bestMatch; | |||
2729 | return start + bestMatchLength; | |||
2730 | } | |||
2731 | ||||
2732 | return -start; | |||
2733 | } | |||
2734 | ||||
2735 | //---------------------------------------------------------------------- | |||
2736 | UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern, | |||
2737 | int32_t &patternOffset, | |||
2738 | const UnicodeString &text, | |||
2739 | int32_t &textOffset, | |||
2740 | UBool whitespaceLenient, | |||
2741 | UBool partialMatchLenient, | |||
2742 | UBool oldLeniency) | |||
2743 | { | |||
2744 | UBool inQuote = false; | |||
2745 | UnicodeString literal; | |||
2746 | int32_t i = patternOffset; | |||
2747 | ||||
2748 | // scan pattern looking for contiguous literal characters | |||
2749 | for ( ; i < pattern.length(); i += 1) { | |||
2750 | char16_t ch = pattern.charAt(i); | |||
2751 | ||||
2752 | if (!inQuote && isSyntaxChar(ch)) { | |||
2753 | break; | |||
2754 | } | |||
2755 | ||||
2756 | if (ch == QUOTE) { | |||
2757 | // Match a quote literal ('') inside OR outside of quotes | |||
2758 | if ((i + 1) < pattern.length() && pattern.charAt(i + 1) == QUOTE) { | |||
2759 | i += 1; | |||
2760 | } else { | |||
2761 | inQuote = !inQuote; | |||
2762 | continue; | |||
2763 | } | |||
2764 | } | |||
2765 | ||||
2766 | literal += ch; | |||
2767 | } | |||
2768 | ||||
2769 | // at this point, literal contains the literal text | |||
2770 | // and i is the index of the next non-literal pattern character. | |||
2771 | int32_t p; | |||
2772 | int32_t t = textOffset; | |||
2773 | ||||
2774 | if (whitespaceLenient) { | |||
2775 | // trim leading, trailing whitespace from | |||
2776 | // the literal text | |||
2777 | literal.trim(); | |||
2778 | ||||
2779 | // ignore any leading whitespace in the text | |||
2780 | while (t < text.length() && u_isWhitespaceu_isWhitespace_77(text.charAt(t))) { | |||
2781 | t += 1; | |||
2782 | } | |||
2783 | } | |||
2784 | ||||
2785 | for (p = 0; p < literal.length() && t < text.length();) { | |||
2786 | UBool needWhitespace = false; | |||
2787 | ||||
2788 | while (p < literal.length() && PatternProps::isWhiteSpace(literal.charAt(p))) { | |||
2789 | needWhitespace = true; | |||
2790 | p += 1; | |||
2791 | } | |||
2792 | ||||
2793 | if (needWhitespace) { | |||
2794 | int32_t tStart = t; | |||
2795 | ||||
2796 | while (t < text.length()) { | |||
2797 | char16_t tch = text.charAt(t); | |||
2798 | ||||
2799 | if (!u_isUWhiteSpaceu_isUWhiteSpace_77(tch) && !PatternProps::isWhiteSpace(tch)) { | |||
2800 | break; | |||
2801 | } | |||
2802 | ||||
2803 | t += 1; | |||
2804 | } | |||
2805 | ||||
2806 | // TODO: should we require internal spaces | |||
2807 | // in lenient mode? (There won't be any | |||
2808 | // leading or trailing spaces) | |||
2809 | if (!whitespaceLenient && t == tStart) { | |||
2810 | // didn't find matching whitespace: | |||
2811 | // an error in strict mode | |||
2812 | return false; | |||
2813 | } | |||
2814 | ||||
2815 | // In strict mode, this run of whitespace | |||
2816 | // may have been at the end. | |||
2817 | if (p >= literal.length()) { | |||
2818 | break; | |||
2819 | } | |||
2820 | } | |||
2821 | if (t >= text.length() || literal.charAt(p) != text.charAt(t)) { | |||
2822 | // Ran out of text, or found a non-matching character: | |||
2823 | // OK in lenient mode, an error in strict mode. | |||
2824 | if (whitespaceLenient) { | |||
2825 | if (t == textOffset && text.charAt(t) == 0x2e && | |||
2826 | isAfterNonNumericField(pattern, patternOffset)) { | |||
2827 | // Lenient mode and the literal input text begins with a "." and | |||
2828 | // we are after a non-numeric field: We skip the "." | |||
2829 | ++t; | |||
2830 | continue; // Do not update p. | |||
2831 | } | |||
2832 | // if it is actual whitespace and we're whitespace lenient it's OK | |||
2833 | ||||
2834 | char16_t wsc = text.charAt(t); | |||
2835 | if(PatternProps::isWhiteSpace(wsc)) { | |||
2836 | // Lenient mode and it's just whitespace we skip it | |||
2837 | ++t; | |||
2838 | continue; // Do not update p. | |||
2839 | } | |||
2840 | } | |||
2841 | // hack around oldleniency being a bit of a catch-all bucket and we're just adding support specifically for partial matches | |||
2842 | if(partialMatchLenient && oldLeniency) { | |||
2843 | break; | |||
2844 | } | |||
2845 | ||||
2846 | return false; | |||
2847 | } | |||
2848 | ++p; | |||
2849 | ++t; | |||
2850 | } | |||
2851 | ||||
2852 | // At this point if we're in strict mode we have a complete match. | |||
2853 | // If we're in lenient mode we may have a partial match, or no | |||
2854 | // match at all. | |||
2855 | if (p <= 0) { | |||
2856 | // no match. Pretend it matched a run of whitespace | |||
2857 | // and ignorables in the text. | |||
2858 | const UnicodeSet *ignorables = nullptr; | |||
2859 | UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(pattern.charAt(i)); | |||
2860 | if (patternCharIndex != UDAT_FIELD_COUNT) { | |||
2861 | ignorables = SimpleDateFormatStaticSets::getIgnorables(patternCharIndex); | |||
2862 | } | |||
2863 | ||||
2864 | for (t = textOffset; t < text.length(); t += 1) { | |||
2865 | char16_t ch = text.charAt(t); | |||
2866 | ||||
2867 | if (ignorables == nullptr || !ignorables->contains(ch)) { | |||
2868 | break; | |||
2869 | } | |||
2870 | } | |||
2871 | } | |||
2872 | ||||
2873 | // if we get here, we've got a complete match. | |||
2874 | patternOffset = i - 1; | |||
2875 | textOffset = t; | |||
2876 | ||||
2877 | return true; | |||
2878 | } | |||
2879 | ||||
2880 | //---------------------------------------------------------------------- | |||
2881 | // check both wide and abbrev months. | |||
2882 | // Does not currently handle monthPattern. | |||
2883 | // UCalendarDateFields field = UCAL_MONTH | |||
2884 | ||||
2885 | int32_t SimpleDateFormat::matchAlphaMonthStrings(const UnicodeString& text, | |||
2886 | int32_t start, | |||
2887 | const UnicodeString* wideData, | |||
2888 | const UnicodeString* shortData, | |||
2889 | int32_t dataCount, | |||
2890 | Calendar& cal) const | |||
2891 | { | |||
2892 | int32_t i; | |||
2893 | int32_t bestMatchLength = 0, bestMatch = -1; | |||
2894 | ||||
2895 | for (i = 0; i < dataCount; ++i) { | |||
2896 | int32_t matchLen = 0; | |||
2897 | if ((matchLen = matchStringWithOptionalDot(text, start, wideData[i])) > bestMatchLength) { | |||
2898 | bestMatch = i; | |||
2899 | bestMatchLength = matchLen; | |||
2900 | } | |||
2901 | } | |||
2902 | for (i = 0; i < dataCount; ++i) { | |||
2903 | int32_t matchLen = 0; | |||
2904 | if ((matchLen = matchStringWithOptionalDot(text, start, shortData[i])) > bestMatchLength) { | |||
2905 | bestMatch = i; | |||
2906 | bestMatchLength = matchLen; | |||
2907 | } | |||
2908 | } | |||
2909 | ||||
2910 | if (bestMatch >= 0) { | |||
2911 | // Adjustment for Hebrew Calendar month Adar II | |||
2912 | if (typeid(cal) == typeid(HebrewCalendar) && bestMatch==13) { | |||
2913 | cal.set(UCAL_MONTH,6); | |||
2914 | } else { | |||
2915 | cal.set(UCAL_MONTH, bestMatch); | |||
2916 | } | |||
2917 | return start + bestMatchLength; | |||
2918 | } | |||
2919 | ||||
2920 | return -start; | |||
2921 | } | |||
2922 | ||||
2923 | //---------------------------------------------------------------------- | |||
2924 | ||||
2925 | int32_t SimpleDateFormat::matchString(const UnicodeString& text, | |||
2926 | int32_t start, | |||
2927 | UCalendarDateFields field, | |||
2928 | const UnicodeString* data, | |||
2929 | int32_t dataCount, | |||
2930 | const UnicodeString* monthPattern, | |||
2931 | Calendar& cal) const | |||
2932 | { | |||
2933 | int32_t i = 0; | |||
2934 | int32_t count = dataCount; | |||
2935 | ||||
2936 | if (field == UCAL_DAY_OF_WEEK) i = 1; | |||
2937 | ||||
2938 | // There may be multiple strings in the data[] array which begin with | |||
2939 | // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech). | |||
2940 | // We keep track of the longest match, and return that. Note that this | |||
2941 | // unfortunately requires us to test all array elements. | |||
2942 | // But this does not really work for cases such as Chuvash in which | |||
2943 | // May is "ҫу" and August is "ҫурла"/"ҫур.", hence matchAlphaMonthStrings. | |||
2944 | int32_t bestMatchLength = 0, bestMatch = -1; | |||
2945 | UnicodeString bestMatchName; | |||
2946 | int32_t isLeapMonth = 0; | |||
2947 | ||||
2948 | for (; i < count; ++i) { | |||
2949 | int32_t matchLen = 0; | |||
2950 | if ((matchLen = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) { | |||
2951 | bestMatch = i; | |||
2952 | bestMatchLength = matchLen; | |||
2953 | } | |||
2954 | ||||
2955 | if (monthPattern != nullptr) { | |||
2956 | UErrorCode status = U_ZERO_ERROR; | |||
2957 | UnicodeString leapMonthName; | |||
2958 | SimpleFormatter(*monthPattern, 1, 1, status).format(data[i], leapMonthName, status); | |||
2959 | if (U_SUCCESS(status)) { | |||
2960 | if ((matchLen = matchStringWithOptionalDot(text, start, leapMonthName)) > bestMatchLength) { | |||
2961 | bestMatch = i; | |||
2962 | bestMatchLength = matchLen; | |||
2963 | isLeapMonth = 1; | |||
2964 | } | |||
2965 | } | |||
2966 | } | |||
2967 | } | |||
2968 | ||||
2969 | if (bestMatch >= 0) { | |||
2970 | if (field < UCAL_FIELD_COUNT) { | |||
2971 | // Adjustment for Hebrew Calendar month Adar II | |||
2972 | if (typeid(cal) == typeid(HebrewCalendar) && field==UCAL_MONTH && bestMatch==13) { | |||
2973 | cal.set(field,6); | |||
2974 | } else { | |||
2975 | if (field == UCAL_YEAR) { | |||
2976 | bestMatch++; // only get here for cyclic year names, which match 1-based years 1-60 | |||
2977 | } | |||
2978 | cal.set(field, bestMatch); | |||
2979 | } | |||
2980 | if (monthPattern != nullptr) { | |||
2981 | cal.set(UCAL_IS_LEAP_MONTH, isLeapMonth); | |||
2982 | } | |||
2983 | } | |||
2984 | ||||
2985 | return start + bestMatchLength; | |||
2986 | } | |||
2987 | ||||
2988 | return -start; | |||
2989 | } | |||
2990 | ||||
2991 | static int32_t | |||
2992 | matchStringWithOptionalDot(const UnicodeString &text, | |||
2993 | int32_t index, | |||
2994 | const UnicodeString &data) { | |||
2995 | UErrorCode sts = U_ZERO_ERROR; | |||
2996 | int32_t matchLenText = 0; | |||
2997 | int32_t matchLenData = 0; | |||
2998 | ||||
2999 | u_caseInsensitivePrefixMatchu_caseInsensitivePrefixMatch_77(text.getBuffer() + index, text.length() - index, | |||
3000 | data.getBuffer(), data.length(), | |||
3001 | 0 /* default case option */, | |||
3002 | &matchLenText, &matchLenData, | |||
3003 | &sts); | |||
3004 | U_ASSERT (U_SUCCESS(sts))(static_cast <bool> (U_SUCCESS(sts)) ? void (0) : __assert_fail ("U_SUCCESS(sts)", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); | |||
3005 | ||||
3006 | if (matchLenData == data.length() /* normal match */ | |||
3007 | || (data.charAt(data.length() - 1) == 0x2e | |||
3008 | && matchLenData == data.length() - 1 /* match without trailing dot */)) { | |||
3009 | return matchLenText; | |||
3010 | } | |||
3011 | ||||
3012 | return 0; | |||
3013 | } | |||
3014 | ||||
3015 | //---------------------------------------------------------------------- | |||
3016 | ||||
3017 | void | |||
3018 | SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status) | |||
3019 | { | |||
3020 | parseAmbiguousDatesAsAfter(d, status); | |||
3021 | } | |||
3022 | ||||
3023 | /** | |||
3024 | * Private member function that converts the parsed date strings into | |||
3025 | * timeFields. Returns -start (for ParsePosition) if failed. | |||
3026 | */ | |||
3027 | int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, char16_t ch, int32_t count, | |||
3028 | UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal, | |||
3029 | int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType, | |||
3030 | int32_t *dayPeriod) const | |||
3031 | { | |||
3032 | Formattable number; | |||
3033 | int32_t value = 0; | |||
3034 | int32_t i; | |||
3035 | int32_t ps = 0; | |||
3036 | UErrorCode status = U_ZERO_ERROR; | |||
3037 | ParsePosition pos(0); | |||
3038 | UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch); | |||
3039 | const NumberFormat *currentNumberFormat; | |||
3040 | UnicodeString temp; | |||
3041 | UBool gotNumber = false; | |||
3042 | ||||
3043 | #if defined (U_DEBUG_CAL) | |||
3044 | //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start); | |||
3045 | #endif | |||
3046 | ||||
3047 | if (patternCharIndex == UDAT_FIELD_COUNT) { | |||
3048 | return -start; | |||
3049 | } | |||
3050 | ||||
3051 | currentNumberFormat = getNumberFormatByIndex(patternCharIndex); | |||
3052 | if (currentNumberFormat == nullptr) { | |||
3053 | return -start; | |||
3054 | } | |||
3055 | UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; // UCAL_FIELD_COUNT if irrelevant | |||
3056 | UnicodeString hebr("hebr", 4, US_INVicu::UnicodeString::kInvariant); | |||
3057 | ||||
3058 | if (numericLeapMonthFormatter != nullptr) { | |||
3059 | numericLeapMonthFormatter->setFormats(reinterpret_cast<const Format**>(¤tNumberFormat), 1); | |||
3060 | } | |||
3061 | ||||
3062 | // If there are any spaces here, skip over them. If we hit the end | |||
3063 | // of the string, then fail. | |||
3064 | for (;;) { | |||
3065 | if (start >= text.length()) { | |||
3066 | return -start; | |||
3067 | } | |||
3068 | UChar32 c = text.char32At(start); | |||
3069 | if (!u_isUWhiteSpaceu_isUWhiteSpace_77(c) /*||*/ && !PatternProps::isWhiteSpace(c)) { | |||
3070 | break; | |||
3071 | } | |||
3072 | start += U16_LENGTH(c)((uint32_t)(c)<=0xffff ? 1 : 2); | |||
3073 | } | |||
3074 | pos.setIndex(start); | |||
3075 | ||||
3076 | UBool isChineseCalendar = typeid(cal) == typeid(ChineseCalendar) || | |||
3077 | typeid(cal) == typeid(DangiCalendar); | |||
3078 | // We handle a few special cases here where we need to parse | |||
3079 | // a number value. We handle further, more generic cases below. We need | |||
3080 | // to handle some of them here because some fields require extra processing on | |||
3081 | // the parsed value. | |||
3082 | if (patternCharIndex == UDAT_HOUR_OF_DAY1_FIELD || // k | |||
3083 | patternCharIndex == UDAT_HOUR_OF_DAY0_FIELD || // H | |||
3084 | patternCharIndex == UDAT_HOUR1_FIELD || // h | |||
3085 | patternCharIndex == UDAT_HOUR0_FIELD || // K | |||
3086 | (patternCharIndex == UDAT_DOW_LOCAL_FIELD && count <= 2) || // e | |||
3087 | (patternCharIndex == UDAT_STANDALONE_DAY_FIELD && count <= 2) || // c | |||
3088 | (patternCharIndex == UDAT_MONTH_FIELD && count <= 2) || // M | |||
3089 | (patternCharIndex == UDAT_STANDALONE_MONTH_FIELD && count <= 2) || // L | |||
3090 | (patternCharIndex == UDAT_QUARTER_FIELD && count <= 2) || // Q | |||
3091 | (patternCharIndex == UDAT_STANDALONE_QUARTER_FIELD && count <= 2) || // q | |||
3092 | patternCharIndex == UDAT_YEAR_FIELD || // y | |||
3093 | patternCharIndex == UDAT_YEAR_WOY_FIELD || // Y | |||
3094 | patternCharIndex == UDAT_YEAR_NAME_FIELD || // U (falls back to numeric) | |||
3095 | (patternCharIndex == UDAT_ERA_FIELD && isChineseCalendar) || // G | |||
3096 | patternCharIndex == UDAT_FRACTIONAL_SECOND_FIELD) // S | |||
3097 | { | |||
3098 | int32_t parseStart = pos.getIndex(); | |||
3099 | // It would be good to unify this with the obeyCount logic below, | |||
3100 | // but that's going to be difficult. | |||
3101 | const UnicodeString* src; | |||
3102 | ||||
3103 | UBool parsedNumericLeapMonth = false; | |||
3104 | if (numericLeapMonthFormatter != nullptr && (patternCharIndex == UDAT_MONTH_FIELD || patternCharIndex == UDAT_STANDALONE_MONTH_FIELD)) { | |||
3105 | int32_t argCount; | |||
3106 | Formattable * args = numericLeapMonthFormatter->parse(text, pos, argCount); | |||
3107 | if (args != nullptr && argCount == 1 && pos.getIndex() > parseStart && args[0].isNumeric()) { | |||
3108 | parsedNumericLeapMonth = true; | |||
3109 | number.setLong(args[0].getLong()); | |||
3110 | cal.set(UCAL_IS_LEAP_MONTH, 1); | |||
3111 | delete[] args; | |||
3112 | } else { | |||
3113 | pos.setIndex(parseStart); | |||
3114 | cal.set(UCAL_IS_LEAP_MONTH, 0); | |||
3115 | } | |||
3116 | } | |||
3117 | ||||
3118 | if (!parsedNumericLeapMonth) { | |||
3119 | if (obeyCount) { | |||
3120 | if ((start+count) > text.length()) { | |||
3121 | return -start; | |||
3122 | } | |||
3123 | ||||
3124 | text.extractBetween(0, start + count, temp); | |||
3125 | src = &temp; | |||
3126 | } else { | |||
3127 | src = &text; | |||
3128 | } | |||
3129 | ||||
3130 | parseInt(*src, number, pos, allowNegative,currentNumberFormat); | |||
3131 | } | |||
3132 | ||||
3133 | int32_t txtLoc = pos.getIndex(); | |||
3134 | ||||
3135 | if (txtLoc > parseStart) { | |||
3136 | value = number.getLong(); | |||
3137 | gotNumber = true; | |||
3138 | ||||
3139 | // suffix processing | |||
3140 | if (value < 0 ) { | |||
3141 | txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, true); | |||
3142 | if (txtLoc != pos.getIndex()) { | |||
3143 | value *= -1; | |||
3144 | } | |||
3145 | } | |||
3146 | else { | |||
3147 | txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, false); | |||
3148 | } | |||
3149 | ||||
3150 | if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) { | |||
3151 | // Check the range of the value | |||
3152 | int32_t bias = gFieldRangeBias[patternCharIndex]; | |||
3153 | if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) { | |||
3154 | return -start; | |||
3155 | } | |||
3156 | } | |||
3157 | ||||
3158 | pos.setIndex(txtLoc); | |||
3159 | } | |||
3160 | } | |||
3161 | ||||
3162 | // Make sure that we got a number if | |||
3163 | // we want one, and didn't get one | |||
3164 | // if we don't want one. | |||
3165 | switch (patternCharIndex) { | |||
3166 | case UDAT_HOUR_OF_DAY1_FIELD: | |||
3167 | case UDAT_HOUR_OF_DAY0_FIELD: | |||
3168 | case UDAT_HOUR1_FIELD: | |||
3169 | case UDAT_HOUR0_FIELD: | |||
3170 | // special range check for hours: | |||
3171 | if (value < 0 || value > 24) { | |||
3172 | return -start; | |||
3173 | } | |||
3174 | ||||
3175 | // fall through to gotNumber check | |||
3176 | U_FALLTHROUGH[[clang::fallthrough]]; | |||
3177 | case UDAT_YEAR_FIELD: | |||
3178 | case UDAT_YEAR_WOY_FIELD: | |||
3179 | case UDAT_FRACTIONAL_SECOND_FIELD: | |||
3180 | // these must be a number | |||
3181 | if (! gotNumber) { | |||
3182 | return -start; | |||
3183 | } | |||
3184 | ||||
3185 | break; | |||
3186 | ||||
3187 | default: | |||
3188 | // we check the rest of the fields below. | |||
3189 | break; | |||
3190 | } | |||
3191 | ||||
3192 | switch (patternCharIndex) { | |||
3193 | case UDAT_ERA_FIELD: | |||
3194 | if (isChineseCalendar) { | |||
3195 | if (!gotNumber) { | |||
3196 | return -start; | |||
3197 | } | |||
3198 | cal.set(UCAL_ERA, value); | |||
3199 | return pos.getIndex(); | |||
3200 | } | |||
3201 | if (count == 5) { | |||
3202 | ps = matchString(text, start, UCAL_ERA, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount, nullptr, cal); | |||
3203 | } else if (count == 4) { | |||
3204 | ps = matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, nullptr, cal); | |||
3205 | } else { | |||
3206 | ps = matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, nullptr, cal); | |||
3207 | } | |||
3208 | ||||
3209 | // check return position, if it equals -start, then matchString error | |||
3210 | // special case the return code so we don't necessarily fail out until we | |||
3211 | // verify no year information also | |||
3212 | if (ps == -start) | |||
3213 | ps--; | |||
3214 | ||||
3215 | return ps; | |||
3216 | ||||
3217 | case UDAT_YEAR_FIELD: | |||
3218 | // If there are 3 or more YEAR pattern characters, this indicates | |||
3219 | // that the year value is to be treated literally, without any | |||
3220 | // two-digit year adjustments (e.g., from "01" to 2001). Otherwise | |||
3221 | // we made adjustments to place the 2-digit year in the proper | |||
3222 | // century, for parsed strings from "00" to "99". Any other string | |||
3223 | // is treated literally: "2250", "-1", "1", "002". | |||
3224 | if (fDateOverride.compare(hebr)==0 && value < 1000) { | |||
3225 | value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR; | |||
3226 | } else if (text.moveIndex32(start, 2) == pos.getIndex() && !isChineseCalendar | |||
3227 | && u_isdigitu_isdigit_77(text.char32At(start)) | |||
3228 | && u_isdigitu_isdigit_77(text.char32At(text.moveIndex32(start, 1)))) | |||
3229 | { | |||
3230 | // only adjust year for patterns less than 3. | |||
3231 | if(count < 3) { | |||
3232 | // Assume for example that the defaultCenturyStart is 6/18/1903. | |||
3233 | // This means that two-digit years will be forced into the range | |||
3234 | // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02 | |||
3235 | // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond | |||
3236 | // to 1904, 1905, etc. If the year is 03, then it is 2003 if the | |||
3237 | // other fields specify a date before 6/18, or 1903 if they specify a | |||
3238 | // date afterwards. As a result, 03 is an ambiguous year. All other | |||
3239 | // two-digit years are unambiguous. | |||
3240 | if(fHaveDefaultCentury) { // check if this formatter even has a pivot year | |||
3241 | int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100; | |||
3242 | ambiguousYear[0] = (value == ambiguousTwoDigitYear); | |||
3243 | value += (fDefaultCenturyStartYear/100)*100 + | |||
3244 | (value < ambiguousTwoDigitYear ? 100 : 0); | |||
3245 | } | |||
3246 | } | |||
3247 | } | |||
3248 | cal.set(UCAL_YEAR, value); | |||
3249 | ||||
3250 | // Delayed checking for adjustment of Hebrew month numbers in non-leap years. | |||
3251 | if (saveHebrewMonth >= 0) { | |||
3252 | HebrewCalendar *hc = (HebrewCalendar*)&cal; | |||
3253 | if (!hc->isLeapYear(value) && saveHebrewMonth >= 6) { | |||
3254 | cal.set(UCAL_MONTH,saveHebrewMonth); | |||
3255 | } else { | |||
3256 | cal.set(UCAL_MONTH,saveHebrewMonth-1); | |||
3257 | } | |||
3258 | saveHebrewMonth = -1; | |||
3259 | } | |||
3260 | return pos.getIndex(); | |||
3261 | ||||
3262 | case UDAT_YEAR_WOY_FIELD: | |||
3263 | // Comment is the same as for UDAT_Year_FIELDs - look above | |||
3264 | if (fDateOverride.compare(hebr)==0 && value < 1000) { | |||
3265 | value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR; | |||
3266 | } else if (text.moveIndex32(start, 2) == pos.getIndex() | |||
3267 | && u_isdigitu_isdigit_77(text.char32At(start)) | |||
3268 | && u_isdigitu_isdigit_77(text.char32At(text.moveIndex32(start, 1))) | |||
3269 | && fHaveDefaultCentury ) | |||
3270 | { | |||
3271 | int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100; | |||
3272 | ambiguousYear[0] = (value == ambiguousTwoDigitYear); | |||
3273 | value += (fDefaultCenturyStartYear/100)*100 + | |||
3274 | (value < ambiguousTwoDigitYear ? 100 : 0); | |||
3275 | } | |||
3276 | cal.set(UCAL_YEAR_WOY, value); | |||
3277 | return pos.getIndex(); | |||
3278 | ||||
3279 | case UDAT_YEAR_NAME_FIELD: | |||
3280 | if (fSymbols->fShortYearNames != nullptr) { | |||
3281 | int32_t newStart = matchString(text, start, UCAL_YEAR, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount, nullptr, cal); | |||
3282 | if (newStart > 0) { | |||
3283 | return newStart; | |||
3284 | } | |||
3285 | } | |||
3286 | if (gotNumber && (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC,status) || value > fSymbols->fShortYearNamesCount)) { | |||
3287 | cal.set(UCAL_YEAR, value); | |||
3288 | return pos.getIndex(); | |||
3289 | } | |||
3290 | return -start; | |||
3291 | ||||
3292 | case UDAT_MONTH_FIELD: | |||
3293 | case UDAT_STANDALONE_MONTH_FIELD: | |||
3294 | if (gotNumber) // i.e., M or MM. | |||
3295 | { | |||
3296 | // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether | |||
3297 | // 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 | |||
3298 | // the year is parsed. | |||
3299 | if (typeid(cal) == typeid(HebrewCalendar)) { | |||
3300 | HebrewCalendar *hc = (HebrewCalendar*)&cal; | |||
3301 | if (cal.isSet(UCAL_YEAR)) { | |||
3302 | UErrorCode monthStatus = U_ZERO_ERROR; | |||
3303 | if (!hc->isLeapYear(hc->get(UCAL_YEAR, monthStatus)) && value >= 6) { | |||
3304 | cal.set(UCAL_MONTH, value); | |||
3305 | } else { | |||
3306 | cal.set(UCAL_MONTH, value - 1); | |||
3307 | } | |||
3308 | } else { | |||
3309 | saveHebrewMonth = value; | |||
3310 | } | |||
3311 | } else { | |||
3312 | // Don't want to parse the month if it is a string | |||
3313 | // while pattern uses numeric style: M/MM, L/LL | |||
3314 | // [We computed 'value' above.] | |||
3315 | cal.set(UCAL_MONTH, value - 1); | |||
3316 | } | |||
3317 | return pos.getIndex(); | |||
3318 | } else { | |||
3319 | // count >= 3 // i.e., MMM/MMMM, LLL/LLLL | |||
3320 | // Want to be able to parse both short and long forms. | |||
3321 | // Try count == 4 first: | |||
3322 | UnicodeString * wideMonthPat = nullptr; | |||
3323 | UnicodeString * shortMonthPat = nullptr; | |||
3324 | if (fSymbols->fLeapMonthPatterns != nullptr && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) { | |||
3325 | if (patternCharIndex==UDAT_MONTH_FIELD) { | |||
3326 | wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide]; | |||
3327 | shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev]; | |||
3328 | } else { | |||
3329 | wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide]; | |||
3330 | shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev]; | |||
3331 | } | |||
3332 | } | |||
3333 | int32_t newStart = 0; | |||
3334 | if (patternCharIndex==UDAT_MONTH_FIELD) { | |||
3335 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) && count>=3 && count <=4 && | |||
3336 | fSymbols->fLeapMonthPatterns==nullptr && fSymbols->fMonthsCount==fSymbols->fShortMonthsCount) { | |||
3337 | // single function to check both wide and short, an experiment | |||
3338 | newStart = matchAlphaMonthStrings(text, start, fSymbols->fMonths, fSymbols->fShortMonths, fSymbols->fMonthsCount, cal); // try MMMM,MMM | |||
3339 | if (newStart > 0) { | |||
3340 | return newStart; | |||
3341 | } | |||
3342 | } | |||
3343 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) { | |||
3344 | newStart = matchString(text, start, UCAL_MONTH, fSymbols->fMonths, fSymbols->fMonthsCount, wideMonthPat, cal); // try MMMM | |||
3345 | if (newStart > 0) { | |||
3346 | return newStart; | |||
3347 | } | |||
3348 | } | |||
3349 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) { | |||
3350 | newStart = matchString(text, start, UCAL_MONTH, fSymbols->fShortMonths, fSymbols->fShortMonthsCount, shortMonthPat, cal); // try MMM | |||
3351 | } | |||
3352 | } else { | |||
3353 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) && count>=3 && count <=4 && | |||
3354 | fSymbols->fLeapMonthPatterns==nullptr && fSymbols->fStandaloneMonthsCount==fSymbols->fStandaloneShortMonthsCount) { | |||
3355 | // single function to check both wide and short, an experiment | |||
3356 | newStart = matchAlphaMonthStrings(text, start, fSymbols->fStandaloneMonths, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneMonthsCount, cal); // try MMMM,MMM | |||
3357 | if (newStart > 0) { | |||
3358 | return newStart; | |||
3359 | } | |||
3360 | } | |||
3361 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) { | |||
3362 | newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, wideMonthPat, cal); // try LLLL | |||
3363 | if (newStart > 0) { | |||
3364 | return newStart; | |||
3365 | } | |||
3366 | } | |||
3367 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) { | |||
3368 | newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, shortMonthPat, cal); // try LLL | |||
3369 | } | |||
3370 | } | |||
3371 | if (newStart > 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) // currently we do not try to parse MMMMM/LLLLL: #8860 | |||
3372 | return newStart; | |||
3373 | // else we allowing parsing as number, below | |||
3374 | } | |||
3375 | break; | |||
3376 | ||||
3377 | case UDAT_HOUR_OF_DAY1_FIELD: | |||
3378 | // [We computed 'value' above.] | |||
3379 | if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1) | |||
3380 | value = 0; | |||
3381 | ||||
3382 | // fall through to set field | |||
3383 | U_FALLTHROUGH[[clang::fallthrough]]; | |||
3384 | case UDAT_HOUR_OF_DAY0_FIELD: | |||
3385 | cal.set(UCAL_HOUR_OF_DAY, value); | |||
3386 | return pos.getIndex(); | |||
3387 | ||||
3388 | case UDAT_FRACTIONAL_SECOND_FIELD: | |||
3389 | // Fractional seconds left-justify | |||
3390 | i = countDigits(text, start, pos.getIndex()); | |||
3391 | if (i < 3) { | |||
3392 | while (i < 3) { | |||
3393 | value *= 10; | |||
3394 | i++; | |||
3395 | } | |||
3396 | } else { | |||
3397 | int32_t a = 1; | |||
3398 | while (i > 3) { | |||
3399 | a *= 10; | |||
3400 | i--; | |||
3401 | } | |||
3402 | value /= a; | |||
3403 | } | |||
3404 | cal.set(UCAL_MILLISECOND, value); | |||
3405 | return pos.getIndex(); | |||
3406 | ||||
3407 | case UDAT_DOW_LOCAL_FIELD: | |||
3408 | if (gotNumber) // i.e., e or ee | |||
3409 | { | |||
3410 | // [We computed 'value' above.] | |||
3411 | cal.set(UCAL_DOW_LOCAL, value); | |||
3412 | return pos.getIndex(); | |||
3413 | } | |||
3414 | // else for eee-eeeee fall through to handling of EEE-EEEEE | |||
3415 | // fall through, do not break here | |||
3416 | U_FALLTHROUGH[[clang::fallthrough]]; | |||
3417 | case UDAT_DAY_OF_WEEK_FIELD: | |||
3418 | { | |||
3419 | // Want to be able to parse both short and long forms. | |||
3420 | // Try count == 4 (EEEE) wide first: | |||
3421 | int32_t newStart = 0; | |||
3422 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) { | |||
3423 | if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, | |||
3424 | fSymbols->fWeekdays, fSymbols->fWeekdaysCount, nullptr, cal)) > 0) | |||
3425 | return newStart; | |||
3426 | } | |||
3427 | // EEEE wide failed, now try EEE abbreviated | |||
3428 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) { | |||
3429 | if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, | |||
3430 | fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, nullptr, cal)) > 0) | |||
3431 | return newStart; | |||
3432 | } | |||
3433 | // EEE abbreviated failed, now try EEEEEE short | |||
3434 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 6) { | |||
3435 | if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, | |||
3436 | fSymbols->fShorterWeekdays, fSymbols->fShorterWeekdaysCount, nullptr, cal)) > 0) | |||
3437 | return newStart; | |||
3438 | } | |||
3439 | // EEEEEE short failed, now try EEEEE narrow | |||
3440 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) { | |||
3441 | if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, | |||
3442 | fSymbols->fNarrowWeekdays, fSymbols->fNarrowWeekdaysCount, nullptr, cal)) > 0) | |||
3443 | return newStart; | |||
3444 | } | |||
3445 | if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status) || patternCharIndex == UDAT_DAY_OF_WEEK_FIELD) | |||
3446 | return newStart; | |||
3447 | // else we allowing parsing as number, below | |||
3448 | } | |||
3449 | break; | |||
3450 | ||||
3451 | case UDAT_STANDALONE_DAY_FIELD: | |||
3452 | { | |||
3453 | if (gotNumber) // c or cc | |||
3454 | { | |||
3455 | // [We computed 'value' above.] | |||
3456 | cal.set(UCAL_DOW_LOCAL, value); | |||
3457 | return pos.getIndex(); | |||
3458 | } | |||
3459 | // Want to be able to parse both short and long forms. | |||
3460 | // Try count == 4 (cccc) first: | |||
3461 | int32_t newStart = 0; | |||
3462 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) { | |||
3463 | if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, | |||
3464 | fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, nullptr, cal)) > 0) | |||
3465 | return newStart; | |||
3466 | } | |||
3467 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) { | |||
3468 | if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, | |||
3469 | fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, nullptr, cal)) > 0) | |||
3470 | return newStart; | |||
3471 | } | |||
3472 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 6) { | |||
3473 | if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK, | |||
3474 | fSymbols->fStandaloneShorterWeekdays, fSymbols->fStandaloneShorterWeekdaysCount, nullptr, cal)) > 0) | |||
3475 | return newStart; | |||
3476 | } | |||
3477 | if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) | |||
3478 | return newStart; | |||
3479 | // else we allowing parsing as number, below | |||
3480 | } | |||
3481 | break; | |||
3482 | ||||
3483 | case UDAT_AM_PM_FIELD: | |||
3484 | { | |||
3485 | // optionally try both wide/abbrev and narrow forms | |||
3486 | int32_t newStart = 0; | |||
3487 | // try wide/abbrev | |||
3488 | if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count < 5 ) { | |||
3489 | if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, nullptr, cal)) > 0) { | |||
3490 | return newStart; | |||
3491 | } | |||
3492 | } | |||
3493 | // try narrow | |||
3494 | if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count >= 5 ) { | |||
3495 | if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fNarrowAmPms, fSymbols->fNarrowAmPmsCount, nullptr, cal)) > 0) { | |||
3496 | return newStart; | |||
3497 | } | |||
3498 | } | |||
3499 | // no matches for given options | |||
3500 | return -start; | |||
3501 | } | |||
3502 | ||||
3503 | case UDAT_HOUR1_FIELD: | |||
3504 | // [We computed 'value' above.] | |||
3505 | if (value == cal.getLeastMaximum(UCAL_HOUR)+1) | |||
3506 | value = 0; | |||
3507 | ||||
3508 | // fall through to set field | |||
3509 | U_FALLTHROUGH[[clang::fallthrough]]; | |||
3510 | case UDAT_HOUR0_FIELD: | |||
3511 | cal.set(UCAL_HOUR, value); | |||
3512 | return pos.getIndex(); | |||
3513 | ||||
3514 | case UDAT_QUARTER_FIELD: | |||
3515 | if (gotNumber) // i.e., Q or QQ. | |||
3516 | { | |||
3517 | // Don't want to parse the month if it is a string | |||
3518 | // while pattern uses numeric style: Q or QQ. | |||
3519 | // [We computed 'value' above.] | |||
3520 | cal.set(UCAL_MONTH, (value - 1) * 3); | |||
3521 | return pos.getIndex(); | |||
3522 | } else { | |||
3523 | // count >= 3 // i.e., QQQ or QQQQ | |||
3524 | // Want to be able to parse short, long, and narrow forms. | |||
3525 | // Try count == 4 first: | |||
3526 | int32_t newStart = 0; | |||
3527 | ||||
3528 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) { | |||
3529 | if ((newStart = matchQuarterString(text, start, UCAL_MONTH, | |||
3530 | fSymbols->fQuarters, fSymbols->fQuartersCount, cal)) > 0) | |||
3531 | return newStart; | |||
3532 | } | |||
3533 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) { | |||
3534 | if ((newStart = matchQuarterString(text, start, UCAL_MONTH, | |||
3535 | fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal)) > 0) | |||
3536 | return newStart; | |||
3537 | } | |||
3538 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) { | |||
3539 | if ((newStart = matchQuarterString(text, start, UCAL_MONTH, | |||
3540 | fSymbols->fNarrowQuarters, fSymbols->fNarrowQuartersCount, cal)) > 0) | |||
3541 | return newStart; | |||
3542 | } | |||
3543 | if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) | |||
3544 | return newStart; | |||
3545 | // else we allowing parsing as number, below | |||
3546 | if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status)) | |||
3547 | return -start; | |||
3548 | } | |||
3549 | break; | |||
3550 | ||||
3551 | case UDAT_STANDALONE_QUARTER_FIELD: | |||
3552 | if (gotNumber) // i.e., q or qq. | |||
3553 | { | |||
3554 | // Don't want to parse the month if it is a string | |||
3555 | // while pattern uses numeric style: q or q. | |||
3556 | // [We computed 'value' above.] | |||
3557 | cal.set(UCAL_MONTH, (value - 1) * 3); | |||
3558 | return pos.getIndex(); | |||
3559 | } else { | |||
3560 | // count >= 3 // i.e., qqq or qqqq | |||
3561 | // Want to be able to parse both short and long forms. | |||
3562 | // Try count == 4 first: | |||
3563 | int32_t newStart = 0; | |||
3564 | ||||
3565 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) { | |||
3566 | if ((newStart = matchQuarterString(text, start, UCAL_MONTH, | |||
3567 | fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount, cal)) > 0) | |||
3568 | return newStart; | |||
3569 | } | |||
3570 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) { | |||
3571 | if ((newStart = matchQuarterString(text, start, UCAL_MONTH, | |||
3572 | fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal)) > 0) | |||
3573 | return newStart; | |||
3574 | } | |||
3575 | if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) { | |||
3576 | if ((newStart = matchQuarterString(text, start, UCAL_MONTH, | |||
3577 | fSymbols->fStandaloneNarrowQuarters, fSymbols->fStandaloneNarrowQuartersCount, cal)) > 0) | |||
3578 | return newStart; | |||
3579 | } | |||
3580 | if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) | |||
3581 | return newStart; | |||
3582 | // else we allowing parsing as number, below | |||
3583 | if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status)) | |||
3584 | return -start; | |||
3585 | } | |||
3586 | break; | |||
3587 | ||||
3588 | case UDAT_TIMEZONE_FIELD: // 'z' | |||
3589 | { | |||
3590 | UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_SPECIFIC_SHORT : UTZFMT_STYLE_SPECIFIC_LONG; | |||
3591 | const TimeZoneFormat *tzfmt = tzFormat(status); | |||
3592 | if (U_SUCCESS(status)) { | |||
3593 | TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); | |||
3594 | if (tz != nullptr) { | |||
3595 | cal.adoptTimeZone(tz); | |||
3596 | return pos.getIndex(); | |||
3597 | } | |||
3598 | } | |||
3599 | return -start; | |||
3600 | } | |||
3601 | break; | |||
3602 | case UDAT_TIMEZONE_RFC_FIELD: // 'Z' | |||
3603 | { | |||
3604 | UTimeZoneFormatStyle style = (count < 4) ? | |||
3605 | UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL : ((count == 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL: UTZFMT_STYLE_LOCALIZED_GMT); | |||
3606 | const TimeZoneFormat *tzfmt = tzFormat(status); | |||
3607 | if (U_SUCCESS(status)) { | |||
3608 | TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); | |||
3609 | if (tz != nullptr) { | |||
3610 | cal.adoptTimeZone(tz); | |||
3611 | return pos.getIndex(); | |||
3612 | } | |||
3613 | } | |||
3614 | return -start; | |||
3615 | } | |||
3616 | case UDAT_TIMEZONE_GENERIC_FIELD: // 'v' | |||
3617 | { | |||
3618 | UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_GENERIC_SHORT : UTZFMT_STYLE_GENERIC_LONG; | |||
3619 | const TimeZoneFormat *tzfmt = tzFormat(status); | |||
3620 | if (U_SUCCESS(status)) { | |||
3621 | TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); | |||
3622 | if (tz != nullptr) { | |||
3623 | cal.adoptTimeZone(tz); | |||
3624 | return pos.getIndex(); | |||
3625 | } | |||
3626 | } | |||
3627 | return -start; | |||
3628 | } | |||
3629 | case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V' | |||
3630 | { | |||
3631 | UTimeZoneFormatStyle style; | |||
3632 | switch (count) { | |||
3633 | case 1: | |||
3634 | style = UTZFMT_STYLE_ZONE_ID_SHORT; | |||
3635 | break; | |||
3636 | case 2: | |||
3637 | style = UTZFMT_STYLE_ZONE_ID; | |||
3638 | break; | |||
3639 | case 3: | |||
3640 | style = UTZFMT_STYLE_EXEMPLAR_LOCATION; | |||
3641 | break; | |||
3642 | default: | |||
3643 | style = UTZFMT_STYLE_GENERIC_LOCATION; | |||
3644 | break; | |||
3645 | } | |||
3646 | const TimeZoneFormat *tzfmt = tzFormat(status); | |||
3647 | if (U_SUCCESS(status)) { | |||
3648 | TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); | |||
3649 | if (tz != nullptr) { | |||
3650 | cal.adoptTimeZone(tz); | |||
3651 | return pos.getIndex(); | |||
3652 | } | |||
3653 | } | |||
3654 | return -start; | |||
3655 | } | |||
3656 | case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O' | |||
3657 | { | |||
3658 | UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT : UTZFMT_STYLE_LOCALIZED_GMT; | |||
3659 | const TimeZoneFormat *tzfmt = tzFormat(status); | |||
3660 | if (U_SUCCESS(status)) { | |||
3661 | TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); | |||
3662 | if (tz != nullptr) { | |||
3663 | cal.adoptTimeZone(tz); | |||
3664 | return pos.getIndex(); | |||
3665 | } | |||
3666 | } | |||
3667 | return -start; | |||
3668 | } | |||
3669 | case UDAT_TIMEZONE_ISO_FIELD: // 'X' | |||
3670 | { | |||
3671 | UTimeZoneFormatStyle style; | |||
3672 | switch (count) { | |||
3673 | case 1: | |||
3674 | style = UTZFMT_STYLE_ISO_BASIC_SHORT; | |||
3675 | break; | |||
3676 | case 2: | |||
3677 | style = UTZFMT_STYLE_ISO_BASIC_FIXED; | |||
3678 | break; | |||
3679 | case 3: | |||
3680 | style = UTZFMT_STYLE_ISO_EXTENDED_FIXED; | |||
3681 | break; | |||
3682 | case 4: | |||
3683 | style = UTZFMT_STYLE_ISO_BASIC_FULL; | |||
3684 | break; | |||
3685 | default: | |||
3686 | style = UTZFMT_STYLE_ISO_EXTENDED_FULL; | |||
3687 | break; | |||
3688 | } | |||
3689 | const TimeZoneFormat *tzfmt = tzFormat(status); | |||
3690 | if (U_SUCCESS(status)) { | |||
3691 | TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); | |||
3692 | if (tz != nullptr) { | |||
3693 | cal.adoptTimeZone(tz); | |||
3694 | return pos.getIndex(); | |||
3695 | } | |||
3696 | } | |||
3697 | return -start; | |||
3698 | } | |||
3699 | case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x' | |||
3700 | { | |||
3701 | UTimeZoneFormatStyle style; | |||
3702 | switch (count) { | |||
3703 | case 1: | |||
3704 | style = UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT; | |||
3705 | break; | |||
3706 | case 2: | |||
3707 | style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED; | |||
3708 | break; | |||
3709 | case 3: | |||
3710 | style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED; | |||
3711 | break; | |||
3712 | case 4: | |||
3713 | style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL; | |||
3714 | break; | |||
3715 | default: | |||
3716 | style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL; | |||
3717 | break; | |||
3718 | } | |||
3719 | const TimeZoneFormat *tzfmt = tzFormat(status); | |||
3720 | if (U_SUCCESS(status)) { | |||
3721 | TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); | |||
3722 | if (tz != nullptr) { | |||
3723 | cal.adoptTimeZone(tz); | |||
3724 | return pos.getIndex(); | |||
3725 | } | |||
3726 | } | |||
3727 | return -start; | |||
3728 | } | |||
3729 | // currently no pattern character is defined for UDAT_TIME_SEPARATOR_FIELD | |||
3730 | // so we should not get here. Leave support in for future definition. | |||
3731 | case UDAT_TIME_SEPARATOR_FIELD: | |||
3732 | { | |||
3733 | static const char16_t def_sep = DateFormatSymbols::DEFAULT_TIME_SEPARATOR; | |||
3734 | static const char16_t alt_sep = DateFormatSymbols::ALTERNATE_TIME_SEPARATOR; | |||
3735 | ||||
3736 | // Try matching a time separator. | |||
3737 | int32_t count_sep = 1; | |||
3738 | UnicodeString data[3]; | |||
3739 | fSymbols->getTimeSeparatorString(data[0]); | |||
3740 | ||||
3741 | // Add the default, if different from the locale. | |||
3742 | if (data[0].compare(&def_sep, 1) != 0) { | |||
3743 | data[count_sep++].setTo(def_sep); | |||
3744 | } | |||
3745 | ||||
3746 | // If lenient, add also the alternate, if different from the locale. | |||
3747 | if (isLenient() && data[0].compare(&alt_sep, 1) != 0) { | |||
3748 | data[count_sep++].setTo(alt_sep); | |||
3749 | } | |||
3750 | ||||
3751 | return matchString(text, start, UCAL_FIELD_COUNT /* => nothing to set */, data, count_sep, nullptr, cal); | |||
3752 | } | |||
3753 | ||||
3754 | case UDAT_AM_PM_MIDNIGHT_NOON_FIELD: | |||
3755 | { | |||
3756 | U_ASSERT(dayPeriod != nullptr)(static_cast <bool> (dayPeriod != nullptr) ? void (0) : __assert_fail ("dayPeriod != nullptr", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); | |||
3757 | int32_t ampmStart = subParse(text, start, 0x61, count, | |||
3758 | obeyCount, allowNegative, ambiguousYear, saveHebrewMonth, cal, | |||
3759 | patLoc, numericLeapMonthFormatter, tzTimeType); | |||
3760 | ||||
3761 | if (ampmStart > 0) { | |||
3762 | return ampmStart; | |||
3763 | } else { | |||
3764 | int32_t newStart = 0; | |||
3765 | ||||
3766 | // Only match the first two strings from the day period strings array. | |||
3767 | if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) { | |||
3768 | if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fAbbreviatedDayPeriods, | |||
3769 | 2, *dayPeriod)) > 0) { | |||
3770 | return newStart; | |||
3771 | } | |||
3772 | } | |||
3773 | if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) { | |||
3774 | if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fNarrowDayPeriods, | |||
3775 | 2, *dayPeriod)) > 0) { | |||
3776 | return newStart; | |||
3777 | } | |||
3778 | } | |||
3779 | // count == 4, but allow other counts | |||
3780 | if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status)) { | |||
3781 | if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fWideDayPeriods, | |||
3782 | 2, *dayPeriod)) > 0) { | |||
3783 | return newStart; | |||
3784 | } | |||
3785 | } | |||
3786 | ||||
3787 | return -start; | |||
3788 | } | |||
3789 | } | |||
3790 | ||||
3791 | case UDAT_FLEXIBLE_DAY_PERIOD_FIELD: | |||
3792 | { | |||
3793 | U_ASSERT(dayPeriod != nullptr)(static_cast <bool> (dayPeriod != nullptr) ? void (0) : __assert_fail ("dayPeriod != nullptr", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); | |||
3794 | int32_t newStart = 0; | |||
3795 | ||||
3796 | if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) { | |||
3797 | if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fAbbreviatedDayPeriods, | |||
3798 | fSymbols->fAbbreviatedDayPeriodsCount, *dayPeriod)) > 0) { | |||
3799 | return newStart; | |||
3800 | } | |||
3801 | } | |||
3802 | if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) { | |||
3803 | if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fNarrowDayPeriods, | |||
3804 | fSymbols->fNarrowDayPeriodsCount, *dayPeriod)) > 0) { | |||
3805 | return newStart; | |||
3806 | } | |||
3807 | } | |||
3808 | if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) { | |||
3809 | if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fWideDayPeriods, | |||
3810 | fSymbols->fWideDayPeriodsCount, *dayPeriod)) > 0) { | |||
3811 | return newStart; | |||
3812 | } | |||
3813 | } | |||
3814 | ||||
3815 | return -start; | |||
3816 | } | |||
3817 | ||||
3818 | default: | |||
3819 | // Handle "generic" fields | |||
3820 | // this is now handled below, outside the switch block | |||
3821 | break; | |||
3822 | } | |||
3823 | // Handle "generic" fields: | |||
3824 | // switch default case now handled here (outside switch block) to allow | |||
3825 | // parsing of some string fields as digits for lenient case | |||
3826 | ||||
3827 | int32_t parseStart = pos.getIndex(); | |||
3828 | const UnicodeString* src; | |||
3829 | if (obeyCount) { | |||
3830 | if ((start+count) > text.length()) { | |||
3831 | return -start; | |||
3832 | } | |||
3833 | text.extractBetween(0, start + count, temp); | |||
3834 | src = &temp; | |||
3835 | } else { | |||
3836 | src = &text; | |||
3837 | } | |||
3838 | parseInt(*src, number, pos, allowNegative,currentNumberFormat); | |||
3839 | if (obeyCount && !isLenient() && pos.getIndex() < start + count) { | |||
3840 | return -start; | |||
3841 | } | |||
3842 | if (pos.getIndex() != parseStart) { | |||
3843 | int32_t val = number.getLong(); | |||
3844 | ||||
3845 | // Don't need suffix processing here (as in number processing at the beginning of the function); | |||
3846 | // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes. | |||
3847 | ||||
3848 | if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) { | |||
3849 | // Check the range of the value | |||
3850 | int32_t bias = gFieldRangeBias[patternCharIndex]; | |||
3851 | if (bias >= 0 && (val > cal.getMaximum(field) + bias || val < cal.getMinimum(field) + bias)) { | |||
3852 | return -start; | |||
3853 | } | |||
3854 | } | |||
3855 | ||||
3856 | // For the following, need to repeat some of the "if (gotNumber)" code above: | |||
3857 | // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD, | |||
3858 | // UDAT_[STANDALONE_]QUARTER_FIELD | |||
3859 | switch (patternCharIndex) { | |||
3860 | case UDAT_MONTH_FIELD: | |||
3861 | // See notes under UDAT_MONTH_FIELD case above | |||
3862 | if (typeid(cal) == typeid(HebrewCalendar)) { | |||
3863 | HebrewCalendar *hc = (HebrewCalendar*)&cal; | |||
3864 | if (cal.isSet(UCAL_YEAR)) { | |||
3865 | UErrorCode monthStatus = U_ZERO_ERROR; | |||
3866 | if (!hc->isLeapYear(hc->get(UCAL_YEAR, monthStatus)) && val >= 6) { | |||
3867 | cal.set(UCAL_MONTH, val); | |||
3868 | } else { | |||
3869 | cal.set(UCAL_MONTH, val - 1); | |||
3870 | } | |||
3871 | } else { | |||
3872 | saveHebrewMonth = val; | |||
3873 | } | |||
3874 | } else { | |||
3875 | cal.set(UCAL_MONTH, val - 1); | |||
3876 | } | |||
3877 | break; | |||
3878 | case UDAT_STANDALONE_MONTH_FIELD: | |||
3879 | cal.set(UCAL_MONTH, val - 1); | |||
3880 | break; | |||
3881 | case UDAT_DOW_LOCAL_FIELD: | |||
3882 | case UDAT_STANDALONE_DAY_FIELD: | |||
3883 | cal.set(UCAL_DOW_LOCAL, val); | |||
3884 | break; | |||
3885 | case UDAT_QUARTER_FIELD: | |||
3886 | case UDAT_STANDALONE_QUARTER_FIELD: | |||
3887 | cal.set(UCAL_MONTH, (val - 1) * 3); | |||
3888 | break; | |||
3889 | case UDAT_RELATED_YEAR_FIELD: | |||
3890 | cal.setRelatedYear(val); | |||
3891 | break; | |||
3892 | default: | |||
3893 | cal.set(field, val); | |||
3894 | break; | |||
3895 | } | |||
3896 | return pos.getIndex(); | |||
3897 | } | |||
3898 | return -start; | |||
3899 | } | |||
3900 | ||||
3901 | /** | |||
3902 | * Parse an integer using fNumberFormat. This method is semantically | |||
3903 | * const, but actually may modify fNumberFormat. | |||
3904 | */ | |||
3905 | void SimpleDateFormat::parseInt(const UnicodeString& text, | |||
3906 | Formattable& number, | |||
3907 | ParsePosition& pos, | |||
3908 | UBool allowNegative, | |||
3909 | const NumberFormat *fmt) const { | |||
3910 | parseInt(text, number, -1, pos, allowNegative,fmt); | |||
3911 | } | |||
3912 | ||||
3913 | /** | |||
3914 | * Parse an integer using fNumberFormat up to maxDigits. | |||
3915 | */ | |||
3916 | void SimpleDateFormat::parseInt(const UnicodeString& text, | |||
3917 | Formattable& number, | |||
3918 | int32_t maxDigits, | |||
3919 | ParsePosition& pos, | |||
3920 | UBool allowNegative, | |||
3921 | const NumberFormat *fmt) const { | |||
3922 | UnicodeString oldPrefix; | |||
3923 | const auto* fmtAsDF = dynamic_cast<const DecimalFormat*>(fmt); | |||
3924 | LocalPointer<DecimalFormat> df; | |||
3925 | if (!allowNegative && fmtAsDF != nullptr) { | |||
3926 | df.adoptInstead(fmtAsDF->clone()); | |||
3927 | if (df.isNull()) { | |||
3928 | // Memory allocation error | |||
3929 | return; | |||
3930 | } | |||
3931 | df->setNegativePrefix(UnicodeString(true, SUPPRESS_NEGATIVE_PREFIX, -1)); | |||
3932 | fmt = df.getAlias(); | |||
3933 | } | |||
3934 | int32_t oldPos = pos.getIndex(); | |||
3935 | fmt->parse(text, number, pos); | |||
3936 | ||||
3937 | if (maxDigits > 0) { | |||
3938 | // adjust the result to fit into | |||
3939 | // the maxDigits and move the position back | |||
3940 | int32_t nDigits = pos.getIndex() - oldPos; | |||
3941 | if (nDigits > maxDigits) { | |||
3942 | int32_t val = number.getLong(); | |||
3943 | nDigits -= maxDigits; | |||
3944 | while (nDigits > 0) { | |||
3945 | val /= 10; | |||
3946 | nDigits--; | |||
3947 | } | |||
3948 | pos.setIndex(oldPos + maxDigits); | |||
3949 | number.setLong(val); | |||
3950 | } | |||
3951 | } | |||
3952 | } | |||
3953 | ||||
3954 | int32_t SimpleDateFormat::countDigits(const UnicodeString& text, int32_t start, int32_t end) const { | |||
3955 | int32_t numDigits = 0; | |||
3956 | int32_t idx = start; | |||
3957 | while (idx < end) { | |||
3958 | UChar32 cp = text.char32At(idx); | |||
3959 | if (u_isdigitu_isdigit_77(cp)) { | |||
3960 | numDigits++; | |||
3961 | } | |||
3962 | idx += U16_LENGTH(cp)((uint32_t)(cp)<=0xffff ? 1 : 2); | |||
3963 | } | |||
3964 | return numDigits; | |||
3965 | } | |||
3966 | ||||
3967 | //---------------------------------------------------------------------- | |||
3968 | ||||
3969 | void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern, | |||
3970 | UnicodeString& translatedPattern, | |||
3971 | const UnicodeString& from, | |||
3972 | const UnicodeString& to, | |||
3973 | UErrorCode& status) | |||
3974 | { | |||
3975 | // run through the pattern and convert any pattern symbols from the version | |||
3976 | // in "from" to the corresponding character in "to". This code takes | |||
3977 | // quoted strings into account (it doesn't try to translate them), and it signals | |||
3978 | // an error if a particular "pattern character" doesn't appear in "from". | |||
3979 | // Depending on the values of "from" and "to" this can convert from generic | |||
3980 | // to localized patterns or localized to generic. | |||
3981 | if (U_FAILURE(status)) { | |||
3982 | return; | |||
3983 | } | |||
3984 | ||||
3985 | translatedPattern.remove(); | |||
3986 | UBool inQuote = false; | |||
3987 | for (int32_t i = 0; i < originalPattern.length(); ++i) { | |||
3988 | char16_t c = originalPattern[i]; | |||
3989 | if (inQuote) { | |||
3990 | if (c == QUOTE) { | |||
3991 | inQuote = false; | |||
3992 | } | |||
3993 | } else { | |||
3994 | if (c == QUOTE) { | |||
3995 | inQuote = true; | |||
3996 | } else if (isSyntaxChar(c)) { | |||
3997 | int32_t ci = from.indexOf(c); | |||
3998 | if (ci == -1) { | |||
3999 | status = U_INVALID_FORMAT_ERROR; | |||
4000 | return; | |||
4001 | } | |||
4002 | c = to[ci]; | |||
4003 | } | |||
4004 | } | |||
4005 | translatedPattern += c; | |||
4006 | } | |||
4007 | if (inQuote) { | |||
4008 | status = U_INVALID_FORMAT_ERROR; | |||
4009 | return; | |||
4010 | } | |||
4011 | } | |||
4012 | ||||
4013 | //---------------------------------------------------------------------- | |||
4014 | ||||
4015 | UnicodeString& | |||
4016 | SimpleDateFormat::toPattern(UnicodeString& result) const | |||
4017 | { | |||
4018 | result = fPattern; | |||
4019 | return result; | |||
4020 | } | |||
4021 | ||||
4022 | //---------------------------------------------------------------------- | |||
4023 | ||||
4024 | UnicodeString& | |||
4025 | SimpleDateFormat::toLocalizedPattern(UnicodeString& result, | |||
4026 | UErrorCode& status) const | |||
4027 | { | |||
4028 | translatePattern(fPattern, result, | |||
4029 | UnicodeString(DateFormatSymbols::getPatternUChars()), | |||
4030 | fSymbols->fLocalPatternChars, status); | |||
4031 | return result; | |||
4032 | } | |||
4033 | ||||
4034 | //---------------------------------------------------------------------- | |||
4035 | ||||
4036 | void | |||
4037 | SimpleDateFormat::applyPattern(const UnicodeString& pattern) | |||
4038 | { | |||
4039 | fPattern = pattern; | |||
4040 | parsePattern(); | |||
4041 | ||||
4042 | // Hack to update use of Gannen year numbering for ja@calendar=japanese - | |||
4043 | // use only if format is non-numeric (includes 年) and no other fDateOverride. | |||
4044 | if (fCalendar != nullptr && typeid(*fCalendar) == typeid(JapaneseCalendar) && | |||
4045 | uprv_strcmp(fLocale.getLanguage(),"ja"):: strcmp(fLocale.getLanguage(), "ja") == 0) { | |||
4046 | if (fDateOverride==UnicodeString(u"y=jpanyear") && !fHasHanYearChar) { | |||
4047 | // Gannen numbering is set but new pattern should not use it, unset; | |||
4048 | // use procedure from adoptNumberFormat to clear overrides | |||
4049 | if (fSharedNumberFormatters) { | |||
4050 | freeSharedNumberFormatters(fSharedNumberFormatters); | |||
4051 | fSharedNumberFormatters = nullptr; | |||
4052 | } | |||
4053 | fDateOverride.setToBogus(); // record status | |||
4054 | } else if (fDateOverride.isBogus() && fHasHanYearChar) { | |||
4055 | // No current override (=> no Gannen numbering) but new pattern needs it; | |||
4056 | // use procedures from initNUmberFormatters / adoptNumberFormat | |||
4057 | umtx_lockumtx_lock_77(&LOCK); | |||
4058 | if (fSharedNumberFormatters == nullptr) { | |||
4059 | fSharedNumberFormatters = allocSharedNumberFormatters(); | |||
4060 | } | |||
4061 | umtx_unlockumtx_unlock_77(&LOCK); | |||
4062 | if (fSharedNumberFormatters != nullptr) { | |||
4063 | Locale ovrLoc(fLocale.getLanguage(),fLocale.getCountry(),fLocale.getVariant(),"numbers=jpanyear"); | |||
4064 | UErrorCode status = U_ZERO_ERROR; | |||
4065 | const SharedNumberFormat *snf = createSharedNumberFormat(ovrLoc, status); | |||
4066 | if (U_SUCCESS(status)) { | |||
4067 | // Now that we have an appropriate number formatter, fill in the | |||
4068 | // appropriate slot in the number formatters table. | |||
4069 | UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(u'y'); | |||
4070 | SharedObject::copyPtr(snf, fSharedNumberFormatters[patternCharIndex]); | |||
4071 | snf->deleteIfZeroRefCount(); | |||
4072 | fDateOverride.setTo(u"y=jpanyear", -1); // record status | |||
4073 | } | |||
4074 | } | |||
4075 | } | |||
4076 | } | |||
4077 | } | |||
4078 | ||||
4079 | //---------------------------------------------------------------------- | |||
4080 | ||||
4081 | void | |||
4082 | SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern, | |||
4083 | UErrorCode &status) | |||
4084 | { | |||
4085 | translatePattern(pattern, fPattern, | |||
4086 | fSymbols->fLocalPatternChars, | |||
4087 | UnicodeString(DateFormatSymbols::getPatternUChars()), status); | |||
4088 | } | |||
4089 | ||||
4090 | //---------------------------------------------------------------------- | |||
4091 | ||||
4092 | const DateFormatSymbols* | |||
4093 | SimpleDateFormat::getDateFormatSymbols() const | |||
4094 | { | |||
4095 | return fSymbols; | |||
4096 | } | |||
4097 | ||||
4098 | //---------------------------------------------------------------------- | |||
4099 | ||||
4100 | void | |||
4101 | SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols* newFormatSymbols) | |||
4102 | { | |||
4103 | delete fSymbols; | |||
4104 | fSymbols = newFormatSymbols; | |||
4105 | } | |||
4106 | ||||
4107 | //---------------------------------------------------------------------- | |||
4108 | void | |||
4109 | SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols) | |||
4110 | { | |||
4111 | delete fSymbols; | |||
4112 | fSymbols = new DateFormatSymbols(newFormatSymbols); | |||
4113 | } | |||
4114 | ||||
4115 | //---------------------------------------------------------------------- | |||
4116 | const TimeZoneFormat* | |||
4117 | SimpleDateFormat::getTimeZoneFormat() const { | |||
4118 | // TimeZoneFormat initialization might fail when out of memory. | |||
4119 | // If we always initialize TimeZoneFormat instance, we can return | |||
4120 | // such status there. For now, this implementation lazily instantiates | |||
4121 | // a TimeZoneFormat for performance optimization reasons, but cannot | |||
4122 | // propagate such error (probably just out of memory case) to the caller. | |||
4123 | UErrorCode status = U_ZERO_ERROR; | |||
4124 | return (const TimeZoneFormat*)tzFormat(status); | |||
4125 | } | |||
4126 | ||||
4127 | //---------------------------------------------------------------------- | |||
4128 | void | |||
4129 | SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat* timeZoneFormatToAdopt) | |||
4130 | { | |||
4131 | delete fTimeZoneFormat; | |||
4132 | fTimeZoneFormat = timeZoneFormatToAdopt; | |||
4133 | } | |||
4134 | ||||
4135 | //---------------------------------------------------------------------- | |||
4136 | void | |||
4137 | SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat& newTimeZoneFormat) | |||
4138 | { | |||
4139 | delete fTimeZoneFormat; | |||
4140 | fTimeZoneFormat = new TimeZoneFormat(newTimeZoneFormat); | |||
4141 | } | |||
4142 | ||||
4143 | //---------------------------------------------------------------------- | |||
4144 | ||||
4145 | ||||
4146 | void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt) | |||
4147 | { | |||
4148 | UErrorCode status = U_ZERO_ERROR; | |||
4149 | Locale calLocale(fLocale); | |||
4150 | calLocale.setKeywordValue("calendar", calendarToAdopt->getType(), status); | |||
4151 | DateFormatSymbols *newSymbols = | |||
4152 | DateFormatSymbols::createForLocale(calLocale, status); | |||
4153 | if (U_FAILURE(status)) { | |||
4154 | delete calendarToAdopt; | |||
4155 | return; | |||
4156 | } | |||
4157 | DateFormat::adoptCalendar(calendarToAdopt); | |||
4158 | delete fSymbols; | |||
4159 | fSymbols = newSymbols; | |||
4160 | initializeDefaultCentury(); // we need a new century (possibly) | |||
4161 | } | |||
4162 | ||||
4163 | ||||
4164 | //---------------------------------------------------------------------- | |||
4165 | ||||
4166 | ||||
4167 | // override the DateFormat implementation in order to | |||
4168 | // lazily initialize fCapitalizationBrkIter | |||
4169 | void | |||
4170 | SimpleDateFormat::setContext(UDisplayContext value, UErrorCode& status) | |||
4171 | { | |||
4172 | DateFormat::setContext(value, status); | |||
4173 | #if !UCONFIG_NO_BREAK_ITERATION1 | |||
4174 | if (U_SUCCESS(status)) { | |||
4175 | if ( fCapitalizationBrkIter == nullptr && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || | |||
4176 | value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE) ) { | |||
4177 | status = U_ZERO_ERROR; | |||
4178 | fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status); | |||
4179 | if (U_FAILURE(status)) { | |||
4180 | delete fCapitalizationBrkIter; | |||
4181 | fCapitalizationBrkIter = nullptr; | |||
4182 | } | |||
4183 | } | |||
4184 | } | |||
4185 | #endif | |||
4186 | } | |||
4187 | ||||
4188 | ||||
4189 | //---------------------------------------------------------------------- | |||
4190 | ||||
4191 | ||||
4192 | UBool | |||
4193 | SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field) const { | |||
4194 | return isFieldUnitIgnored(fPattern, field); | |||
4195 | } | |||
4196 | ||||
4197 | ||||
4198 | UBool | |||
4199 | SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern, | |||
4200 | UCalendarDateFields field) { | |||
4201 | int32_t fieldLevel = fgCalendarFieldToLevel[field]; | |||
4202 | int32_t level; | |||
4203 | char16_t ch; | |||
4204 | UBool inQuote = false; | |||
4205 | char16_t prevCh = 0; | |||
4206 | int32_t count = 0; | |||
4207 | ||||
4208 | for (int32_t i = 0; i < pattern.length(); ++i) { | |||
4209 | ch = pattern[i]; | |||
4210 | if (ch != prevCh && count > 0) { | |||
4211 | level = getLevelFromChar(prevCh); | |||
4212 | // the larger the level, the smaller the field unit. | |||
4213 | if (fieldLevel <= level) { | |||
4214 | return false; | |||
4215 | } | |||
4216 | count = 0; | |||
4217 | } | |||
4218 | if (ch == QUOTE) { | |||
4219 | if ((i+1) < pattern.length() && pattern[i+1] == QUOTE) { | |||
4220 | ++i; | |||
4221 | } else { | |||
4222 | inQuote = ! inQuote; | |||
4223 | } | |||
4224 | } | |||
4225 | else if (!inQuote && isSyntaxChar(ch)) { | |||
4226 | prevCh = ch; | |||
4227 | ++count; | |||
4228 | } | |||
4229 | } | |||
4230 | if (count > 0) { | |||
4231 | // last item | |||
4232 | level = getLevelFromChar(prevCh); | |||
4233 | if (fieldLevel <= level) { | |||
4234 | return false; | |||
4235 | } | |||
4236 | } | |||
4237 | return true; | |||
4238 | } | |||
4239 | ||||
4240 | //---------------------------------------------------------------------- | |||
4241 | ||||
4242 | const Locale& | |||
4243 | SimpleDateFormat::getSmpFmtLocale() const { | |||
4244 | return fLocale; | |||
4245 | } | |||
4246 | ||||
4247 | //---------------------------------------------------------------------- | |||
4248 | ||||
4249 | int32_t | |||
4250 | SimpleDateFormat::checkIntSuffix(const UnicodeString& text, int32_t start, | |||
4251 | int32_t patLoc, UBool isNegative) const { | |||
4252 | // local variables | |||
4253 | UnicodeString suf; | |||
4254 | int32_t patternMatch; | |||
4255 | int32_t textPreMatch; | |||
4256 | int32_t textPostMatch; | |||
4257 | ||||
4258 | // check that we are still in range | |||
4259 | if ( (start > text.length()) || | |||
4260 | (start < 0) || | |||
4261 | (patLoc < 0) || | |||
4262 | (patLoc > fPattern.length())) { | |||
4263 | // out of range, don't advance location in text | |||
4264 | return start; | |||
4265 | } | |||
4266 | ||||
4267 | // get the suffix | |||
4268 | DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fNumberFormat); | |||
4269 | if (decfmt != nullptr) { | |||
4270 | if (isNegative) { | |||
4271 | suf = decfmt->getNegativeSuffix(suf); | |||
4272 | } | |||
4273 | else { | |||
4274 | suf = decfmt->getPositiveSuffix(suf); | |||
4275 | } | |||
4276 | } | |||
4277 | ||||
4278 | // check for suffix | |||
4279 | if (suf.length() <= 0) { | |||
4280 | return start; | |||
4281 | } | |||
4282 | ||||
4283 | // check suffix will be encountered in the pattern | |||
4284 | patternMatch = compareSimpleAffix(suf,fPattern,patLoc); | |||
4285 | ||||
4286 | // check if a suffix will be encountered in the text | |||
4287 | textPreMatch = compareSimpleAffix(suf,text,start); | |||
4288 | ||||
4289 | // check if a suffix was encountered in the text | |||
4290 | textPostMatch = compareSimpleAffix(suf,text,start-suf.length()); | |||
4291 | ||||
4292 | // check for suffix match | |||
4293 | if ((textPreMatch >= 0) && (patternMatch >= 0) && (textPreMatch == patternMatch)) { | |||
4294 | return start; | |||
4295 | } | |||
4296 | else if ((textPostMatch >= 0) && (patternMatch >= 0) && (textPostMatch == patternMatch)) { | |||
4297 | return start - suf.length(); | |||
4298 | } | |||
4299 | ||||
4300 | // should not get here | |||
4301 | return start; | |||
4302 | } | |||
4303 | ||||
4304 | //---------------------------------------------------------------------- | |||
4305 | ||||
4306 | int32_t | |||
4307 | SimpleDateFormat::compareSimpleAffix(const UnicodeString& affix, | |||
4308 | const UnicodeString& input, | |||
4309 | int32_t pos) const { | |||
4310 | int32_t start = pos; | |||
4311 | for (int32_t i=0; i<affix.length(); ) { | |||
4312 | UChar32 c = affix.char32At(i); | |||
4313 | int32_t len = U16_LENGTH(c)((uint32_t)(c)<=0xffff ? 1 : 2); | |||
4314 | if (PatternProps::isWhiteSpace(c)) { | |||
4315 | // We may have a pattern like: \u200F \u0020 | |||
4316 | // and input text like: \u200F \u0020 | |||
4317 | // Note that U+200F and U+0020 are Pattern_White_Space but only | |||
4318 | // U+0020 is UWhiteSpace. So we have to first do a direct | |||
4319 | // match of the run of Pattern_White_Space in the pattern, | |||
4320 | // then match any extra characters. | |||
4321 | UBool literalMatch = false; | |||
4322 | while (pos < input.length() && | |||
4323 | input.char32At(pos) == c) { | |||
4324 | literalMatch = true; | |||
4325 | i += len; | |||
4326 | pos += len; | |||
4327 | if (i == affix.length()) { | |||
4328 | break; | |||
4329 | } | |||
4330 | c = affix.char32At(i); | |||
4331 | len = U16_LENGTH(c)((uint32_t)(c)<=0xffff ? 1 : 2); | |||
4332 | if (!PatternProps::isWhiteSpace(c)) { | |||
4333 | break; | |||
4334 | } | |||
4335 | } | |||
4336 | ||||
4337 | // Advance over run in pattern | |||
4338 | i = skipPatternWhiteSpace(affix, i); | |||
4339 | ||||
4340 | // Advance over run in input text | |||
4341 | // Must see at least one white space char in input, | |||
4342 | // unless we've already matched some characters literally. | |||
4343 | int32_t s = pos; | |||
4344 | pos = skipUWhiteSpace(input, pos); | |||
4345 | if (pos == s && !literalMatch) { | |||
4346 | return -1; | |||
4347 | } | |||
4348 | ||||
4349 | // If we skip UWhiteSpace in the input text, we need to skip it in the pattern. | |||
4350 | // Otherwise, the previous lines may have skipped over text (such as U+00A0) that | |||
4351 | // is also in the affix. | |||
4352 | i = skipUWhiteSpace(affix, i); | |||
4353 | } else { | |||
4354 | if (pos < input.length() && | |||
4355 | input.char32At(pos) == c) { | |||
4356 | i += len; | |||
4357 | pos += len; | |||
4358 | } else { | |||
4359 | return -1; | |||
4360 | } | |||
4361 | } | |||
4362 | } | |||
4363 | return pos - start; | |||
4364 | } | |||
4365 | ||||
4366 | //---------------------------------------------------------------------- | |||
4367 | ||||
4368 | int32_t | |||
4369 | SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) const { | |||
4370 | const char16_t* s = text.getBuffer(); | |||
4371 | return static_cast<int32_t>(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s); | |||
4372 | } | |||
4373 | ||||
4374 | //---------------------------------------------------------------------- | |||
4375 | ||||
4376 | int32_t | |||
4377 | SimpleDateFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) const { | |||
4378 | while (pos < text.length()) { | |||
4379 | UChar32 c = text.char32At(pos); | |||
4380 | if (!u_isUWhiteSpaceu_isUWhiteSpace_77(c)) { | |||
4381 | break; | |||
4382 | } | |||
4383 | pos += U16_LENGTH(c)((uint32_t)(c)<=0xffff ? 1 : 2); | |||
4384 | } | |||
4385 | return pos; | |||
4386 | } | |||
4387 | ||||
4388 | //---------------------------------------------------------------------- | |||
4389 | ||||
4390 | // Lazy TimeZoneFormat instantiation, semantically const. | |||
4391 | TimeZoneFormat * | |||
4392 | SimpleDateFormat::tzFormat(UErrorCode &status) const { | |||
4393 | Mutex m(&LOCK); | |||
4394 | if (fTimeZoneFormat == nullptr && U_SUCCESS(status)) { | |||
4395 | const_cast<SimpleDateFormat *>(this)->fTimeZoneFormat = | |||
4396 | TimeZoneFormat::createInstance(fLocale, status); | |||
4397 | } | |||
4398 | return fTimeZoneFormat; | |||
4399 | } | |||
4400 | ||||
4401 | void SimpleDateFormat::parsePattern() { | |||
4402 | fHasMinute = false; | |||
4403 | fHasSecond = false; | |||
4404 | fHasHanYearChar = false; | |||
4405 | ||||
4406 | int len = fPattern.length(); | |||
4407 | UBool inQuote = false; | |||
4408 | for (int32_t i = 0; i < len; ++i) { | |||
4409 | char16_t ch = fPattern[i]; | |||
4410 | if (ch == QUOTE) { | |||
4411 | inQuote = !inQuote; | |||
4412 | } | |||
4413 | if (ch == 0x5E74) { // don't care whether this is inside quotes | |||
4414 | fHasHanYearChar = true; | |||
4415 | } | |||
4416 | if (!inQuote) { | |||
4417 | if (ch == 0x6D) { // 0x6D == 'm' | |||
4418 | fHasMinute = true; | |||
4419 | } | |||
4420 | if (ch == 0x73) { // 0x73 == 's' | |||
4421 | fHasSecond = true; | |||
4422 | } | |||
4423 | } | |||
4424 | } | |||
4425 | } | |||
4426 | ||||
4427 | U_NAMESPACE_END} | |||
4428 | ||||
4429 | #endif /* #if !UCONFIG_NO_FORMATTING */ | |||
4430 | ||||
4431 | //eof |