1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2007-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
11 *******************************************************************************
14 #include "unicode/utypes.h"
15 #if !UCONFIG_NO_FORMATTING
17 #include "unicode/datefmt.h"
18 #include "unicode/decimfmt.h"
19 #include "unicode/dtfmtsym.h"
20 #include "unicode/dtptngen.h"
21 #include "unicode/localpointer.h"
22 #include "unicode/schriter.h"
23 #include "unicode/simpleformatter.h"
24 #include "unicode/smpdtfmt.h"
25 #include "unicode/udat.h"
26 #include "unicode/udatpg.h"
27 #include "unicode/uniset.h"
28 #include "unicode/uloc.h"
29 #include "unicode/ures.h"
30 #include "unicode/ustring.h"
31 #include "unicode/rep.h"
32 #include "unicode/region.h"
42 #include "dtptngen_impl.h"
47 #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
49 * If we are on EBCDIC, use an iterator which will
50 * traverse the bundles in ASCII order.
52 #define U_USE_ASCII_BUNDLE_ITERATOR
53 #define U_SORT_ASCII_BUNDLE_ITERATOR
56 #if defined(U_USE_ASCII_BUNDLE_ITERATOR)
58 #include "unicode/ustring.h"
63 UResourceBundle
*item
;
66 struct UResourceBundleAIterator
{
67 UResourceBundle
*bund
;
73 /* Must be C linkage to pass function pointer to the sort function */
77 static int32_t U_CALLCONV
78 ures_a_codepointSort(const void *context
, const void *left
, const void *right
) {
79 //CompareContext *cmp=(CompareContext *)context;
80 return u_strcmp(((const UResAEntry
*)left
)->key
,
81 ((const UResAEntry
*)right
)->key
);
86 static void ures_a_open(UResourceBundleAIterator
*aiter
, UResourceBundle
*bund
, UErrorCode
*status
) {
87 if(U_FAILURE(*status
)) {
91 aiter
->num
= ures_getSize(aiter
->bund
);
93 #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
94 aiter
->entries
= nullptr;
96 aiter
->entries
= (UResAEntry
*)uprv_malloc(sizeof(UResAEntry
)*aiter
->num
);
97 for(int i
=0;i
<aiter
->num
;i
++) {
98 aiter
->entries
[i
].item
= ures_getByIndex(aiter
->bund
, i
, nullptr, status
);
99 const char *akey
= ures_getKey(aiter
->entries
[i
].item
);
100 int32_t len
= uprv_strlen(akey
)+1;
101 aiter
->entries
[i
].key
= (UChar
*)uprv_malloc(len
*sizeof(UChar
));
102 u_charsToUChars(akey
, aiter
->entries
[i
].key
, len
);
104 uprv_sortArray(aiter
->entries
, aiter
->num
, sizeof(UResAEntry
), ures_a_codepointSort
, nullptr, TRUE
, status
);
108 static void ures_a_close(UResourceBundleAIterator
*aiter
) {
109 #if defined(U_SORT_ASCII_BUNDLE_ITERATOR)
110 for(int i
=0;i
<aiter
->num
;i
++) {
111 uprv_free(aiter
->entries
[i
].key
);
112 ures_close(aiter
->entries
[i
].item
);
117 static const UChar
*ures_a_getNextString(UResourceBundleAIterator
*aiter
, int32_t *len
, const char **key
, UErrorCode
*err
) {
118 #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
119 return ures_getNextString(aiter
->bund
, len
, key
, err
);
121 if(U_FAILURE(*err
)) return nullptr;
122 UResourceBundle
*item
= aiter
->entries
[aiter
->cursor
].item
;
123 const UChar
* ret
= ures_getString(item
, len
, err
);
124 *key
= ures_getKey(item
);
136 // *****************************************************************************
137 // class DateTimePatternGenerator
138 // *****************************************************************************
139 static const UChar Canonical_Items
[] = {
141 CAP_G
, LOW_Y
, CAP_Q
, CAP_M
, LOW_W
, CAP_W
, CAP_E
,
142 CAP_D
, CAP_F
, LOW_D
, LOW_A
, // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
143 CAP_H
, LOW_M
, LOW_S
, CAP_S
, LOW_V
, 0
146 static const dtTypeElem dtTypes
[] = {
147 // patternChar, field, type, minLen, weight
148 {CAP_G
, UDATPG_ERA_FIELD
, DT_SHORT
, 1, 3,},
149 {CAP_G
, UDATPG_ERA_FIELD
, DT_LONG
, 4, 0},
150 {CAP_G
, UDATPG_ERA_FIELD
, DT_NARROW
, 5, 0},
152 {LOW_Y
, UDATPG_YEAR_FIELD
, DT_NUMERIC
, 1, 20},
153 {CAP_Y
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 20},
154 {LOW_U
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ 2*DT_DELTA
, 1, 20},
155 {LOW_R
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ 3*DT_DELTA
, 1, 20},
156 {CAP_U
, UDATPG_YEAR_FIELD
, DT_SHORT
, 1, 3},
157 {CAP_U
, UDATPG_YEAR_FIELD
, DT_LONG
, 4, 0},
158 {CAP_U
, UDATPG_YEAR_FIELD
, DT_NARROW
, 5, 0},
160 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_NUMERIC
, 1, 2},
161 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_SHORT
, 3, 0},
162 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_LONG
, 4, 0},
163 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_NARROW
, 5, 0},
164 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2},
165 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
166 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
167 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
169 {CAP_M
, UDATPG_MONTH_FIELD
, DT_NUMERIC
, 1, 2},
170 {CAP_M
, UDATPG_MONTH_FIELD
, DT_SHORT
, 3, 0},
171 {CAP_M
, UDATPG_MONTH_FIELD
, DT_LONG
, 4, 0},
172 {CAP_M
, UDATPG_MONTH_FIELD
, DT_NARROW
, 5, 0},
173 {CAP_L
, UDATPG_MONTH_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2},
174 {CAP_L
, UDATPG_MONTH_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
175 {CAP_L
, UDATPG_MONTH_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
176 {CAP_L
, UDATPG_MONTH_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
177 {LOW_L
, UDATPG_MONTH_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 1},
179 {LOW_W
, UDATPG_WEEK_OF_YEAR_FIELD
, DT_NUMERIC
, 1, 2},
181 {CAP_W
, UDATPG_WEEK_OF_MONTH_FIELD
, DT_NUMERIC
, 1, 0},
183 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
, 1, 3},
184 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_LONG
, 4, 0},
185 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
, 5, 0},
186 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORTER
, 6, 0},
187 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_NUMERIC
+ 2*DT_DELTA
, 1, 2},
188 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
- 2*DT_DELTA
, 3, 0},
189 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_LONG
- 2*DT_DELTA
, 4, 0},
190 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
- 2*DT_DELTA
, 5, 0},
191 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_SHORTER
- 2*DT_DELTA
, 6, 0},
192 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2}, // LOW_E is currently not used in CLDR data, should not be canonical
193 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
194 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
195 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
196 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORTER
- DT_DELTA
, 6, 0},
198 {LOW_D
, UDATPG_DAY_FIELD
, DT_NUMERIC
, 1, 2},
199 {LOW_G
, UDATPG_DAY_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 20}, // really internal use, so we don't care
201 {CAP_D
, UDATPG_DAY_OF_YEAR_FIELD
, DT_NUMERIC
, 1, 3},
203 {CAP_F
, UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD
, DT_NUMERIC
, 1, 0},
205 {LOW_A
, UDATPG_DAYPERIOD_FIELD
, DT_SHORT
, 1, 3},
206 {LOW_A
, UDATPG_DAYPERIOD_FIELD
, DT_LONG
, 4, 0},
207 {LOW_A
, UDATPG_DAYPERIOD_FIELD
, DT_NARROW
, 5, 0},
208 {LOW_B
, UDATPG_DAYPERIOD_FIELD
, DT_SHORT
- DT_DELTA
, 1, 3},
209 {LOW_B
, UDATPG_DAYPERIOD_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
210 {LOW_B
, UDATPG_DAYPERIOD_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
211 // b needs to be closer to a than to B, so we make this 3*DT_DELTA
212 {CAP_B
, UDATPG_DAYPERIOD_FIELD
, DT_SHORT
- 3*DT_DELTA
, 1, 3},
213 {CAP_B
, UDATPG_DAYPERIOD_FIELD
, DT_LONG
- 3*DT_DELTA
, 4, 0},
214 {CAP_B
, UDATPG_DAYPERIOD_FIELD
, DT_NARROW
- 3*DT_DELTA
, 5, 0},
216 {CAP_H
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 10*DT_DELTA
, 1, 2}, // 24 hour
217 {LOW_K
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 11*DT_DELTA
, 1, 2}, // 24 hour
218 {LOW_H
, UDATPG_HOUR_FIELD
, DT_NUMERIC
, 1, 2}, // 12 hour
219 {CAP_K
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2}, // 12 hour
220 // The C code has had versions of the following 3, keep & update. Should not need these, but...
221 // Without these, certain tests using e.g. staticGetSkeleton fail because j/J in patterns
222 // get skipped instead of mapped to the right hour chars, for example in
223 // DateFormatTest::TestPatternFromSkeleton
224 // IntlTestDateTimePatternGeneratorAPI:: testStaticGetSkeleton
225 // DateIntervalFormatTest::testTicket11985
226 // Need to investigate better handling of jJC replacement e.g. in staticGetSkeleton.
227 {CAP_J
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 5*DT_DELTA
, 1, 2}, // 12/24 hour no AM/PM
228 {LOW_J
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 6*DT_DELTA
, 1, 6}, // 12/24 hour
229 {CAP_C
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 7*DT_DELTA
, 1, 6}, // 12/24 hour with preferred dayPeriods for 12
231 {LOW_M
, UDATPG_MINUTE_FIELD
, DT_NUMERIC
, 1, 2},
233 {LOW_S
, UDATPG_SECOND_FIELD
, DT_NUMERIC
, 1, 2},
234 {CAP_A
, UDATPG_SECOND_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 1000},
236 {CAP_S
, UDATPG_FRACTIONAL_SECOND_FIELD
, DT_NUMERIC
, 1, 1000},
238 {LOW_V
, UDATPG_ZONE_FIELD
, DT_SHORT
- 2*DT_DELTA
, 1, 0},
239 {LOW_V
, UDATPG_ZONE_FIELD
, DT_LONG
- 2*DT_DELTA
, 4, 0},
240 {LOW_Z
, UDATPG_ZONE_FIELD
, DT_SHORT
, 1, 3},
241 {LOW_Z
, UDATPG_ZONE_FIELD
, DT_LONG
, 4, 0},
242 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 3},
243 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
244 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 5, 0},
245 {CAP_O
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 1, 0},
246 {CAP_O
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
247 {CAP_V
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 1, 0},
248 {CAP_V
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 2, 0},
249 {CAP_V
, UDATPG_ZONE_FIELD
, DT_LONG
-1 - DT_DELTA
, 3, 0},
250 {CAP_V
, UDATPG_ZONE_FIELD
, DT_LONG
-2 - DT_DELTA
, 4, 0},
251 {CAP_X
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 0},
252 {CAP_X
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 2, 0},
253 {CAP_X
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
254 {LOW_X
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 0},
255 {LOW_X
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 2, 0},
256 {LOW_X
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
258 {0, UDATPG_FIELD_COUNT
, 0, 0, 0} , // last row of dtTypes[]
261 static const char* const CLDR_FIELD_APPEND
[] = {
262 "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week",
263 "*", "*", "Day", "*", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
264 "Hour", "Minute", "Second", "*", "Timezone"
267 static const char* const CLDR_FIELD_NAME
[UDATPG_FIELD_COUNT
] = {
268 "era", "year", "quarter", "month", "week", "weekOfMonth", "weekday",
269 "dayOfYear", "weekdayOfMonth", "day", "dayperiod", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
270 "hour", "minute", "second", "*", "zone"
273 static const char* const CLDR_FIELD_WIDTH
[] = { // [UDATPG_WIDTH_COUNT]
274 "", "-short", "-narrow"
277 // TODO(ticket:13619): remove when definition uncommented in dtptngen.h.
278 static const int32_t UDATPG_WIDTH_COUNT
= UDATPG_NARROW
+ 1;
279 static constexpr UDateTimePGDisplayWidth UDATPG_WIDTH_APPENDITEM
= UDATPG_WIDE
;
280 static constexpr int32_t UDATPG_FIELD_KEY_MAX
= 24; // max length of CLDR field tag (type + width)
283 static const UChar UDATPG_ItemFormat
[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A,
284 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524
286 //static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ"
288 static const char DT_DateTimePatternsTag
[]="DateTimePatterns";
289 static const char DT_DateTimeCalendarTag
[]="calendar";
290 static const char DT_DateTimeGregorianTag
[]="gregorian";
291 static const char DT_DateTimeAppendItemsTag
[]="appendItems";
292 static const char DT_DateTimeFieldsTag
[]="fields";
293 static const char DT_DateTimeAvailableFormatsTag
[]="availableFormats";
294 //static const UnicodeString repeatedPattern=UnicodeString(repeatedPatterns);
296 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator
)
297 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration
)
298 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration
)
300 DateTimePatternGenerator
* U_EXPORT2
301 DateTimePatternGenerator::createInstance(UErrorCode
& status
) {
302 return createInstance(Locale::getDefault(), status
);
305 DateTimePatternGenerator
* U_EXPORT2
306 DateTimePatternGenerator::createInstance(const Locale
& locale
, UErrorCode
& status
, UBool skipICUData
) {
307 if (U_FAILURE(status
)) {
310 LocalPointer
<DateTimePatternGenerator
> result(
311 new DateTimePatternGenerator(locale
, status
, skipICUData
), status
);
312 return U_SUCCESS(status
) ? result
.orphan() : nullptr;
315 DateTimePatternGenerator
* U_EXPORT2
316 DateTimePatternGenerator::createEmptyInstance(UErrorCode
& status
) {
317 if (U_FAILURE(status
)) {
320 LocalPointer
<DateTimePatternGenerator
> result(
321 new DateTimePatternGenerator(status
), status
);
322 return U_SUCCESS(status
) ? result
.orphan() : nullptr;
325 DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode
&status
) :
326 skipMatcher(nullptr),
327 fAvailableFormatKeyHash(nullptr),
328 internalErrorCode(U_ZERO_ERROR
)
330 fp
= new FormatParser();
331 dtMatcher
= new DateTimeMatcher();
332 distanceInfo
= new DistanceInfo();
333 patternMap
= new PatternMap();
334 if (fp
== nullptr || dtMatcher
== nullptr || distanceInfo
== nullptr || patternMap
== nullptr) {
335 internalErrorCode
= status
= U_MEMORY_ALLOCATION_ERROR
;
339 DateTimePatternGenerator::DateTimePatternGenerator(const Locale
& locale
, UErrorCode
&status
, UBool skipICUData
) :
340 skipMatcher(nullptr),
341 fAvailableFormatKeyHash(nullptr),
342 internalErrorCode(U_ZERO_ERROR
),
345 fp
= new FormatParser();
346 dtMatcher
= new DateTimeMatcher();
347 distanceInfo
= new DistanceInfo();
348 patternMap
= new PatternMap();
349 if (fp
== nullptr || dtMatcher
== nullptr || distanceInfo
== nullptr || patternMap
== nullptr) {
350 internalErrorCode
= status
= U_MEMORY_ALLOCATION_ERROR
;
353 initData(locale
, status
, skipICUData
);
357 DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator
& other
) :
359 skipMatcher(nullptr),
360 fAvailableFormatKeyHash(nullptr),
361 internalErrorCode(U_ZERO_ERROR
)
363 fp
= new FormatParser();
364 dtMatcher
= new DateTimeMatcher();
365 distanceInfo
= new DistanceInfo();
366 patternMap
= new PatternMap();
367 if (fp
== nullptr || dtMatcher
== nullptr || distanceInfo
== nullptr || patternMap
== nullptr) {
368 internalErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
373 DateTimePatternGenerator
&
374 DateTimePatternGenerator::operator=(const DateTimePatternGenerator
& other
) {
376 if (&other
== this) {
379 internalErrorCode
= other
.internalErrorCode
;
380 pLocale
= other
.pLocale
;
381 fDefaultHourFormatChar
= other
.fDefaultHourFormatChar
;
383 dtMatcher
->copyFrom(other
.dtMatcher
->skeleton
);
384 *distanceInfo
= *(other
.distanceInfo
);
385 dateTimeFormat
= other
.dateTimeFormat
;
386 decimal
= other
.decimal
;
387 // NUL-terminate for the C API.
388 dateTimeFormat
.getTerminatedBuffer();
389 decimal
.getTerminatedBuffer();
391 if ( other
.skipMatcher
== nullptr ) {
392 skipMatcher
= nullptr;
395 skipMatcher
= new DateTimeMatcher(*other
.skipMatcher
);
396 if (skipMatcher
== nullptr)
398 internalErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
402 for (int32_t i
=0; i
< UDATPG_FIELD_COUNT
; ++i
) {
403 appendItemFormats
[i
] = other
.appendItemFormats
[i
];
404 appendItemFormats
[i
].getTerminatedBuffer(); // NUL-terminate for the C API.
405 for (int32_t j
=0; j
< UDATPG_WIDTH_COUNT
; ++j
) {
406 fieldDisplayNames
[i
][j
] = other
.fieldDisplayNames
[i
][j
];
407 fieldDisplayNames
[i
][j
].getTerminatedBuffer(); // NUL-terminate for the C API.
410 patternMap
->copyFrom(*other
.patternMap
, internalErrorCode
);
411 copyHashtable(other
.fAvailableFormatKeyHash
, internalErrorCode
);
417 DateTimePatternGenerator::operator==(const DateTimePatternGenerator
& other
) const {
418 if (this == &other
) {
421 if ((pLocale
==other
.pLocale
) && (patternMap
->equals(*other
.patternMap
)) &&
422 (dateTimeFormat
==other
.dateTimeFormat
) && (decimal
==other
.decimal
)) {
423 for ( int32_t i
=0 ; i
<UDATPG_FIELD_COUNT
; ++i
) {
424 if (appendItemFormats
[i
] != other
.appendItemFormats
[i
]) {
427 for (int32_t j
=0; j
< UDATPG_WIDTH_COUNT
; ++j
) {
428 if (fieldDisplayNames
[i
][j
] != other
.fieldDisplayNames
[i
][j
]) {
441 DateTimePatternGenerator::operator!=(const DateTimePatternGenerator
& other
) const {
442 return !operator==(other
);
445 DateTimePatternGenerator::~DateTimePatternGenerator() {
446 if (fAvailableFormatKeyHash
!=nullptr) {
447 delete fAvailableFormatKeyHash
;
450 if (fp
!= nullptr) delete fp
;
451 if (dtMatcher
!= nullptr) delete dtMatcher
;
452 if (distanceInfo
!= nullptr) delete distanceInfo
;
453 if (patternMap
!= nullptr) delete patternMap
;
454 if (skipMatcher
!= nullptr) delete skipMatcher
;
459 UInitOnce initOnce
= U_INITONCE_INITIALIZER
;
460 UHashtable
*localeToAllowedHourFormatsMap
= nullptr;
462 // Value deleter for hashmap.
463 U_CFUNC
void U_CALLCONV
deleteAllowedHourFormats(void *ptr
) {
467 // Close hashmap at cleanup.
468 U_CFUNC UBool U_CALLCONV
allowedHourFormatsCleanup() {
469 uhash_close(localeToAllowedHourFormatsMap
);
473 enum AllowedHourFormat
{
474 ALLOWED_HOUR_FORMAT_UNKNOWN
= -1,
475 ALLOWED_HOUR_FORMAT_h
,
476 ALLOWED_HOUR_FORMAT_H
,
477 ALLOWED_HOUR_FORMAT_K
, // Added ICU-20383, used by JP
478 ALLOWED_HOUR_FORMAT_k
, // Added ICU-20383, not currently used
479 ALLOWED_HOUR_FORMAT_hb
,
480 ALLOWED_HOUR_FORMAT_hB
,
481 ALLOWED_HOUR_FORMAT_Kb
, // Added ICU-20383, not currently used
482 ALLOWED_HOUR_FORMAT_KB
, // Added ICU-20383, not currently used
483 // ICU-20383 The following are unlikely and not currently used
484 ALLOWED_HOUR_FORMAT_Hb
,
485 ALLOWED_HOUR_FORMAT_HB
491 DateTimePatternGenerator::initData(const Locale
& locale
, UErrorCode
&status
, UBool skipICUData
) {
492 //const char *baseLangName = locale.getBaseName(); // unused
494 skipMatcher
= nullptr;
495 fAvailableFormatKeyHash
=nullptr;
496 addCanonicalItems(status
);
498 addICUPatterns(locale
, status
); // skip to prevent circular dependency when called from SimpleDateFormat::construct
500 addCLDRData(locale
, status
);
501 setDateTimeFromCalendar(locale
, status
);
502 setDecimalSymbols(locale
, status
);
503 umtx_initOnce(initOnce
, loadAllowedHourFormatsData
, status
);
504 getAllowedHourFormats(locale
, status
);
505 // If any of the above methods failed then the object is in an invalid state.
506 internalErrorCode
= status
;
507 } // DateTimePatternGenerator::initData
511 struct AllowedHourFormatsSink
: public ResourceSink
{
512 // Initialize sub-sinks.
513 AllowedHourFormatsSink() {}
514 virtual ~AllowedHourFormatsSink();
516 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
517 UErrorCode
&errorCode
) {
518 ResourceTable timeData
= value
.getTable(errorCode
);
519 if (U_FAILURE(errorCode
)) { return; }
520 for (int32_t i
= 0; timeData
.getKeyAndValue(i
, key
, value
); ++i
) {
521 const char *regionOrLocale
= key
;
522 ResourceTable formatList
= value
.getTable(errorCode
);
523 if (U_FAILURE(errorCode
)) { return; }
524 // below we construct a list[] that has an entry for the "preferred" value at [0],
525 // followed by 1 or more entries for the "allowed" values, terminated with an
526 // entry for ALLOWED_HOUR_FORMAT_UNKNOWN (not included in length below)
527 LocalMemory
<int32_t> list
;
529 int32_t preferredFormat
= ALLOWED_HOUR_FORMAT_UNKNOWN
;
530 for (int32_t j
= 0; formatList
.getKeyAndValue(j
, key
, value
); ++j
) {
531 if (uprv_strcmp(key
, "allowed") == 0) {
532 if (value
.getType() == URES_STRING
) {
533 length
= 2; // 1 preferred to add later, 1 allowed to add now
534 if (list
.allocateInsteadAndReset(length
+ 1) == nullptr) {
535 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
538 list
[1] = getHourFormatFromUnicodeString(value
.getUnicodeString(errorCode
));
541 ResourceArray allowedFormats
= value
.getArray(errorCode
);
542 length
= allowedFormats
.getSize() + 1; // 1 preferred, getSize allowed
543 if (list
.allocateInsteadAndReset(length
+ 1) == nullptr) {
544 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
547 for (int32_t k
= 1; k
< length
; ++k
) {
548 allowedFormats
.getValue(k
-1, value
);
549 list
[k
] = getHourFormatFromUnicodeString(value
.getUnicodeString(errorCode
));
552 } else if (uprv_strcmp(key
, "preferred") == 0) {
553 preferredFormat
= getHourFormatFromUnicodeString(value
.getUnicodeString(errorCode
));
557 list
[0] = (preferredFormat
!=ALLOWED_HOUR_FORMAT_UNKNOWN
)? preferredFormat
: list
[1];
559 // fallback handling for missing data
560 length
= 2; // 1 preferred, 1 allowed
561 if (list
.allocateInsteadAndReset(length
+ 1) == nullptr) {
562 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
565 list
[0] = (preferredFormat
!=ALLOWED_HOUR_FORMAT_UNKNOWN
)? preferredFormat
: ALLOWED_HOUR_FORMAT_H
;
568 list
[length
] = ALLOWED_HOUR_FORMAT_UNKNOWN
;
569 // At this point list[] will have at least two non-ALLOWED_HOUR_FORMAT_UNKNOWN entries,
570 // followed by ALLOWED_HOUR_FORMAT_UNKNOWN.
571 uhash_put(localeToAllowedHourFormatsMap
, const_cast<char *>(regionOrLocale
), list
.orphan(), &errorCode
);
572 if (U_FAILURE(errorCode
)) { return; }
576 AllowedHourFormat
getHourFormatFromUnicodeString(const UnicodeString
&s
) {
577 if (s
.length() == 1) {
578 if (s
[0] == LOW_H
) { return ALLOWED_HOUR_FORMAT_h
; }
579 if (s
[0] == CAP_H
) { return ALLOWED_HOUR_FORMAT_H
; }
580 if (s
[0] == CAP_K
) { return ALLOWED_HOUR_FORMAT_K
; }
581 if (s
[0] == LOW_K
) { return ALLOWED_HOUR_FORMAT_k
; }
582 } else if (s
.length() == 2) {
583 if (s
[0] == LOW_H
&& s
[1] == LOW_B
) { return ALLOWED_HOUR_FORMAT_hb
; }
584 if (s
[0] == LOW_H
&& s
[1] == CAP_B
) { return ALLOWED_HOUR_FORMAT_hB
; }
585 if (s
[0] == CAP_K
&& s
[1] == LOW_B
) { return ALLOWED_HOUR_FORMAT_Kb
; }
586 if (s
[0] == CAP_K
&& s
[1] == CAP_B
) { return ALLOWED_HOUR_FORMAT_KB
; }
587 if (s
[0] == CAP_H
&& s
[1] == LOW_B
) { return ALLOWED_HOUR_FORMAT_Hb
; }
588 if (s
[0] == CAP_H
&& s
[1] == CAP_B
) { return ALLOWED_HOUR_FORMAT_HB
; }
591 return ALLOWED_HOUR_FORMAT_UNKNOWN
;
597 AllowedHourFormatsSink::~AllowedHourFormatsSink() {}
599 U_CFUNC
void U_CALLCONV
DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode
&status
) {
600 if (U_FAILURE(status
)) { return; }
601 localeToAllowedHourFormatsMap
= uhash_open(
602 uhash_hashChars
, uhash_compareChars
, nullptr, &status
);
603 if (U_FAILURE(status
)) { return; }
605 uhash_setValueDeleter(localeToAllowedHourFormatsMap
, deleteAllowedHourFormats
);
606 ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS
, allowedHourFormatsCleanup
);
608 LocalUResourceBundlePointer
rb(ures_openDirect(nullptr, "supplementalData", &status
));
609 if (U_FAILURE(status
)) { return; }
611 AllowedHourFormatsSink sink
;
612 // TODO: Currently in the enumeration each table allocates a new array.
613 // Try to reduce the number of memory allocations. Consider storing a
614 // UVector32 with the concatenation of all of the sub-arrays, put the start index
615 // into the hashmap, store 6 single-value sub-arrays right at the beginning of the
616 // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime
617 // object. Remember to clean up the vector, too.
618 ures_getAllItemsWithFallback(rb
.getAlias(), "timeData", sink
, status
);
621 static int32_t* getAllowedHourFormatsLangCountry(const char* language
, const char* country
, UErrorCode
& status
) {
622 CharString langCountry
;
623 langCountry
.append(language
, status
);
624 langCountry
.append('_', status
);
625 langCountry
.append(country
, status
);
627 int32_t* allowedFormats
;
628 allowedFormats
= (int32_t *)uhash_get(localeToAllowedHourFormatsMap
, langCountry
.data());
629 if (allowedFormats
== nullptr) {
630 allowedFormats
= (int32_t *)uhash_get(localeToAllowedHourFormatsMap
, const_cast<char *>(country
));
633 return allowedFormats
;
636 void DateTimePatternGenerator::getAllowedHourFormats(const Locale
&locale
, UErrorCode
&status
) {
637 if (U_FAILURE(status
)) { return; }
639 const char *language
= locale
.getLanguage();
640 const char *country
= locale
.getCountry();
641 const char *locName
= locale
.getName(); // Apple addition
642 if (*locName
==0 || uprv_strcmp(locName
,"root")==0 || uprv_strcmp(locName
,"und")==0) { // Apple addition
646 Locale maxLocale
; // must be here for correct lifetime
647 if (*language
== '\0' || *country
== '\0') {
649 UErrorCode localStatus
= U_ZERO_ERROR
;
650 maxLocale
.addLikelySubtags(localStatus
);
651 if (U_SUCCESS(localStatus
)) {
652 language
= maxLocale
.getLanguage();
653 country
= maxLocale
.getCountry();
656 if (*language
== '\0') {
657 // Unexpected, but fail gracefully
660 if (*country
== '\0') {
664 int32_t* allowedFormats
= getAllowedHourFormatsLangCountry(language
, country
, status
);
666 // Check if the region has an alias
667 if (allowedFormats
== nullptr) {
668 UErrorCode localStatus
= U_ZERO_ERROR
;
669 const Region
* region
= Region::getInstance(country
, localStatus
);
670 if (U_SUCCESS(localStatus
)) {
671 country
= region
->getRegionCode(); // the real region code
672 allowedFormats
= getAllowedHourFormatsLangCountry(language
, country
, status
);
676 if (allowedFormats
!= nullptr) { // Lookup is successful
677 // Here allowedFormats points to a list consisting of key for preferredFormat,
678 // followed by one or more keys for allowedFormats, then followed by ALLOWED_HOUR_FORMAT_UNKNOWN.
679 switch (allowedFormats
[0]) {
680 case ALLOWED_HOUR_FORMAT_h
: fDefaultHourFormatChar
= LOW_H
; break;
681 case ALLOWED_HOUR_FORMAT_H
: fDefaultHourFormatChar
= CAP_H
; break;
682 case ALLOWED_HOUR_FORMAT_K
: fDefaultHourFormatChar
= CAP_K
; break;
683 case ALLOWED_HOUR_FORMAT_k
: fDefaultHourFormatChar
= LOW_K
; break;
684 default: fDefaultHourFormatChar
= CAP_H
; break;
686 for (int32_t i
= 0; i
< UPRV_LENGTHOF(fAllowedHourFormats
); ++i
) {
687 fAllowedHourFormats
[i
] = allowedFormats
[i
+ 1];
688 if (fAllowedHourFormats
[i
] == ALLOWED_HOUR_FORMAT_UNKNOWN
) {
692 } else { // Lookup failed, twice
693 fDefaultHourFormatChar
= CAP_H
;
694 fAllowedHourFormats
[0] = ALLOWED_HOUR_FORMAT_H
;
695 fAllowedHourFormats
[1] = ALLOWED_HOUR_FORMAT_UNKNOWN
;
700 DateTimePatternGenerator::getSkeleton(const UnicodeString
& pattern
, UErrorCode
&
703 DateTimeMatcher matcher
;
704 PtnSkeleton localSkeleton
;
705 matcher
.set(pattern
, &fp2
, localSkeleton
);
706 return localSkeleton
.getSkeleton();
710 DateTimePatternGenerator::staticGetSkeleton(
711 const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
713 DateTimeMatcher matcher
;
714 PtnSkeleton localSkeleton
;
715 matcher
.set(pattern
, &fp
, localSkeleton
);
716 return localSkeleton
.getSkeleton();
720 DateTimePatternGenerator::getBaseSkeleton(const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
722 DateTimeMatcher matcher
;
723 PtnSkeleton localSkeleton
;
724 matcher
.set(pattern
, &fp2
, localSkeleton
);
725 return localSkeleton
.getBaseSkeleton();
729 DateTimePatternGenerator::staticGetBaseSkeleton(
730 const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
732 DateTimeMatcher matcher
;
733 PtnSkeleton localSkeleton
;
734 matcher
.set(pattern
, &fp
, localSkeleton
);
735 return localSkeleton
.getBaseSkeleton();
739 DateTimePatternGenerator::addICUPatterns(const Locale
& locale
, UErrorCode
& status
) {
740 if (U_FAILURE(status
)) { return; }
741 UnicodeString dfPattern
;
742 UnicodeString conflictingString
;
745 // Load with ICU patterns
746 for (int32_t i
=DateFormat::kFull
; i
<=DateFormat::kShort
; i
++) {
747 DateFormat::EStyle style
= (DateFormat::EStyle
)i
;
748 df
= DateFormat::createDateInstance(style
, locale
);
749 SimpleDateFormat
* sdf
;
750 if (df
!= nullptr && (sdf
= dynamic_cast<SimpleDateFormat
*>(df
)) != nullptr) {
751 sdf
->toPattern(dfPattern
);
752 addPattern(dfPattern
, FALSE
, conflictingString
, status
);
754 // TODO Maybe we should return an error when the date format isn't simple.
756 if (U_FAILURE(status
)) { return; }
758 df
= DateFormat::createTimeInstance(style
, locale
);
759 if (df
!= nullptr && (sdf
= dynamic_cast<SimpleDateFormat
*>(df
)) != nullptr) {
760 sdf
->toPattern(dfPattern
);
761 addPattern(dfPattern
, FALSE
, conflictingString
, status
);
763 // TODO: C++ and Java are inconsistent (see #12568).
764 // C++ uses MEDIUM, but Java uses SHORT.
765 if ( i
==DateFormat::kShort
&& !dfPattern
.isEmpty() ) {
766 consumeShortTimePattern(dfPattern
, status
);
769 // TODO Maybe we should return an error when the date format isn't simple.
771 if (U_FAILURE(status
)) { return; }
776 DateTimePatternGenerator::hackTimes(const UnicodeString
& hackPattern
, UErrorCode
& status
) {
777 UnicodeString conflictingString
;
779 fp
->set(hackPattern
);
782 for (int32_t i
=0; i
<fp
->itemNumber
; ++i
) {
783 UnicodeString field
= fp
->items
[i
];
784 if ( fp
->isQuoteLiteral(field
) ) {
786 UnicodeString quoteLiteral
;
787 fp
->getQuoteLiteral(quoteLiteral
, &i
);
788 mmss
+= quoteLiteral
;
792 if (fp
->isPatternSeparator(field
) && gotMm
) {
796 UChar ch
=field
.charAt(0);
807 addPattern(mmss
, FALSE
, conflictingString
, status
);
811 if (gotMm
|| ch
==LOW_Z
|| ch
==CAP_Z
|| ch
==LOW_V
|| ch
==CAP_V
) {
821 #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
824 DateTimePatternGenerator::getCalendarTypeToUse(const Locale
& locale
, CharString
& destination
, UErrorCode
& err
) {
825 destination
.clear().append(DT_DateTimeGregorianTag
, -1, err
); // initial default
826 if ( U_SUCCESS(err
) ) {
827 // HACK to maintain backward compatibility with old behavior-- the Calendar API used to return "japanese"
828 // as the default calendar for ja_JP_TRADTITIONAL, but now it returns "gregorian".
829 // But ures_getFunctionalEquivalent(), which this function used to call, still returned "japanese". To keep
830 // the old unit tests passing, we special-case this situation to still return "japanese" for ja_JP_TRADITIONAL.
831 if (uprv_strcmp(locale
.getName(), "ja_JP_TRADITIONAL") == 0) {
832 destination
.clear().append("japanese", -1, err
);
835 Calendar::getCalendarTypeFromLocale(locale
, calType
, 50, err
);
836 if (U_SUCCESS(err
)) {
837 destination
.clear().append(calType
, -1, err
);
844 DateTimePatternGenerator::consumeShortTimePattern(const UnicodeString
& shortTimePattern
,
845 UErrorCode
& status
) {
846 if (U_FAILURE(status
)) { return; }
847 // ICU-20383 No longer set fDefaultHourFormatChar to the hour format character from
848 // this pattern; instead it is set from localeToAllowedHourFormatsMap which now
849 // includes entries for both preferred and allowed formats.
852 hackTimes(shortTimePattern
, status
);
855 struct DateTimePatternGenerator::AppendItemFormatsSink
: public ResourceSink
{
857 // Destination for data, modified via setters.
858 DateTimePatternGenerator
& dtpg
;
860 AppendItemFormatsSink(DateTimePatternGenerator
& _dtpg
) : dtpg(_dtpg
) {}
861 virtual ~AppendItemFormatsSink();
863 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
864 UErrorCode
&errorCode
) {
865 ResourceTable itemsTable
= value
.getTable(errorCode
);
866 if (U_FAILURE(errorCode
)) { return; }
867 for (int32_t i
= 0; itemsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
868 UDateTimePatternField field
= dtpg
.getAppendFormatNumber(key
);
869 if (field
== UDATPG_FIELD_COUNT
) { continue; }
870 const UnicodeString
& valueStr
= value
.getUnicodeString(errorCode
);
871 if (dtpg
.getAppendItemFormat(field
).isEmpty() && !valueStr
.isEmpty()) {
872 dtpg
.setAppendItemFormat(field
, valueStr
);
877 void fillInMissing() {
878 UnicodeString
defaultItemFormat(TRUE
, UDATPG_ItemFormat
, UPRV_LENGTHOF(UDATPG_ItemFormat
)-1); // Read-only alias.
879 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; i
++) {
880 UDateTimePatternField field
= (UDateTimePatternField
)i
;
881 if (dtpg
.getAppendItemFormat(field
).isEmpty()) {
882 dtpg
.setAppendItemFormat(field
, defaultItemFormat
);
888 struct DateTimePatternGenerator::AppendItemNamesSink
: public ResourceSink
{
890 // Destination for data, modified via setters.
891 DateTimePatternGenerator
& dtpg
;
893 AppendItemNamesSink(DateTimePatternGenerator
& _dtpg
) : dtpg(_dtpg
) {}
894 virtual ~AppendItemNamesSink();
896 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
897 UErrorCode
&errorCode
) {
898 ResourceTable itemsTable
= value
.getTable(errorCode
);
899 if (U_FAILURE(errorCode
)) { return; }
900 for (int32_t i
= 0; itemsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
901 UDateTimePGDisplayWidth width
;
902 UDateTimePatternField field
= dtpg
.getFieldAndWidthIndices(key
, &width
);
903 if (field
== UDATPG_FIELD_COUNT
) { continue; }
904 ResourceTable detailsTable
= value
.getTable(errorCode
);
905 if (U_FAILURE(errorCode
)) { return; }
906 for (int32_t j
= 0; detailsTable
.getKeyAndValue(j
, key
, value
); ++j
) {
907 if (uprv_strcmp(key
, "dn") != 0) { continue; }
908 const UnicodeString
& valueStr
= value
.getUnicodeString(errorCode
);
909 if (dtpg
.getFieldDisplayName(field
,width
).isEmpty() && !valueStr
.isEmpty()) {
910 dtpg
.setFieldDisplayName(field
,width
,valueStr
);
917 void fillInMissing() {
918 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; i
++) {
919 UnicodeString
& valueStr
= dtpg
.getMutableFieldDisplayName((UDateTimePatternField
)i
, UDATPG_WIDE
);
920 if (valueStr
.isEmpty()) {
925 valueStr
+= (UChar
)(i
+0x30);
928 valueStr
+= (UChar
)0x31;
929 valueStr
+= (UChar
)(i
-10 + 0x30);
931 // NUL-terminate for the C API.
932 valueStr
.getTerminatedBuffer();
934 for (int32_t j
= 1; j
< UDATPG_WIDTH_COUNT
; j
++) {
935 UnicodeString
& valueStr2
= dtpg
.getMutableFieldDisplayName((UDateTimePatternField
)i
, (UDateTimePGDisplayWidth
)j
);
936 if (valueStr2
.isEmpty()) {
937 valueStr2
= dtpg
.getFieldDisplayName((UDateTimePatternField
)i
, (UDateTimePGDisplayWidth
)(j
-1));
944 struct DateTimePatternGenerator::AvailableFormatsSink
: public ResourceSink
{
946 // Destination for data, modified via setters.
947 DateTimePatternGenerator
& dtpg
;
949 // UBool flag indicating whether to populate the generator with all patterns or just date patterns with numeric cores
950 UBool onlyDatesWithNumericCores
;
952 // Temporary variable, required for calling addPatternWithSkeleton.
953 UnicodeString conflictingPattern
;
955 AvailableFormatsSink(DateTimePatternGenerator
& _dtpg
, UBool onlyDatesWithNumericCores
) : dtpg(_dtpg
), onlyDatesWithNumericCores(onlyDatesWithNumericCores
) {}
956 virtual ~AvailableFormatsSink();
958 virtual void put(const char *key
, ResourceValue
&value
, UBool isRoot
,
959 UErrorCode
&errorCode
) {
960 ResourceTable itemsTable
= value
.getTable(errorCode
);
961 if (U_FAILURE(errorCode
)) { return; }
962 for (int32_t i
= 0; itemsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
963 UErrorCode valueErr
= U_ZERO_ERROR
; // if the resource value isn't a string, skip it without returning an error to the caller
964 const UnicodeString
formatKey(key
, -1, US_INV
);
965 UnicodeString formatValue
= value
.getUnicodeString(valueErr
);
966 if (U_SUCCESS(valueErr
) && !dtpg
.isAvailableFormatSet(formatKey
) && (!onlyDatesWithNumericCores
|| datePatternHasNumericCore(formatValue
))) {
967 // if the date pattern generator's locale is a LTR locale, strip out any Unicode right-to-left marks
968 // (if the pattern string came from a RTL locale, it may have RLMs around some separator characters
969 // to get them to lay out correctly, but we don't want that in an LTR context)
970 if (!dtpg
.pLocale
.isRightToLeft()) {
971 formatValue
.findAndReplace(UnicodeString(u
'\u200f'), UnicodeString());
973 dtpg
.setAvailableFormat(formatKey
, errorCode
);
974 // Add pattern with its associated skeleton. Override any duplicate
975 // derived from std patterns, but not a previous availableFormats entry:
976 conflictingPattern
.remove();
977 dtpg
.addPatternWithSkeleton(formatValue
, &formatKey
, !isRoot
, conflictingPattern
, errorCode
);
983 // Virtual destructors must be defined out of line.
984 DateTimePatternGenerator::AppendItemFormatsSink::~AppendItemFormatsSink() {}
985 DateTimePatternGenerator::AppendItemNamesSink::~AppendItemNamesSink() {}
986 DateTimePatternGenerator::AvailableFormatsSink::~AvailableFormatsSink() {}
989 DateTimePatternGenerator::addCLDRData(const Locale
& locale
, UErrorCode
& errorCode
) {
990 if (U_FAILURE(errorCode
)) { return; }
991 UnicodeString rbPattern
, value
, field
;
994 UBool hasCountryFallbackResource
= FALSE
;
995 LocalUResourceBundlePointer
rb(ures_open(nullptr, locale
.getName(), &errorCode
));
996 LocalUResourceBundlePointer
countryRB(ures_openWithCountryFallback(nullptr, locale
.getName(), &hasCountryFallbackResource
, &errorCode
));
997 if (U_FAILURE(errorCode
)) { return; }
999 CharString calendarTypeToUse
; // to be filled in with the type to use, if all goes well
1000 getCalendarTypeToUse(locale
, calendarTypeToUse
, errorCode
);
1001 if (U_FAILURE(errorCode
)) { return; }
1003 // Local err to ignore resource not found exceptions
1004 UErrorCode err
= U_ZERO_ERROR
;
1006 // Load append item formats.
1007 AppendItemFormatsSink
appendItemFormatsSink(*this);
1009 .append(DT_DateTimeCalendarTag
, errorCode
)
1010 .append('/', errorCode
)
1011 .append(calendarTypeToUse
, errorCode
)
1012 .append('/', errorCode
)
1013 .append(DT_DateTimeAppendItemsTag
, errorCode
); // i.e., calendar/xxx/appendItems
1014 if (U_FAILURE(errorCode
)) { return; }
1015 ures_getAllItemsWithFallback(rb
.getAlias(), path
.data(), appendItemFormatsSink
, err
);
1016 appendItemFormatsSink
.fillInMissing();
1018 // Load CLDR item names.
1020 AppendItemNamesSink
appendItemNamesSink(*this);
1021 ures_getAllItemsWithFallback(rb
.getAlias(), DT_DateTimeFieldsTag
, appendItemNamesSink
, err
);
1022 appendItemNamesSink
.fillInMissing();
1024 // Load the available formats from CLDR.
1026 initHashtable(errorCode
);
1027 if (U_FAILURE(errorCode
)) { return; }
1029 .append(DT_DateTimeCalendarTag
, errorCode
)
1030 .append('/', errorCode
)
1031 .append(calendarTypeToUse
, errorCode
)
1032 .append('/', errorCode
)
1033 .append(DT_DateTimeAvailableFormatsTag
, errorCode
); // i.e., calendar/xxx/availableFormats
1034 if (U_FAILURE(errorCode
)) { return; }
1035 if (hasCountryFallbackResource
) {
1036 AvailableFormatsSink
countryAvailableFormatsSink(*this, TRUE
);
1037 ures_getAllItemsWithFallback(countryRB
.getAlias(), path
.data(), countryAvailableFormatsSink
, err
);
1039 AvailableFormatsSink
availableFormatsSink(*this, FALSE
);
1040 ures_getAllItemsWithFallback(rb
.getAlias(), path
.data(), availableFormatsSink
, err
);
1044 DateTimePatternGenerator::initHashtable(UErrorCode
& err
) {
1045 if (U_FAILURE(err
)) { return; }
1046 if (fAvailableFormatKeyHash
!=nullptr) {
1049 LocalPointer
<Hashtable
> hash(new Hashtable(FALSE
, err
), err
);
1050 if (U_SUCCESS(err
)) {
1051 fAvailableFormatKeyHash
= hash
.orphan();
1056 DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field
, const UnicodeString
& value
) {
1057 appendItemFormats
[field
] = value
;
1058 // NUL-terminate for the C API.
1059 appendItemFormats
[field
].getTerminatedBuffer();
1062 const UnicodeString
&
1063 DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field
) const {
1064 return appendItemFormats
[field
];
1068 DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field
, const UnicodeString
& value
) {
1069 setFieldDisplayName(field
, UDATPG_WIDTH_APPENDITEM
, value
);
1072 const UnicodeString
&
1073 DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field
) const {
1074 return fieldDisplayNames
[field
][UDATPG_WIDTH_APPENDITEM
];
1078 DateTimePatternGenerator::setFieldDisplayName(UDateTimePatternField field
, UDateTimePGDisplayWidth width
, const UnicodeString
& value
) {
1079 fieldDisplayNames
[field
][width
] = value
;
1080 // NUL-terminate for the C API.
1081 fieldDisplayNames
[field
][width
].getTerminatedBuffer();
1085 DateTimePatternGenerator::getFieldDisplayName(UDateTimePatternField field
, UDateTimePGDisplayWidth width
) const {
1086 return fieldDisplayNames
[field
][width
];
1090 DateTimePatternGenerator::getMutableFieldDisplayName(UDateTimePatternField field
, UDateTimePGDisplayWidth width
) {
1091 return fieldDisplayNames
[field
][width
];
1095 DateTimePatternGenerator::getAppendName(UDateTimePatternField field
, UnicodeString
& value
) {
1096 value
= SINGLE_QUOTE
;
1097 value
+= fieldDisplayNames
[field
][UDATPG_WIDTH_APPENDITEM
];
1098 value
+= SINGLE_QUOTE
;
1102 DateTimePatternGenerator::getBestPattern(const UnicodeString
& patternForm
, UErrorCode
& status
) {
1103 return getBestPattern(patternForm
, UDATPG_MATCH_NO_OPTIONS
, status
);
1107 DateTimePatternGenerator::getBestPattern(const UnicodeString
& patternForm
, UDateTimePatternMatchOptions options
, UErrorCode
& status
) {
1108 if (U_FAILURE(status
)) {
1109 return UnicodeString();
1111 if (U_FAILURE(internalErrorCode
)) {
1112 status
= internalErrorCode
;
1113 return UnicodeString();
1115 const UnicodeString
*bestPattern
= nullptr;
1116 UnicodeString dtFormat
;
1117 UnicodeString resultPattern
;
1118 int32_t flags
= kDTPGNoFlags
;
1120 int32_t dateMask
=(1<<UDATPG_DAYPERIOD_FIELD
) - 1;
1121 int32_t timeMask
=(1<<UDATPG_FIELD_COUNT
) - 1 - dateMask
;
1123 // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
1124 UnicodeString patternFormMapped
= mapSkeletonMetacharacters(patternForm
, &flags
, options
, status
);
1125 if (U_FAILURE(status
)) {
1126 return UnicodeString();
1129 resultPattern
.remove();
1130 dtMatcher
->set(patternFormMapped
, fp
);
1131 const PtnSkeleton
* specifiedSkeleton
= nullptr;
1132 bestPattern
=getBestRaw(*dtMatcher
, -1, distanceInfo
, status
, &specifiedSkeleton
);
1134 // getBestRaw() might return a pattern with an era field in it, even if the skeleton didn't specifically ask for it.
1135 // Check for that and take it out of distanceInfo->missingFieldMask so that we don't end up adding a SECOND era
1137 if (bestPattern
->indexOf(u
'G') != -1) {
1138 distanceInfo
->missingFieldMask
&= ~(1 << UDATPG_ERA_FIELD
);
1141 if (U_FAILURE(status
)) {
1142 return UnicodeString();
1145 if ( distanceInfo
->missingFieldMask
==0 && distanceInfo
->extraFieldMask
==0 ) {
1146 resultPattern
= adjustFieldTypes(*bestPattern
, specifiedSkeleton
, flags
, options
);
1148 return resultPattern
;
1150 int32_t neededFields
= dtMatcher
->getFieldMask();
1151 UnicodeString datePattern
=getBestAppending(neededFields
& dateMask
, flags
, status
, options
);
1152 UnicodeString timePattern
=getBestAppending(neededFields
& timeMask
, flags
, status
, options
);
1153 if (U_FAILURE(status
)) {
1154 return UnicodeString();
1156 if (datePattern
.length()==0) {
1157 if (timePattern
.length()==0) {
1158 resultPattern
.remove();
1164 if (timePattern
.length()==0) {
1167 resultPattern
.remove();
1168 status
= U_ZERO_ERROR
;
1169 dtFormat
=getDateTimeFormat();
1170 SimpleFormatter(dtFormat
, 2, 2, status
).format(timePattern
, datePattern
, resultPattern
, status
);
1171 return resultPattern
;
1175 * Map a skeleton that may have metacharacters jJC to one without, by replacing
1176 * the metacharacters with locale-appropriate fields of h/H/k/K and of a/b/B
1177 * (depends on fDefaultHourFormatChar and fAllowedHourFormats being set, which in
1178 * turn depends on initData having been run). This method also updates the flags
1179 * as necessary. Returns the updated skeleton.
1182 DateTimePatternGenerator::mapSkeletonMetacharacters(const UnicodeString
& patternForm
, int32_t* flags
, UDateTimePatternMatchOptions options
, UErrorCode
& status
) {
1183 UnicodeString patternFormMapped
;
1184 patternFormMapped
.remove();
1185 UChar hourFormatSkeletonCharForLowJ
= fDefaultHourFormatChar
;
1186 switch (options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) {
1187 case UADATPG_FORCE_12_HOUR_CYCLE
: hourFormatSkeletonCharForLowJ
= LOW_H
; break;
1188 case UADATPG_FORCE_24_HOUR_CYCLE
: hourFormatSkeletonCharForLowJ
= CAP_H
; break;
1191 UBool inQuoted
= FALSE
;
1192 int32_t patPos
, patLen
= patternForm
.length();
1193 for (patPos
= 0; patPos
< patLen
; patPos
++) {
1194 UChar patChr
= patternForm
.charAt(patPos
);
1195 if (patChr
== SINGLE_QUOTE
) {
1196 inQuoted
= !inQuoted
;
1197 } else if (!inQuoted
) {
1198 // Handle special mappings for 'j' and 'C' in which fields lengths
1199 // 1,3,5 => hour field length 1
1200 // 2,4,6 => hour field length 2
1201 // 1,2 => abbreviated dayPeriod (field length 1..3)
1202 // 3,4 => long dayPeriod (field length 4)
1203 // 5,6 => narrow dayPeriod (field length 5)
1204 if (patChr
== LOW_J
|| patChr
== CAP_C
) {
1205 int32_t extraLen
= 0; // 1 less than total field length
1206 while (patPos
+1 < patLen
&& patternForm
.charAt(patPos
+1)==patChr
) {
1210 int32_t hourLen
= 1 + (extraLen
& 1);
1211 int32_t dayPeriodLen
= (extraLen
< 2)? 1: 3 + (extraLen
>> 1);
1212 UChar hourChar
= LOW_H
;
1213 UChar dayPeriodChar
= LOW_A
;
1214 if (patChr
== LOW_J
) {
1215 hourChar
= hourFormatSkeletonCharForLowJ
;
1217 AllowedHourFormat bestAllowed
;
1218 if (fAllowedHourFormats
[0] != ALLOWED_HOUR_FORMAT_UNKNOWN
) {
1219 bestAllowed
= (AllowedHourFormat
)fAllowedHourFormats
[0];
1221 status
= U_INVALID_FORMAT_ERROR
;
1222 return UnicodeString();
1224 if (bestAllowed
== ALLOWED_HOUR_FORMAT_H
|| bestAllowed
== ALLOWED_HOUR_FORMAT_HB
|| bestAllowed
== ALLOWED_HOUR_FORMAT_Hb
) {
1226 } else if (bestAllowed
== ALLOWED_HOUR_FORMAT_K
|| bestAllowed
== ALLOWED_HOUR_FORMAT_KB
|| bestAllowed
== ALLOWED_HOUR_FORMAT_Kb
) {
1228 } else if (bestAllowed
== ALLOWED_HOUR_FORMAT_k
) {
1231 // in #13183 just add b/B to skeleton, no longer need to set special flags
1232 if (bestAllowed
== ALLOWED_HOUR_FORMAT_HB
|| bestAllowed
== ALLOWED_HOUR_FORMAT_hB
|| bestAllowed
== ALLOWED_HOUR_FORMAT_KB
) {
1233 dayPeriodChar
= CAP_B
;
1234 } else if (bestAllowed
== ALLOWED_HOUR_FORMAT_Hb
|| bestAllowed
== ALLOWED_HOUR_FORMAT_hb
|| bestAllowed
== ALLOWED_HOUR_FORMAT_Kb
) {
1235 dayPeriodChar
= LOW_B
;
1238 if (hourChar
==CAP_H
|| hourChar
==LOW_K
) {
1241 while (dayPeriodLen
-- > 0) {
1242 patternFormMapped
.append(dayPeriodChar
);
1244 while (hourLen
-- > 0) {
1245 patternFormMapped
.append(hourChar
);
1247 } else if (patChr
== CAP_J
) {
1248 // Get pattern for skeleton with H, then replace H or k
1249 // with fDefaultHourFormatChar (if different)
1250 patternFormMapped
.append(CAP_H
);
1251 *flags
|= kDTPGSkeletonUsesCapJ
;
1253 patternFormMapped
.append(patChr
);
1257 return patternFormMapped
;
1261 DateTimePatternGenerator::replaceFieldTypes(const UnicodeString
& pattern
,
1262 const UnicodeString
& skeleton
,
1263 UErrorCode
& status
) {
1264 return replaceFieldTypes(pattern
, skeleton
, UDATPG_MATCH_NO_OPTIONS
, status
);
1268 DateTimePatternGenerator::replaceFieldTypes(const UnicodeString
& pattern
,
1269 const UnicodeString
& skeleton
,
1270 UDateTimePatternMatchOptions options
,
1271 UErrorCode
& status
) {
1272 if (U_FAILURE(status
)) {
1273 return UnicodeString();
1275 if (U_FAILURE(internalErrorCode
)) {
1276 status
= internalErrorCode
;
1277 return UnicodeString();
1279 dtMatcher
->set(skeleton
, fp
);
1280 UnicodeString result
= adjustFieldTypes(pattern
, nullptr, kDTPGNoFlags
, options
);
1285 DateTimePatternGenerator::setDecimal(const UnicodeString
& newDecimal
) {
1286 this->decimal
= newDecimal
;
1287 // NUL-terminate for the C API.
1288 this->decimal
.getTerminatedBuffer();
1291 const UnicodeString
&
1292 DateTimePatternGenerator::getDecimal() const {
1297 DateTimePatternGenerator::addCanonicalItems(UErrorCode
& status
) {
1298 if (U_FAILURE(status
)) { return; }
1299 UnicodeString conflictingPattern
;
1301 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; i
++) {
1302 if (Canonical_Items
[i
] > 0) {
1303 addPattern(UnicodeString(Canonical_Items
[i
]), FALSE
, conflictingPattern
, status
);
1305 if (U_FAILURE(status
)) { return; }
1310 DateTimePatternGenerator::setDateTimeFormat(const UnicodeString
& dtFormat
) {
1311 dateTimeFormat
= dtFormat
;
1312 // NUL-terminate for the C API.
1313 dateTimeFormat
.getTerminatedBuffer();
1316 const UnicodeString
&
1317 DateTimePatternGenerator::getDateTimeFormat() const {
1318 return dateTimeFormat
;
1322 DateTimePatternGenerator::setDateTimeFromCalendar(const Locale
& locale
, UErrorCode
& status
) {
1323 if (U_FAILURE(status
)) { return; }
1325 const UChar
*resStr
;
1326 int32_t resStrLen
= 0;
1328 LocalPointer
<Calendar
> fCalendar(Calendar::createInstance(locale
, status
), status
);
1329 if (U_FAILURE(status
)) { return; }
1331 LocalUResourceBundlePointer
calData(ures_open(nullptr, locale
.getBaseName(), &status
));
1332 if (U_FAILURE(status
)) { return; }
1333 ures_getByKey(calData
.getAlias(), DT_DateTimeCalendarTag
, calData
.getAlias(), &status
);
1334 if (U_FAILURE(status
)) { return; }
1336 LocalUResourceBundlePointer dateTimePatterns
;
1337 if (fCalendar
->getType() != nullptr && *fCalendar
->getType() != '\0'
1338 && uprv_strcmp(fCalendar
->getType(), DT_DateTimeGregorianTag
) != 0) {
1339 dateTimePatterns
.adoptInstead(ures_getByKeyWithFallback(calData
.getAlias(), fCalendar
->getType(),
1341 ures_getByKeyWithFallback(dateTimePatterns
.getAlias(), DT_DateTimePatternsTag
,
1342 dateTimePatterns
.getAlias(), &status
);
1345 if (dateTimePatterns
.isNull() || status
== U_MISSING_RESOURCE_ERROR
) {
1346 status
= U_ZERO_ERROR
;
1347 dateTimePatterns
.adoptInstead(ures_getByKeyWithFallback(calData
.getAlias(), DT_DateTimeGregorianTag
,
1348 dateTimePatterns
.orphan(), &status
));
1349 ures_getByKeyWithFallback(dateTimePatterns
.getAlias(), DT_DateTimePatternsTag
,
1350 dateTimePatterns
.getAlias(), &status
);
1352 if (U_FAILURE(status
)) { return; }
1354 if (ures_getSize(dateTimePatterns
.getAlias()) <= DateFormat::kDateTime
)
1356 status
= U_INVALID_FORMAT_ERROR
;
1359 resStr
= ures_getStringByIndex(dateTimePatterns
.getAlias(), (int32_t)DateFormat::kDateTime
, &resStrLen
, &status
);
1360 setDateTimeFormat(UnicodeString(TRUE
, resStr
, resStrLen
));
1364 DateTimePatternGenerator::setDecimalSymbols(const Locale
& locale
, UErrorCode
& status
) {
1365 DecimalFormatSymbols dfs
= DecimalFormatSymbols(locale
, status
);
1366 if(U_SUCCESS(status
)) {
1367 decimal
= dfs
.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol
);
1368 // NUL-terminate for the C API.
1369 decimal
.getTerminatedBuffer();
1373 UDateTimePatternConflict
1374 DateTimePatternGenerator::addPattern(
1375 const UnicodeString
& pattern
,
1377 UnicodeString
&conflictingPattern
,
1380 if (U_FAILURE(internalErrorCode
)) {
1381 status
= internalErrorCode
;
1382 return UDATPG_NO_CONFLICT
;
1385 return addPatternWithSkeleton(pattern
, nullptr, override
, conflictingPattern
, status
);
1388 // For DateTimePatternGenerator::addPatternWithSkeleton -
1389 // If skeletonToUse is specified, then an availableFormats entry is being added. In this case:
1390 // 1. We pass that skeleton to matcher.set instead of having it derive a skeleton from the pattern.
1391 // 2. If the new entry's skeleton or basePattern does match an existing entry but that entry also had a skeleton specified
1392 // (i.e. it was also from availableFormats), then the new entry does not override it regardless of the value of the override
1393 // parameter. This prevents later availableFormats entries from a parent locale overriding earlier ones from the actual
1394 // specified locale. However, availableFormats entries *should* override entries with matching skeleton whose skeleton was
1395 // derived (i.e. entries derived from the standard date/time patters for the specified locale).
1396 // 3. When adding the pattern (patternMap->add), we set a new boolean to indicate that the added entry had a
1397 // specified skeleton (which sets a new field in the PtnElem in the PatternMap).
1398 UDateTimePatternConflict
1399 DateTimePatternGenerator::addPatternWithSkeleton(
1400 const UnicodeString
& pattern
,
1401 const UnicodeString
* skeletonToUse
,
1403 UnicodeString
& conflictingPattern
,
1406 if (U_FAILURE(internalErrorCode
)) {
1407 status
= internalErrorCode
;
1408 return UDATPG_NO_CONFLICT
;
1411 UnicodeString basePattern
;
1412 PtnSkeleton skeleton
;
1413 UDateTimePatternConflict conflictingStatus
= UDATPG_NO_CONFLICT
;
1415 DateTimeMatcher matcher
;
1416 if ( skeletonToUse
== nullptr ) {
1417 matcher
.set(pattern
, fp
, skeleton
);
1418 matcher
.getBasePattern(basePattern
);
1420 matcher
.set(*skeletonToUse
, fp
, skeleton
); // no longer trims skeleton fields to max len 3, per #7930
1421 matcher
.getBasePattern(basePattern
); // or perhaps instead: basePattern = *skeletonToUse;
1423 // We only care about base conflicts - and replacing the pattern associated with a base - if:
1424 // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous
1425 // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or
1426 // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen
1427 // if we are getting here from a subsequent call to addPattern).
1428 // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking
1429 // availableFormats items from root, which should not override any previous entry with the same base.
1430 UBool entryHadSpecifiedSkeleton
;
1431 const UnicodeString
*duplicatePattern
= patternMap
->getPatternFromBasePattern(basePattern
, entryHadSpecifiedSkeleton
);
1432 if (duplicatePattern
!= nullptr && (!entryHadSpecifiedSkeleton
|| (skeletonToUse
!= nullptr && !override
))) {
1433 conflictingStatus
= UDATPG_BASE_CONFLICT
;
1434 conflictingPattern
= *duplicatePattern
;
1436 return conflictingStatus
;
1439 // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats
1440 // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with
1441 // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for
1442 // the previously-specified conflicting item.
1443 const PtnSkeleton
* entrySpecifiedSkeleton
= nullptr;
1444 duplicatePattern
= patternMap
->getPatternFromSkeleton(skeleton
, &entrySpecifiedSkeleton
);
1445 if (duplicatePattern
!= nullptr ) {
1446 conflictingStatus
= UDATPG_CONFLICT
;
1447 conflictingPattern
= *duplicatePattern
;
1448 if (!override
|| (skeletonToUse
!= nullptr && entrySpecifiedSkeleton
!= nullptr)) {
1449 return conflictingStatus
;
1452 patternMap
->add(basePattern
, skeleton
, pattern
, skeletonToUse
!= nullptr, status
);
1453 if(U_FAILURE(status
)) {
1454 return conflictingStatus
;
1457 return UDATPG_NO_CONFLICT
;
1461 UDateTimePatternField
1462 DateTimePatternGenerator::getAppendFormatNumber(const char* field
) const {
1463 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1464 if (uprv_strcmp(CLDR_FIELD_APPEND
[i
], field
)==0) {
1465 return (UDateTimePatternField
)i
;
1468 return UDATPG_FIELD_COUNT
;
1471 UDateTimePatternField
1472 DateTimePatternGenerator::getFieldAndWidthIndices(const char* key
, UDateTimePGDisplayWidth
* widthP
) const {
1473 char cldrFieldKey
[UDATPG_FIELD_KEY_MAX
+ 1];
1474 uprv_strncpy(cldrFieldKey
, key
, UDATPG_FIELD_KEY_MAX
);
1475 cldrFieldKey
[UDATPG_FIELD_KEY_MAX
]=0; // ensure termination
1476 *widthP
= UDATPG_WIDE
;
1477 char* hyphenPtr
= uprv_strchr(cldrFieldKey
, '-');
1479 for (int32_t i
=UDATPG_WIDTH_COUNT
-1; i
>0; --i
) {
1480 if (uprv_strcmp(CLDR_FIELD_WIDTH
[i
], hyphenPtr
)==0) {
1481 *widthP
=(UDateTimePGDisplayWidth
)i
;
1485 *hyphenPtr
= 0; // now delete width portion of key
1487 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1488 if (uprv_strcmp(CLDR_FIELD_NAME
[i
],cldrFieldKey
)==0) {
1489 return (UDateTimePatternField
)i
;
1492 return UDATPG_FIELD_COUNT
;
1495 const UnicodeString
*
1496 DateTimePatternGenerator::getBestRaw(DateTimeMatcher
& source
,
1497 int32_t includeMask
,
1498 DistanceInfo
* missingFields
,
1500 const PtnSkeleton
** specifiedSkeletonPtr
) {
1501 int32_t bestDistance
= 0x7fffffff;
1502 DistanceInfo tempInfo
;
1503 const UnicodeString
*bestPattern
=nullptr;
1504 const PtnSkeleton
* specifiedSkeleton
=nullptr;
1506 PatternMapIterator
it(status
);
1507 if (U_FAILURE(status
)) { return nullptr; }
1509 for (it
.set(*patternMap
); it
.hasNext(); ) {
1510 DateTimeMatcher trial
= it
.next();
1511 if (trial
.equals(skipMatcher
)) {
1514 int32_t distance
=source
.getDistance(trial
, includeMask
, tempInfo
);
1515 if (distance
<bestDistance
) {
1516 bestDistance
=distance
;
1517 bestPattern
=patternMap
->getPatternFromSkeleton(*trial
.getSkeletonPtr(), &specifiedSkeleton
);
1518 missingFields
->setTo(tempInfo
);
1525 // If the best raw match had a specified skeleton and that skeleton was requested by the caller,
1526 // then return it too. This generally happens when the caller needs to pass that skeleton
1527 // through to adjustFieldTypes so the latter can do a better job.
1528 if (bestPattern
&& specifiedSkeletonPtr
) {
1529 *specifiedSkeletonPtr
= specifiedSkeleton
;
1535 DateTimePatternGenerator::adjustFieldTypes(const UnicodeString
& pattern
,
1536 const PtnSkeleton
* specifiedSkeleton
,
1538 UDateTimePatternMatchOptions options
) {
1539 UnicodeString newPattern
;
1541 for (int32_t i
=0; i
< fp
->itemNumber
; i
++) {
1542 UnicodeString field
= fp
->items
[i
];
1543 if ( fp
->isQuoteLiteral(field
) ) {
1545 UnicodeString quoteLiteral
;
1546 fp
->getQuoteLiteral(quoteLiteral
, &i
);
1547 newPattern
+= quoteLiteral
;
1550 if (fp
->isPatternSeparator(field
)) {
1554 int32_t canonicalIndex
= fp
->getCanonicalIndex(field
);
1555 if (canonicalIndex
< 0) {
1557 continue; // don't adjust
1559 const dtTypeElem
*row
= &dtTypes
[canonicalIndex
];
1560 int32_t typeValue
= row
->field
;
1562 // handle day periods - with #13183, no longer need special handling here, integrated with normal types
1564 if ((flags
& kDTPGFixFractionalSeconds
) != 0 && typeValue
== UDATPG_SECOND_FIELD
) {
1566 dtMatcher
->skeleton
.original
.appendFieldTo(UDATPG_FRACTIONAL_SECOND_FIELD
, field
);
1567 } else if (dtMatcher
->skeleton
.type
[typeValue
]!=0) {
1569 // - "reqField" is the field from the originally requested skeleton, with length
1571 // - "field" is the field from the found pattern.
1573 // The adjusted field should consist of characters from the originally requested
1574 // skeleton, except in the case of UDATPG_HOUR_FIELD or UDATPG_MONTH_FIELD or
1575 // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist
1576 // of characters from the found pattern.
1578 // The length of the adjusted field (adjFieldLen) should match that in the originally
1579 // requested skeleton, except that in the following cases the length of the adjusted field
1580 // should match that in the found pattern (i.e. the length of this pattern field should
1581 // not be adjusted):
1582 // 1. typeValue is UDATPG_HOUR_FIELD/MINUTE/SECOND and the corresponding bit in options is
1583 // not set (ticket #7180). Note, we may want to implement a similar change for other
1584 // numeric fields (MM, dd, etc.) so the default behavior is to get locale preference for
1585 // field length, but options bits can be used to override this.
1586 // 2. There is a specified skeleton for the found pattern and one of the following is true:
1587 // a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen.
1588 // b) The pattern field is numeric and the skeleton field is not, or vice versa.
1590 UChar reqFieldChar
= dtMatcher
->skeleton
.original
.getFieldChar(typeValue
);
1591 int32_t reqFieldLen
= dtMatcher
->skeleton
.original
.getFieldLength(typeValue
);
1592 if (reqFieldChar
== CAP_E
&& reqFieldLen
< 3)
1593 reqFieldLen
= 3; // 1-3 for E are equivalent to 3 for c,e
1594 int32_t adjFieldLen
= reqFieldLen
;
1595 if ( (typeValue
==UDATPG_HOUR_FIELD
&& (options
& UDATPG_MATCH_HOUR_FIELD_LENGTH
)==0) ||
1596 (typeValue
==UDATPG_MINUTE_FIELD
&& (options
& UDATPG_MATCH_MINUTE_FIELD_LENGTH
)==0) ||
1597 (typeValue
==UDATPG_SECOND_FIELD
&& (options
& UDATPG_MATCH_SECOND_FIELD_LENGTH
)==0) ) {
1598 adjFieldLen
= field
.length();
1599 } else if (specifiedSkeleton
) {
1600 int32_t skelFieldLen
= specifiedSkeleton
->original
.getFieldLength(typeValue
);
1601 UBool patFieldIsNumeric
= (row
->type
> 0);
1602 UBool skelFieldIsNumeric
= (specifiedSkeleton
->type
[typeValue
] > 0);
1603 if (skelFieldLen
== reqFieldLen
|| (patFieldIsNumeric
&& !skelFieldIsNumeric
) || (skelFieldIsNumeric
&& !patFieldIsNumeric
)) {
1604 // don't adjust the field length in the found pattern
1605 adjFieldLen
= field
.length();
1608 UChar c
= (typeValue
!= UDATPG_HOUR_FIELD
1609 && typeValue
!= UDATPG_MONTH_FIELD
1610 && typeValue
!= UDATPG_WEEKDAY_FIELD
1611 && (typeValue
!= UDATPG_YEAR_FIELD
|| reqFieldChar
==CAP_Y
))
1614 if (typeValue
== UDATPG_HOUR_FIELD
&& (flags
& kDTPGSkeletonUsesCapJ
) != 0) {
1615 c
= fDefaultHourFormatChar
;
1616 switch (options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) {
1617 case UADATPG_FORCE_12_HOUR_CYCLE
:
1618 if (c
== CAP_H
|| c
== LOW_K
) {
1619 // Have 24-hour cycle, change to 12-hour cycle.
1620 // Should have better way to pick 'h' or 'K'.
1624 case UADATPG_FORCE_24_HOUR_CYCLE
:
1625 if (c
== LOW_H
|| c
== CAP_K
) {
1626 // Have 12-hour cycle, change to 24-hour cycle.
1627 // Should have better way to pick 'H' or 'k'.
1636 for (int32_t j
=adjFieldLen
; j
>0; --j
) {
1647 DateTimePatternGenerator::getBestAppending(int32_t missingFields
, int32_t flags
, UErrorCode
&status
, UDateTimePatternMatchOptions options
) {
1648 if (U_FAILURE(status
)) {
1649 return UnicodeString();
1651 UnicodeString resultPattern
, tempPattern
;
1652 const UnicodeString
* tempPatternPtr
;
1653 int32_t lastMissingFieldMask
=0;
1654 if (missingFields
!=0) {
1655 resultPattern
=UnicodeString();
1656 const PtnSkeleton
* specifiedSkeleton
=nullptr;
1657 tempPatternPtr
= getBestRaw(*dtMatcher
, missingFields
, distanceInfo
, status
, &specifiedSkeleton
);
1658 if (U_FAILURE(status
)) {
1659 return UnicodeString();
1661 tempPattern
= *tempPatternPtr
;
1662 resultPattern
= adjustFieldTypes(tempPattern
, specifiedSkeleton
, flags
, options
);
1663 if ( distanceInfo
->missingFieldMask
==0 ) {
1664 return resultPattern
;
1666 while (distanceInfo
->missingFieldMask
!=0) { // precondition: EVERY single field must work!
1667 if ( lastMissingFieldMask
== distanceInfo
->missingFieldMask
) {
1668 break; // cannot find the proper missing field
1670 if (((distanceInfo
->missingFieldMask
& UDATPG_SECOND_AND_FRACTIONAL_MASK
)==UDATPG_FRACTIONAL_MASK
) &&
1671 ((missingFields
& UDATPG_SECOND_AND_FRACTIONAL_MASK
) == UDATPG_SECOND_AND_FRACTIONAL_MASK
)) {
1672 resultPattern
= adjustFieldTypes(resultPattern
, specifiedSkeleton
, flags
| kDTPGFixFractionalSeconds
, options
);
1673 distanceInfo
->missingFieldMask
&= ~UDATPG_FRACTIONAL_MASK
;
1676 int32_t startingMask
= distanceInfo
->missingFieldMask
;
1677 tempPatternPtr
= getBestRaw(*dtMatcher
, distanceInfo
->missingFieldMask
, distanceInfo
, status
, &specifiedSkeleton
);
1678 if (U_FAILURE(status
)) {
1679 return UnicodeString();
1681 tempPattern
= *tempPatternPtr
;
1682 tempPattern
= adjustFieldTypes(tempPattern
, specifiedSkeleton
, flags
, options
);
1683 int32_t foundMask
=startingMask
& ~distanceInfo
->missingFieldMask
;
1684 int32_t topField
=getTopBitNumber(foundMask
);
1686 if (appendItemFormats
[topField
].length() != 0) {
1687 UnicodeString appendName
;
1688 getAppendName((UDateTimePatternField
)topField
, appendName
);
1689 const UnicodeString
*values
[3] = {
1694 SimpleFormatter(appendItemFormats
[topField
], 2, 3, status
).
1695 formatAndReplace(values
, 3, resultPattern
, nullptr, 0, status
);
1697 lastMissingFieldMask
= distanceInfo
->missingFieldMask
;
1700 return resultPattern
;
1704 DateTimePatternGenerator::getTopBitNumber(int32_t foundMask
) const {
1705 if ( foundMask
==0 ) {
1709 while (foundMask
!=0) {
1713 if (i
-1 >UDATPG_ZONE_FIELD
) {
1714 return UDATPG_ZONE_FIELD
;
1721 DateTimePatternGenerator::setAvailableFormat(const UnicodeString
&key
, UErrorCode
& err
)
1723 fAvailableFormatKeyHash
->puti(key
, 1, err
);
1727 DateTimePatternGenerator::isAvailableFormatSet(const UnicodeString
&key
) const {
1728 return (UBool
)(fAvailableFormatKeyHash
->geti(key
) == 1);
1732 DateTimePatternGenerator::copyHashtable(Hashtable
*other
, UErrorCode
&status
) {
1733 if (other
== nullptr || U_FAILURE(status
)) {
1736 if (fAvailableFormatKeyHash
!= nullptr) {
1737 delete fAvailableFormatKeyHash
;
1738 fAvailableFormatKeyHash
= nullptr;
1740 initHashtable(status
);
1741 if(U_FAILURE(status
)){
1744 int32_t pos
= UHASH_FIRST
;
1745 const UHashElement
* elem
= nullptr;
1746 // walk through the hash table and create a deep clone
1747 while((elem
= other
->nextElement(pos
))!= nullptr){
1748 const UHashTok otherKeyTok
= elem
->key
;
1749 UnicodeString
* otherKey
= (UnicodeString
*)otherKeyTok
.pointer
;
1750 fAvailableFormatKeyHash
->puti(*otherKey
, 1, status
);
1751 if(U_FAILURE(status
)){
1758 DateTimePatternGenerator::getSkeletons(UErrorCode
& status
) const {
1759 if (U_FAILURE(status
)) {
1762 if (U_FAILURE(internalErrorCode
)) {
1763 status
= internalErrorCode
;
1766 LocalPointer
<StringEnumeration
> skeletonEnumerator(
1767 new DTSkeletonEnumeration(*patternMap
, DT_SKELETON
, status
), status
);
1769 return U_SUCCESS(status
) ? skeletonEnumerator
.orphan() : nullptr;
1772 const UnicodeString
&
1773 DateTimePatternGenerator::getPatternForSkeleton(const UnicodeString
& skeleton
) const {
1776 if (skeleton
.length() ==0) {
1779 curElem
= patternMap
->getHeader(skeleton
.charAt(0));
1780 while ( curElem
!= nullptr ) {
1781 if ( curElem
->skeleton
->getSkeleton()==skeleton
) {
1782 return curElem
->pattern
;
1784 curElem
= curElem
->next
.getAlias();
1790 DateTimePatternGenerator::getBaseSkeletons(UErrorCode
& status
) const {
1791 if (U_FAILURE(status
)) {
1794 if (U_FAILURE(internalErrorCode
)) {
1795 status
= internalErrorCode
;
1798 LocalPointer
<StringEnumeration
> baseSkeletonEnumerator(
1799 new DTSkeletonEnumeration(*patternMap
, DT_BASESKELETON
, status
), status
);
1801 return U_SUCCESS(status
) ? baseSkeletonEnumerator
.orphan() : nullptr;
1805 DateTimePatternGenerator::getRedundants(UErrorCode
& status
) {
1806 if (U_FAILURE(status
)) { return nullptr; }
1807 if (U_FAILURE(internalErrorCode
)) {
1808 status
= internalErrorCode
;
1811 LocalPointer
<StringEnumeration
> output(new DTRedundantEnumeration(), status
);
1812 if (U_FAILURE(status
)) { return nullptr; }
1813 const UnicodeString
*pattern
;
1814 PatternMapIterator
it(status
);
1815 if (U_FAILURE(status
)) { return nullptr; }
1817 for (it
.set(*patternMap
); it
.hasNext(); ) {
1818 DateTimeMatcher current
= it
.next();
1819 pattern
= patternMap
->getPatternFromSkeleton(*(it
.getSkeleton()));
1820 if ( isCanonicalItem(*pattern
) ) {
1823 if ( skipMatcher
== nullptr ) {
1824 skipMatcher
= new DateTimeMatcher(current
);
1825 if (skipMatcher
== nullptr) {
1826 status
= U_MEMORY_ALLOCATION_ERROR
;
1831 *skipMatcher
= current
;
1833 UnicodeString trial
= getBestPattern(current
.getPattern(), status
);
1834 if (U_FAILURE(status
)) { return nullptr; }
1835 if (trial
== *pattern
) {
1836 ((DTRedundantEnumeration
*)output
.getAlias())->add(*pattern
, status
);
1837 if (U_FAILURE(status
)) { return nullptr; }
1839 if (current
.equals(skipMatcher
)) {
1843 return output
.orphan();
1847 DateTimePatternGenerator::isCanonicalItem(const UnicodeString
& item
) const {
1848 if ( item
.length() != 1 ) {
1851 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1852 if (item
.charAt(0)==Canonical_Items
[i
]) {
1860 DateTimePatternGenerator
*
1861 DateTimePatternGenerator::clone() const {
1862 return new DateTimePatternGenerator(*this);
1865 PatternMap::PatternMap() {
1866 for (int32_t i
=0; i
< MAX_PATTERN_ENTRIES
; ++i
) {
1869 isDupAllowed
= TRUE
;
1873 PatternMap::copyFrom(const PatternMap
& other
, UErrorCode
& status
) {
1874 if (U_FAILURE(status
)) {
1877 this->isDupAllowed
= other
.isDupAllowed
;
1878 for (int32_t bootIndex
= 0; bootIndex
< MAX_PATTERN_ENTRIES
; ++bootIndex
) {
1879 PtnElem
*curElem
, *otherElem
, *prevElem
=nullptr;
1880 otherElem
= other
.boot
[bootIndex
];
1881 while (otherElem
!= nullptr) {
1882 LocalPointer
<PtnElem
> newElem(new PtnElem(otherElem
->basePattern
, otherElem
->pattern
), status
);
1883 if (U_FAILURE(status
)) {
1884 return; // out of memory
1886 newElem
->skeleton
.adoptInsteadAndCheckErrorCode(new PtnSkeleton(*(otherElem
->skeleton
)), status
);
1887 if (U_FAILURE(status
)) {
1888 return; // out of memory
1890 newElem
->skeletonWasSpecified
= otherElem
->skeletonWasSpecified
;
1892 // Release ownership from the LocalPointer of the PtnElem object.
1893 // The PtnElem will now be owned by either the boot (for the first entry in the linked-list)
1894 // or owned by the previous PtnElem object in the linked-list.
1895 curElem
= newElem
.orphan();
1897 if (this->boot
[bootIndex
] == nullptr) {
1898 this->boot
[bootIndex
] = curElem
;
1900 if (prevElem
!= nullptr) {
1901 prevElem
->next
.adoptInstead(curElem
);
1907 otherElem
= otherElem
->next
.getAlias();
1914 PatternMap::getHeader(UChar baseChar
) const {
1917 if ( (baseChar
>= CAP_A
) && (baseChar
<= CAP_Z
) ) {
1918 curElem
= boot
[baseChar
-CAP_A
];
1921 if ( (baseChar
>=LOW_A
) && (baseChar
<= LOW_Z
) ) {
1922 curElem
= boot
[26+baseChar
-LOW_A
];
1931 PatternMap::~PatternMap() {
1932 for (int32_t i
=0; i
< MAX_PATTERN_ENTRIES
; ++i
) {
1933 if (boot
[i
] != nullptr ) {
1938 } // PatternMap destructor
1941 PatternMap::add(const UnicodeString
& basePattern
,
1942 const PtnSkeleton
& skeleton
,
1943 const UnicodeString
& value
,// mapped pattern value
1944 UBool skeletonWasSpecified
,
1945 UErrorCode
&status
) {
1946 UChar baseChar
= basePattern
.charAt(0);
1947 PtnElem
*curElem
, *baseElem
;
1948 status
= U_ZERO_ERROR
;
1950 // the baseChar must be A-Z or a-z
1951 if ((baseChar
>= CAP_A
) && (baseChar
<= CAP_Z
)) {
1952 baseElem
= boot
[baseChar
-CAP_A
];
1955 if ((baseChar
>=LOW_A
) && (baseChar
<= LOW_Z
)) {
1956 baseElem
= boot
[26+baseChar
-LOW_A
];
1959 status
= U_ILLEGAL_CHARACTER
;
1964 if (baseElem
== nullptr) {
1965 LocalPointer
<PtnElem
> newElem(new PtnElem(basePattern
, value
), status
);
1966 if (U_FAILURE(status
)) {
1967 return; // out of memory
1969 newElem
->skeleton
.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton
), status
);
1970 if (U_FAILURE(status
)) {
1971 return; // out of memory
1973 newElem
->skeletonWasSpecified
= skeletonWasSpecified
;
1974 if (baseChar
>= LOW_A
) {
1975 boot
[26 + (baseChar
- LOW_A
)] = newElem
.orphan(); // the boot array now owns the PtnElem.
1978 boot
[baseChar
- CAP_A
] = newElem
.orphan(); // the boot array now owns the PtnElem.
1981 if ( baseElem
!= nullptr ) {
1982 curElem
= getDuplicateElem(basePattern
, skeleton
, baseElem
);
1984 if (curElem
== nullptr) {
1985 // add new element to the list.
1987 while( curElem
-> next
!= nullptr )
1989 curElem
= curElem
->next
.getAlias();
1992 LocalPointer
<PtnElem
> newElem(new PtnElem(basePattern
, value
), status
);
1993 if (U_FAILURE(status
)) {
1994 return; // out of memory
1996 newElem
->skeleton
.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton
), status
);
1997 if (U_FAILURE(status
)) {
1998 return; // out of memory
2000 newElem
->skeletonWasSpecified
= skeletonWasSpecified
;
2001 curElem
->next
.adoptInstead(newElem
.orphan());
2002 curElem
= curElem
->next
.getAlias();
2005 // Pattern exists in the list already.
2006 if ( !isDupAllowed
) {
2009 // Overwrite the value.
2010 curElem
->pattern
= value
;
2011 // It was a bug that we were not doing the following previously,
2012 // though that bug hid other problems by making things partly work.
2013 curElem
->skeletonWasSpecified
= skeletonWasSpecified
;
2016 } // PatternMap::add
2018 // Find the pattern from the given basePattern string.
2019 const UnicodeString
*
2020 PatternMap::getPatternFromBasePattern(const UnicodeString
& basePattern
, UBool
& skeletonWasSpecified
) const { // key to search for
2023 if ((curElem
=getHeader(basePattern
.charAt(0)))==nullptr) {
2024 return nullptr; // no match
2028 if ( basePattern
.compare(curElem
->basePattern
)==0 ) {
2029 skeletonWasSpecified
= curElem
->skeletonWasSpecified
;
2030 return &(curElem
->pattern
);
2032 curElem
= curElem
->next
.getAlias();
2033 } while (curElem
!= nullptr);
2036 } // PatternMap::getFromBasePattern
2039 // Find the pattern from the given skeleton.
2040 // At least when this is called from getBestRaw & addPattern (in which case specifiedSkeletonPtr is non-NULL),
2041 // the comparison should be based on skeleton.original (which is unique and tied to the distance measurement in bestRaw)
2042 // and not skeleton.baseOriginal (which is not unique); otherwise we may pick a different skeleton than the one with the
2043 // optimum distance value in getBestRaw. When this is called from public getRedundants (specifiedSkeletonPtr is NULL),
2044 // for now it will continue to compare based on baseOriginal so as not to change the behavior unnecessarily.
2045 const UnicodeString
*
2046 PatternMap::getPatternFromSkeleton(const PtnSkeleton
& skeleton
, const PtnSkeleton
** specifiedSkeletonPtr
) const { // key to search for
2049 if (specifiedSkeletonPtr
) {
2050 *specifiedSkeletonPtr
= nullptr;
2054 UChar baseChar
= skeleton
.getFirstChar();
2055 if ((curElem
=getHeader(baseChar
))==nullptr) {
2056 return nullptr; // no match
2061 if (specifiedSkeletonPtr
!= nullptr) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original
2062 equal
= curElem
->skeleton
->original
== skeleton
.original
;
2063 } else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal
2064 equal
= curElem
->skeleton
->baseOriginal
== skeleton
.baseOriginal
;
2067 if (specifiedSkeletonPtr
&& curElem
->skeletonWasSpecified
) {
2068 *specifiedSkeletonPtr
= curElem
->skeleton
.getAlias();
2070 return &(curElem
->pattern
);
2072 curElem
= curElem
->next
.getAlias();
2073 } while (curElem
!= nullptr);
2079 PatternMap::equals(const PatternMap
& other
) const {
2080 if ( this==&other
) {
2083 for (int32_t bootIndex
= 0; bootIndex
< MAX_PATTERN_ENTRIES
; ++bootIndex
) {
2084 if (boot
[bootIndex
] == other
.boot
[bootIndex
]) {
2087 if ((boot
[bootIndex
] == nullptr) || (other
.boot
[bootIndex
] == nullptr)) {
2090 PtnElem
*otherElem
= other
.boot
[bootIndex
];
2091 PtnElem
*myElem
= boot
[bootIndex
];
2092 while ((otherElem
!= nullptr) || (myElem
!= nullptr)) {
2093 if ( myElem
== otherElem
) {
2096 if ((otherElem
== nullptr) || (myElem
== nullptr)) {
2099 if ( (myElem
->basePattern
!= otherElem
->basePattern
) ||
2100 (myElem
->pattern
!= otherElem
->pattern
) ) {
2103 if ((myElem
->skeleton
.getAlias() != otherElem
->skeleton
.getAlias()) &&
2104 !myElem
->skeleton
->equals(*(otherElem
->skeleton
))) {
2107 myElem
= myElem
->next
.getAlias();
2108 otherElem
= otherElem
->next
.getAlias();
2114 // find any key existing in the mapping table already.
2115 // return TRUE if there is an existing key, otherwise return FALSE.
2117 PatternMap::getDuplicateElem(
2118 const UnicodeString
&basePattern
,
2119 const PtnSkeleton
&skeleton
,
2120 PtnElem
*baseElem
) {
2123 if ( baseElem
== nullptr ) {
2130 if ( basePattern
.compare(curElem
->basePattern
)==0 ) {
2131 UBool isEqual
= TRUE
;
2132 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; ++i
) {
2133 if (curElem
->skeleton
->type
[i
] != skeleton
.type
[i
] ) {
2142 curElem
= curElem
->next
.getAlias();
2143 } while( curElem
!= nullptr );
2148 } // PatternMap::getDuplicateElem
2150 DateTimeMatcher::DateTimeMatcher(void) {
2153 DateTimeMatcher::~DateTimeMatcher() {}
2155 DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher
& other
) {
2156 copyFrom(other
.skeleton
);
2161 DateTimeMatcher::set(const UnicodeString
& pattern
, FormatParser
* fp
) {
2162 PtnSkeleton localSkeleton
;
2163 return set(pattern
, fp
, localSkeleton
);
2167 DateTimeMatcher::set(const UnicodeString
& pattern
, FormatParser
* fp
, PtnSkeleton
& skeletonResult
) {
2169 for (i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2170 skeletonResult
.type
[i
] = NONE
;
2172 skeletonResult
.original
.clear();
2173 skeletonResult
.baseOriginal
.clear();
2174 skeletonResult
.addedDefaultDayPeriod
= FALSE
;
2177 for (i
=0; i
< fp
->itemNumber
; i
++) {
2178 const UnicodeString
& value
= fp
->items
[i
];
2179 // don't skip 'a' anymore, dayPeriod handled specially below
2181 if ( fp
->isQuoteLiteral(value
) ) {
2182 UnicodeString quoteLiteral
;
2183 fp
->getQuoteLiteral(quoteLiteral
, &i
);
2186 int32_t canonicalIndex
= fp
->getCanonicalIndex(value
);
2187 if (canonicalIndex
< 0) {
2190 const dtTypeElem
*row
= &dtTypes
[canonicalIndex
];
2191 int32_t field
= row
->field
;
2192 skeletonResult
.original
.populate(field
, value
);
2193 UChar repeatChar
= row
->patternChar
;
2194 int32_t repeatCount
= row
->minLen
;
2195 skeletonResult
.baseOriginal
.populate(field
, repeatChar
, repeatCount
);
2196 int16_t subField
= row
->type
;
2197 if (row
->type
> 0) {
2198 U_ASSERT(value
.length() < INT16_MAX
);
2199 subField
+= static_cast<int16_t>(value
.length());
2201 skeletonResult
.type
[field
] = subField
;
2203 // #13183, handle special behavior for day period characters (a, b, B)
2204 if (!skeletonResult
.original
.isFieldEmpty(UDATPG_HOUR_FIELD
)) {
2205 if (skeletonResult
.original
.getFieldChar(UDATPG_HOUR_FIELD
)==LOW_H
|| skeletonResult
.original
.getFieldChar(UDATPG_HOUR_FIELD
)==CAP_K
) {
2206 // We have a skeleton with 12-hour-cycle format
2207 if (skeletonResult
.original
.isFieldEmpty(UDATPG_DAYPERIOD_FIELD
)) {
2208 // But we do not have a day period in the skeleton; add the default DAYPERIOD (currently "a")
2209 for (i
= 0; dtTypes
[i
].patternChar
!= 0; i
++) {
2210 if ( dtTypes
[i
].field
== UDATPG_DAYPERIOD_FIELD
) {
2211 // first entry for UDATPG_DAYPERIOD_FIELD
2212 skeletonResult
.original
.populate(UDATPG_DAYPERIOD_FIELD
, dtTypes
[i
].patternChar
, dtTypes
[i
].minLen
);
2213 skeletonResult
.baseOriginal
.populate(UDATPG_DAYPERIOD_FIELD
, dtTypes
[i
].patternChar
, dtTypes
[i
].minLen
);
2214 skeletonResult
.type
[UDATPG_DAYPERIOD_FIELD
] = dtTypes
[i
].type
;
2215 skeletonResult
.addedDefaultDayPeriod
= TRUE
;
2221 // Skeleton has 24-hour-cycle hour format and has dayPeriod, delete dayPeriod (i.e. ignore it)
2222 skeletonResult
.original
.clearField(UDATPG_DAYPERIOD_FIELD
);
2223 skeletonResult
.baseOriginal
.clearField(UDATPG_DAYPERIOD_FIELD
);
2224 skeletonResult
.type
[UDATPG_DAYPERIOD_FIELD
] = NONE
;
2227 copyFrom(skeletonResult
);
2231 DateTimeMatcher::getBasePattern(UnicodeString
&result
) {
2232 result
.remove(); // Reset the result first.
2233 skeleton
.baseOriginal
.appendTo(result
);
2237 DateTimeMatcher::getPattern() {
2238 UnicodeString result
;
2239 return skeleton
.original
.appendTo(result
);
2243 DateTimeMatcher::getDistance(const DateTimeMatcher
& other
, int32_t includeMask
, DistanceInfo
& distanceInfo
) const {
2245 distanceInfo
.clear();
2246 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2247 int32_t myType
= (includeMask
&(1<<i
))==0 ? 0 : skeleton
.type
[i
];
2248 int32_t otherType
= other
.skeleton
.type
[i
];
2249 if (myType
==otherType
) {
2252 if (myType
==0) {// and other is not
2253 result
+= EXTRA_FIELD
;
2254 distanceInfo
.addExtra(i
);
2258 result
+= MISSING_FIELD
;
2259 distanceInfo
.addMissing(i
);
2262 result
+= abs(myType
- otherType
);
2271 DateTimeMatcher::copyFrom(const PtnSkeleton
& newSkeleton
) {
2272 skeleton
.copyFrom(newSkeleton
);
2276 DateTimeMatcher::copyFrom() {
2282 DateTimeMatcher::equals(const DateTimeMatcher
* other
) const {
2283 if (other
==nullptr) { return FALSE
; }
2284 return skeleton
.original
== other
->skeleton
.original
;
2288 DateTimeMatcher::getFieldMask() const {
2291 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2292 if (skeleton
.type
[i
]!=0) {
2300 DateTimeMatcher::getSkeletonPtr() {
2304 FormatParser::FormatParser () {
2310 FormatParser::~FormatParser () {
2314 // Find the next token with the starting position and length
2315 // Note: the startPos may
2316 FormatParser::TokenStatus
2317 FormatParser::setTokens(const UnicodeString
& pattern
, int32_t startPos
, int32_t *len
) {
2318 int32_t curLoc
= startPos
;
2319 if ( curLoc
>= pattern
.length()) {
2322 // check the current char is between A-Z or a-z
2324 UChar c
=pattern
.charAt(curLoc
);
2325 if ( (c
>=CAP_A
&& c
<=CAP_Z
) || (c
>=LOW_A
&& c
<=LOW_Z
) ) {
2334 if ( pattern
.charAt(curLoc
)!= pattern
.charAt(startPos
) ) {
2335 break; // not the same token
2337 } while(curLoc
<= pattern
.length());
2338 *len
= curLoc
-startPos
;
2343 FormatParser::set(const UnicodeString
& pattern
) {
2344 int32_t startPos
= 0;
2345 TokenStatus result
= START
;
2350 result
= setTokens( pattern
, startPos
, &len
);
2351 if ( result
== ADD_TOKEN
)
2353 items
[itemNumber
++] = UnicodeString(pattern
, startPos
, len
);
2359 } while (result
==ADD_TOKEN
&& itemNumber
< MAX_DT_TOKEN
);
2363 FormatParser::getCanonicalIndex(const UnicodeString
& s
, UBool strict
) {
2364 int32_t len
= s
.length();
2368 UChar ch
= s
.charAt(0);
2370 // Verify that all are the same character.
2371 for (int32_t l
= 1; l
< len
; l
++) {
2372 if (ch
!= s
.charAt(l
)) {
2377 int32_t bestRow
= -1;
2378 while (dtTypes
[i
].patternChar
!= 0x0000) {
2379 if ( dtTypes
[i
].patternChar
!= ch
) {
2384 if (dtTypes
[i
].patternChar
!= dtTypes
[i
+1].patternChar
) {
2387 if (dtTypes
[i
+1].minLen
<= len
) {
2393 return strict
? -1 : bestRow
;
2397 FormatParser::isQuoteLiteral(const UnicodeString
& s
) {
2398 return (UBool
)(s
.charAt(0) == SINGLE_QUOTE
);
2401 // This function assumes the current itemIndex points to the quote literal.
2402 // Please call isQuoteLiteral prior to this function.
2404 FormatParser::getQuoteLiteral(UnicodeString
& quote
, int32_t *itemIndex
) {
2405 int32_t i
= *itemIndex
;
2408 if (items
[i
].charAt(0)==SINGLE_QUOTE
) {
2412 while ( i
< itemNumber
) {
2413 if ( items
[i
].charAt(0)==SINGLE_QUOTE
) {
2414 if ( (i
+1<itemNumber
) && (items
[i
+1].charAt(0)==SINGLE_QUOTE
)) {
2415 // two single quotes e.g. 'o''clock'
2416 quote
+= items
[i
++];
2417 quote
+= items
[i
++];
2434 FormatParser::isPatternSeparator(const UnicodeString
& field
) const {
2435 for (int32_t i
=0; i
<field
.length(); ++i
) {
2436 UChar c
= field
.charAt(i
);
2437 if ( (c
==SINGLE_QUOTE
) || (c
==BACKSLASH
) || (c
==SPACE
) || (c
==COLON
) ||
2438 (c
==QUOTATION_MARK
) || (c
==COMMA
) || (c
==HYPHEN
) ||(items
[i
].charAt(0)==DOT
) ) {
2448 DistanceInfo::~DistanceInfo() {}
2451 DistanceInfo::setTo(const DistanceInfo
& other
) {
2452 missingFieldMask
= other
.missingFieldMask
;
2453 extraFieldMask
= other
.extraFieldMask
;
2456 PatternMapIterator::PatternMapIterator(UErrorCode
& status
) :
2457 bootIndex(0), nodePtr(nullptr), matcher(nullptr), patternMap(nullptr)
2459 if (U_FAILURE(status
)) { return; }
2460 matcher
.adoptInsteadAndCheckErrorCode(new DateTimeMatcher(), status
);
2463 PatternMapIterator::~PatternMapIterator() {
2467 PatternMapIterator::set(PatternMap
& newPatternMap
) {
2468 this->patternMap
=&newPatternMap
;
2472 PatternMapIterator::getSkeleton() const {
2473 if ( nodePtr
== nullptr ) {
2477 return nodePtr
->skeleton
.getAlias();
2482 PatternMapIterator::hasNext() const {
2483 int32_t headIndex
= bootIndex
;
2484 PtnElem
*curPtr
= nodePtr
;
2486 if (patternMap
==nullptr) {
2489 while ( headIndex
< MAX_PATTERN_ENTRIES
) {
2490 if ( curPtr
!= nullptr ) {
2491 if ( curPtr
->next
!= nullptr ) {
2501 if ( patternMap
->boot
[headIndex
] != nullptr ) {
2514 PatternMapIterator::next() {
2515 while ( bootIndex
< MAX_PATTERN_ENTRIES
) {
2516 if ( nodePtr
!= nullptr ) {
2517 if ( nodePtr
->next
!= nullptr ) {
2518 nodePtr
= nodePtr
->next
.getAlias();
2528 if ( patternMap
->boot
[bootIndex
] != nullptr ) {
2529 nodePtr
= patternMap
->boot
[bootIndex
];
2538 if (nodePtr
!=nullptr) {
2539 matcher
->copyFrom(*nodePtr
->skeleton
);
2542 matcher
->copyFrom();
2548 SkeletonFields::SkeletonFields() {
2549 // Set initial values to zero
2553 void SkeletonFields::clear() {
2554 uprv_memset(chars
, 0, sizeof(chars
));
2555 uprv_memset(lengths
, 0, sizeof(lengths
));
2558 void SkeletonFields::copyFrom(const SkeletonFields
& other
) {
2559 uprv_memcpy(chars
, other
.chars
, sizeof(chars
));
2560 uprv_memcpy(lengths
, other
.lengths
, sizeof(lengths
));
2563 void SkeletonFields::clearField(int32_t field
) {
2568 UChar
SkeletonFields::getFieldChar(int32_t field
) const {
2569 return chars
[field
];
2572 int32_t SkeletonFields::getFieldLength(int32_t field
) const {
2573 return lengths
[field
];
2576 void SkeletonFields::populate(int32_t field
, const UnicodeString
& value
) {
2577 populate(field
, value
.charAt(0), value
.length());
2580 void SkeletonFields::populate(int32_t field
, UChar ch
, int32_t length
) {
2581 chars
[field
] = (int8_t) ch
;
2582 lengths
[field
] = (int8_t) length
;
2585 UBool
SkeletonFields::isFieldEmpty(int32_t field
) const {
2586 return lengths
[field
] == 0;
2589 UnicodeString
& SkeletonFields::appendTo(UnicodeString
& string
) const {
2590 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; ++i
) {
2591 appendFieldTo(i
, string
);
2596 UnicodeString
& SkeletonFields::appendFieldTo(int32_t field
, UnicodeString
& string
) const {
2597 UChar
ch(chars
[field
]);
2598 int32_t length
= (int32_t) lengths
[field
];
2600 for (int32_t i
=0; i
<length
; i
++) {
2606 UChar
SkeletonFields::getFirstChar() const {
2607 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; ++i
) {
2608 if (lengths
[i
] != 0) {
2616 PtnSkeleton::PtnSkeleton()
2617 : addedDefaultDayPeriod(FALSE
) {
2620 PtnSkeleton::PtnSkeleton(const PtnSkeleton
& other
) {
2624 void PtnSkeleton::copyFrom(const PtnSkeleton
& other
) {
2625 uprv_memcpy(type
, other
.type
, sizeof(type
));
2626 original
.copyFrom(other
.original
);
2627 baseOriginal
.copyFrom(other
.baseOriginal
);
2628 addedDefaultDayPeriod
= other
.addedDefaultDayPeriod
;
2631 void PtnSkeleton::clear() {
2632 uprv_memset(type
, 0, sizeof(type
));
2634 baseOriginal
.clear();
2638 PtnSkeleton::equals(const PtnSkeleton
& other
) const {
2639 return (original
== other
.original
)
2640 && (baseOriginal
== other
.baseOriginal
)
2641 && (uprv_memcmp(type
, other
.type
, sizeof(type
)) == 0);
2645 PtnSkeleton::getSkeleton() const {
2646 UnicodeString result
;
2647 result
= original
.appendTo(result
);
2649 if (addedDefaultDayPeriod
&& (pos
= result
.indexOf(LOW_A
)) >= 0) {
2650 // for backward compatibility: if DateTimeMatcher.set added a single 'a' that
2651 // was not in the provided skeleton, remove it here before returning skeleton.
2652 result
.remove(pos
, 1);
2658 PtnSkeleton::getBaseSkeleton() const {
2659 UnicodeString result
;
2660 result
= baseOriginal
.appendTo(result
);
2662 if (addedDefaultDayPeriod
&& (pos
= result
.indexOf(LOW_A
)) >= 0) {
2663 // for backward compatibility: if DateTimeMatcher.set added a single 'a' that
2664 // was not in the provided skeleton, remove it here before returning skeleton.
2665 result
.remove(pos
, 1);
2671 PtnSkeleton::getFirstChar() const {
2672 return baseOriginal
.getFirstChar();
2675 PtnSkeleton::~PtnSkeleton() {
2678 PtnElem::PtnElem(const UnicodeString
&basePat
, const UnicodeString
&pat
) :
2679 basePattern(basePat
), skeleton(nullptr), pattern(pat
), next(nullptr)
2683 PtnElem::~PtnElem() {
2686 DTSkeletonEnumeration::DTSkeletonEnumeration(PatternMap
& patternMap
, dtStrEnum type
, UErrorCode
& status
) : fSkeletons(nullptr) {
2688 PtnSkeleton
*curSkeleton
;
2693 fSkeletons
.adoptInsteadAndCheckErrorCode(new UVector(status
), status
);
2694 if (U_FAILURE(status
)) {
2698 for (bootIndex
=0; bootIndex
<MAX_PATTERN_ENTRIES
; ++bootIndex
) {
2699 curElem
= patternMap
.boot
[bootIndex
];
2700 while (curElem
!=nullptr) {
2702 case DT_BASESKELETON
:
2703 s
=curElem
->basePattern
;
2709 curSkeleton
=curElem
->skeleton
.getAlias();
2710 s
=curSkeleton
->getSkeleton();
2713 if ( !isCanonicalItem(s
) ) {
2714 LocalPointer
<UnicodeString
> newElem(new UnicodeString(s
), status
);
2715 if (U_FAILURE(status
)) {
2718 fSkeletons
->addElement(newElem
.getAlias(), status
);
2719 if (U_FAILURE(status
)) {
2720 fSkeletons
.adoptInstead(nullptr);
2723 newElem
.orphan(); // fSkeletons vector now owns the UnicodeString.
2725 curElem
= curElem
->next
.getAlias();
2728 if ((bootIndex
==MAX_PATTERN_ENTRIES
) && (curElem
!=nullptr) ) {
2729 status
= U_BUFFER_OVERFLOW_ERROR
;
2733 const UnicodeString
*
2734 DTSkeletonEnumeration::snext(UErrorCode
& status
) {
2735 if (U_SUCCESS(status
) && fSkeletons
.isValid() && pos
< fSkeletons
->size()) {
2736 return (const UnicodeString
*)fSkeletons
->elementAt(pos
++);
2742 DTSkeletonEnumeration::reset(UErrorCode
& /*status*/) {
2747 DTSkeletonEnumeration::count(UErrorCode
& /*status*/) const {
2748 return (fSkeletons
.isNull()) ? 0 : fSkeletons
->size();
2752 DTSkeletonEnumeration::isCanonicalItem(const UnicodeString
& item
) {
2753 if ( item
.length() != 1 ) {
2756 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2757 if (item
.charAt(0)==Canonical_Items
[i
]) {
2764 DTSkeletonEnumeration::~DTSkeletonEnumeration() {
2766 if (fSkeletons
.isValid()) {
2767 for (int32_t i
= 0; i
< fSkeletons
->size(); ++i
) {
2768 if ((s
= (UnicodeString
*)fSkeletons
->elementAt(i
)) != nullptr) {
2775 DTRedundantEnumeration::DTRedundantEnumeration() : pos(0), fPatterns(nullptr) {
2779 DTRedundantEnumeration::add(const UnicodeString
& pattern
, UErrorCode
& status
) {
2780 if (U_FAILURE(status
)) { return; }
2781 if (fPatterns
.isNull()) {
2782 fPatterns
.adoptInsteadAndCheckErrorCode(new UVector(status
), status
);
2783 if (U_FAILURE(status
)) {
2787 LocalPointer
<UnicodeString
> newElem(new UnicodeString(pattern
), status
);
2788 if (U_FAILURE(status
)) {
2791 fPatterns
->addElement(newElem
.getAlias(), status
);
2792 if (U_FAILURE(status
)) {
2793 fPatterns
.adoptInstead(nullptr);
2796 newElem
.orphan(); // fPatterns now owns the string.
2799 const UnicodeString
*
2800 DTRedundantEnumeration::snext(UErrorCode
& status
) {
2801 if (U_SUCCESS(status
) && fPatterns
.isValid() && pos
< fPatterns
->size()) {
2802 return (const UnicodeString
*)fPatterns
->elementAt(pos
++);
2808 DTRedundantEnumeration::reset(UErrorCode
& /*status*/) {
2813 DTRedundantEnumeration::count(UErrorCode
& /*status*/) const {
2814 return (fPatterns
.isNull()) ? 0 : fPatterns
->size();
2818 DTRedundantEnumeration::isCanonicalItem(const UnicodeString
& item
) const {
2819 if ( item
.length() != 1 ) {
2822 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2823 if (item
.charAt(0)==Canonical_Items
[i
]) {
2830 DTRedundantEnumeration::~DTRedundantEnumeration() {
2832 if (fPatterns
.isValid()) {
2833 for (int32_t i
= 0; i
< fPatterns
->size(); ++i
) {
2834 if ((s
= (UnicodeString
*)fPatterns
->elementAt(i
)) != nullptr) {
2842 * This is a utility function used by the various date formatting classes to determine whether a particular pattern string will produce an all-numeric date.
2843 * It does this by examining the pattern string. If the range between the first d, M, or y and the last d, M, or y in the pattern contains nothing but d, M, y,
2844 * punctuation, whitespace, and Unicode right-to-left marks, and it doesn't contain more than two Ms in a row, it's considered to have a "numeric core"--
2845 * that is, the part of the pattern that generates a date (minus fields like the day of the week and the era) produces an all-numeric date.
2847 extern UBool
datePatternHasNumericCore(const UnicodeString
& datePattern
) {
2848 StringCharacterIterator it
= StringCharacterIterator(datePattern
);
2849 int32_t coreStart
= -1;
2850 int32_t coreEnd
= -1;
2851 int32_t firstLetterAfterCoreStart
= -1;
2853 UBool sawD
= FALSE
, sawY
= FALSE
;
2854 for (UChar c
= it
.first(); it
.hasNext(); c
= it
.next()) {
2856 case u
'y': case u
'Y': case u
'r': case u
'u':
2857 case u
'M': case u
'L': case u
'd':
2858 if (coreStart
== -1) {
2859 coreStart
= it
.getIndex();
2861 coreEnd
= it
.getIndex();
2863 case u
'y': case u
'Y': case u
'r': case u
'u':
2866 case u
'M': case u
'L':
2867 // if the pattern contains more than 2 M's, the month is a word, not a number, which means
2868 // we don't have a numeric core
2883 if (coreStart
!= -1 && firstLetterAfterCoreStart
== -1) {
2884 firstLetterAfterCoreStart
= it
.getIndex();
2886 } else if (!u_isspace(c
) && !u_ispunct(c
) && c
!= u
'\u200f') {
2887 // the numeric core must contain nothing but d, M, y, whitespace, punctuation, and the Unicode right-to-left mark
2894 // if we didn't find d, M, or y in the pattern, return FALSE
2895 if (coreStart
< 0 || coreEnd
< 0) {
2899 // if there's quoted literal text anywhere in the pattern, whether in the "core" or not, treat it as though
2900 // we don't have a numeric core
2901 if (datePattern
.indexOf(u
'\'') != -1) {
2905 // if we found a letter other than d, M, or y between the first d, M, or y and the last one,
2906 // we don't have a numeric core
2907 if (firstLetterAfterCoreStart
!= -1 && firstLetterAfterCoreStart
< coreEnd
) {
2911 // if the format contains only one numeric field (out of d, M, or y), we don't count it as a numeric core
2912 if (((numMs
> 0) ? 1 : 0) + (sawY
? 1 : 0) + (sawD
? 1 : 0) <= 1) {
2916 // if we get to here, we have a numeric core
2923 #endif /* #if !UCONFIG_NO_FORMATTING */