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/simpleformatter.h"
23 #include "unicode/smpdtfmt.h"
24 #include "unicode/udat.h"
25 #include "unicode/udatpg.h"
26 #include "unicode/uniset.h"
27 #include "unicode/uloc.h"
28 #include "unicode/ures.h"
29 #include "unicode/ustring.h"
30 #include "unicode/rep.h"
40 #include "dtptngen_impl.h"
45 #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
47 * If we are on EBCDIC, use an iterator which will
48 * traverse the bundles in ASCII order.
50 #define U_USE_ASCII_BUNDLE_ITERATOR
51 #define U_SORT_ASCII_BUNDLE_ITERATOR
54 #if defined(U_USE_ASCII_BUNDLE_ITERATOR)
56 #include "unicode/ustring.h"
61 UResourceBundle
*item
;
64 struct UResourceBundleAIterator
{
65 UResourceBundle
*bund
;
71 /* Must be C linkage to pass function pointer to the sort function */
75 static int32_t U_CALLCONV
76 ures_a_codepointSort(const void *context
, const void *left
, const void *right
) {
77 //CompareContext *cmp=(CompareContext *)context;
78 return u_strcmp(((const UResAEntry
*)left
)->key
,
79 ((const UResAEntry
*)right
)->key
);
84 static void ures_a_open(UResourceBundleAIterator
*aiter
, UResourceBundle
*bund
, UErrorCode
*status
) {
85 if(U_FAILURE(*status
)) {
89 aiter
->num
= ures_getSize(aiter
->bund
);
91 #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
92 aiter
->entries
= nullptr;
94 aiter
->entries
= (UResAEntry
*)uprv_malloc(sizeof(UResAEntry
)*aiter
->num
);
95 for(int i
=0;i
<aiter
->num
;i
++) {
96 aiter
->entries
[i
].item
= ures_getByIndex(aiter
->bund
, i
, nullptr, status
);
97 const char *akey
= ures_getKey(aiter
->entries
[i
].item
);
98 int32_t len
= uprv_strlen(akey
)+1;
99 aiter
->entries
[i
].key
= (UChar
*)uprv_malloc(len
*sizeof(UChar
));
100 u_charsToUChars(akey
, aiter
->entries
[i
].key
, len
);
102 uprv_sortArray(aiter
->entries
, aiter
->num
, sizeof(UResAEntry
), ures_a_codepointSort
, nullptr, TRUE
, status
);
106 static void ures_a_close(UResourceBundleAIterator
*aiter
) {
107 #if defined(U_SORT_ASCII_BUNDLE_ITERATOR)
108 for(int i
=0;i
<aiter
->num
;i
++) {
109 uprv_free(aiter
->entries
[i
].key
);
110 ures_close(aiter
->entries
[i
].item
);
115 static const UChar
*ures_a_getNextString(UResourceBundleAIterator
*aiter
, int32_t *len
, const char **key
, UErrorCode
*err
) {
116 #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
117 return ures_getNextString(aiter
->bund
, len
, key
, err
);
119 if(U_FAILURE(*err
)) return nullptr;
120 UResourceBundle
*item
= aiter
->entries
[aiter
->cursor
].item
;
121 const UChar
* ret
= ures_getString(item
, len
, err
);
122 *key
= ures_getKey(item
);
134 // *****************************************************************************
135 // class DateTimePatternGenerator
136 // *****************************************************************************
137 static const UChar Canonical_Items
[] = {
139 CAP_G
, LOW_Y
, CAP_Q
, CAP_M
, LOW_W
, CAP_W
, CAP_E
,
140 CAP_D
, CAP_F
, LOW_D
, LOW_A
, // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
141 CAP_H
, LOW_M
, LOW_S
, CAP_S
, LOW_V
, 0
144 static const dtTypeElem dtTypes
[] = {
145 // patternChar, field, type, minLen, weight
146 {CAP_G
, UDATPG_ERA_FIELD
, DT_SHORT
, 1, 3,},
147 {CAP_G
, UDATPG_ERA_FIELD
, DT_LONG
, 4, 0},
148 {CAP_G
, UDATPG_ERA_FIELD
, DT_NARROW
, 5, 0},
150 {LOW_Y
, UDATPG_YEAR_FIELD
, DT_NUMERIC
, 1, 20},
151 {CAP_Y
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 20},
152 {LOW_U
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ 2*DT_DELTA
, 1, 20},
153 {LOW_R
, UDATPG_YEAR_FIELD
, DT_NUMERIC
+ 3*DT_DELTA
, 1, 20},
154 {CAP_U
, UDATPG_YEAR_FIELD
, DT_SHORT
, 1, 3},
155 {CAP_U
, UDATPG_YEAR_FIELD
, DT_LONG
, 4, 0},
156 {CAP_U
, UDATPG_YEAR_FIELD
, DT_NARROW
, 5, 0},
158 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_NUMERIC
, 1, 2},
159 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_SHORT
, 3, 0},
160 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_LONG
, 4, 0},
161 {CAP_Q
, UDATPG_QUARTER_FIELD
, DT_NARROW
, 5, 0},
162 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2},
163 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
164 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
165 {LOW_Q
, UDATPG_QUARTER_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
167 {CAP_M
, UDATPG_MONTH_FIELD
, DT_NUMERIC
, 1, 2},
168 {CAP_M
, UDATPG_MONTH_FIELD
, DT_SHORT
, 3, 0},
169 {CAP_M
, UDATPG_MONTH_FIELD
, DT_LONG
, 4, 0},
170 {CAP_M
, UDATPG_MONTH_FIELD
, DT_NARROW
, 5, 0},
171 {CAP_L
, UDATPG_MONTH_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2},
172 {CAP_L
, UDATPG_MONTH_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
173 {CAP_L
, UDATPG_MONTH_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
174 {CAP_L
, UDATPG_MONTH_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
175 {LOW_L
, UDATPG_MONTH_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 1},
177 {LOW_W
, UDATPG_WEEK_OF_YEAR_FIELD
, DT_NUMERIC
, 1, 2},
179 {CAP_W
, UDATPG_WEEK_OF_MONTH_FIELD
, DT_NUMERIC
, 1, 0},
181 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
, 1, 3},
182 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_LONG
, 4, 0},
183 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
, 5, 0},
184 {CAP_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORTER
, 6, 0},
185 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_NUMERIC
+ 2*DT_DELTA
, 1, 2},
186 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
- 2*DT_DELTA
, 3, 0},
187 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_LONG
- 2*DT_DELTA
, 4, 0},
188 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
- 2*DT_DELTA
, 5, 0},
189 {LOW_C
, UDATPG_WEEKDAY_FIELD
, DT_SHORTER
- 2*DT_DELTA
, 6, 0},
190 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2}, // LOW_E is currently not used in CLDR data, should not be canonical
191 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORT
- DT_DELTA
, 3, 0},
192 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
193 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
194 {LOW_E
, UDATPG_WEEKDAY_FIELD
, DT_SHORTER
- DT_DELTA
, 6, 0},
196 {LOW_D
, UDATPG_DAY_FIELD
, DT_NUMERIC
, 1, 2},
197 {LOW_G
, UDATPG_DAY_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 20}, // really internal use, so we don't care
199 {CAP_D
, UDATPG_DAY_OF_YEAR_FIELD
, DT_NUMERIC
, 1, 3},
201 {CAP_F
, UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD
, DT_NUMERIC
, 1, 0},
203 {LOW_A
, UDATPG_DAYPERIOD_FIELD
, DT_SHORT
, 1, 3},
204 {LOW_A
, UDATPG_DAYPERIOD_FIELD
, DT_LONG
, 4, 0},
205 {LOW_A
, UDATPG_DAYPERIOD_FIELD
, DT_NARROW
, 5, 0},
206 {LOW_B
, UDATPG_DAYPERIOD_FIELD
, DT_SHORT
- DT_DELTA
, 1, 3},
207 {LOW_B
, UDATPG_DAYPERIOD_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
208 {LOW_B
, UDATPG_DAYPERIOD_FIELD
, DT_NARROW
- DT_DELTA
, 5, 0},
209 // b needs to be closer to a than to B, so we make this 3*DT_DELTA
210 {CAP_B
, UDATPG_DAYPERIOD_FIELD
, DT_SHORT
- 3*DT_DELTA
, 1, 3},
211 {CAP_B
, UDATPG_DAYPERIOD_FIELD
, DT_LONG
- 3*DT_DELTA
, 4, 0},
212 {CAP_B
, UDATPG_DAYPERIOD_FIELD
, DT_NARROW
- 3*DT_DELTA
, 5, 0},
214 {CAP_H
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 10*DT_DELTA
, 1, 2}, // 24 hour
215 {LOW_K
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 11*DT_DELTA
, 1, 2}, // 24 hour
216 {LOW_H
, UDATPG_HOUR_FIELD
, DT_NUMERIC
, 1, 2}, // 12 hour
217 {CAP_K
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 2}, // 12 hour
218 // The C code has had versions of the following 3, keep & update. Should not need these, but...
219 // Without these, certain tests using e.g. staticGetSkeleton fail because j/J in patterns
220 // get skipped instead of mapped to the right hour chars, for example in
221 // DateFormatTest::TestPatternFromSkeleton
222 // IntlTestDateTimePatternGeneratorAPI:: testStaticGetSkeleton
223 // DateIntervalFormatTest::testTicket11985
224 // Need to investigate better handling of jJC replacement e.g. in staticGetSkeleton.
225 {CAP_J
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 5*DT_DELTA
, 1, 2}, // 12/24 hour no AM/PM
226 {LOW_J
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 6*DT_DELTA
, 1, 6}, // 12/24 hour
227 {CAP_C
, UDATPG_HOUR_FIELD
, DT_NUMERIC
+ 7*DT_DELTA
, 1, 6}, // 12/24 hour with preferred dayPeriods for 12
229 {LOW_M
, UDATPG_MINUTE_FIELD
, DT_NUMERIC
, 1, 2},
231 {LOW_S
, UDATPG_SECOND_FIELD
, DT_NUMERIC
, 1, 2},
232 {CAP_A
, UDATPG_SECOND_FIELD
, DT_NUMERIC
+ DT_DELTA
, 1, 1000},
234 {CAP_S
, UDATPG_FRACTIONAL_SECOND_FIELD
, DT_NUMERIC
, 1, 1000},
236 {LOW_V
, UDATPG_ZONE_FIELD
, DT_SHORT
- 2*DT_DELTA
, 1, 0},
237 {LOW_V
, UDATPG_ZONE_FIELD
, DT_LONG
- 2*DT_DELTA
, 4, 0},
238 {LOW_Z
, UDATPG_ZONE_FIELD
, DT_SHORT
, 1, 3},
239 {LOW_Z
, UDATPG_ZONE_FIELD
, DT_LONG
, 4, 0},
240 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 3},
241 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
242 {CAP_Z
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 5, 0},
243 {CAP_O
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 1, 0},
244 {CAP_O
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
245 {CAP_V
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 1, 0},
246 {CAP_V
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 2, 0},
247 {CAP_V
, UDATPG_ZONE_FIELD
, DT_LONG
-1 - DT_DELTA
, 3, 0},
248 {CAP_V
, UDATPG_ZONE_FIELD
, DT_LONG
-2 - DT_DELTA
, 4, 0},
249 {CAP_X
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 0},
250 {CAP_X
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 2, 0},
251 {CAP_X
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
252 {LOW_X
, UDATPG_ZONE_FIELD
, DT_NARROW
- DT_DELTA
, 1, 0},
253 {LOW_X
, UDATPG_ZONE_FIELD
, DT_SHORT
- DT_DELTA
, 2, 0},
254 {LOW_X
, UDATPG_ZONE_FIELD
, DT_LONG
- DT_DELTA
, 4, 0},
256 {0, UDATPG_FIELD_COUNT
, 0, 0, 0} , // last row of dtTypes[]
259 static const char* const CLDR_FIELD_APPEND
[] = {
260 "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week",
261 "*", "*", "Day", "*", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
262 "Hour", "Minute", "Second", "*", "Timezone"
265 static const char* const CLDR_FIELD_NAME
[UDATPG_FIELD_COUNT
] = {
266 "era", "year", "quarter", "month", "week", "weekOfMonth", "weekday",
267 "dayOfYear", "weekdayOfMonth", "day", "dayperiod", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
268 "hour", "minute", "second", "*", "zone"
271 static const char* const CLDR_FIELD_WIDTH
[] = { // [UDATPG_WIDTH_COUNT]
272 "", "-short", "-narrow"
275 // TODO(ticket:13619): remove when definition uncommented in dtptngen.h.
276 static const int32_t UDATPG_WIDTH_COUNT
= UDATPG_NARROW
+ 1;
277 static constexpr UDateTimePGDisplayWidth UDATPG_WIDTH_APPENDITEM
= UDATPG_WIDE
;
278 static constexpr int32_t UDATPG_FIELD_KEY_MAX
= 24; // max length of CLDR field tag (type + width)
281 static const UChar UDATPG_ItemFormat
[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A,
282 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524
284 //static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ"
286 static const char DT_DateTimePatternsTag
[]="DateTimePatterns";
287 static const char DT_DateTimeCalendarTag
[]="calendar";
288 static const char DT_DateTimeGregorianTag
[]="gregorian";
289 static const char DT_DateTimeAppendItemsTag
[]="appendItems";
290 static const char DT_DateTimeFieldsTag
[]="fields";
291 static const char DT_DateTimeAvailableFormatsTag
[]="availableFormats";
292 //static const UnicodeString repeatedPattern=UnicodeString(repeatedPatterns);
294 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator
)
295 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration
)
296 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration
)
298 DateTimePatternGenerator
* U_EXPORT2
299 DateTimePatternGenerator::createInstance(UErrorCode
& status
) {
300 return createInstance(Locale::getDefault(), status
);
303 DateTimePatternGenerator
* U_EXPORT2
304 DateTimePatternGenerator::createInstance(const Locale
& locale
, UErrorCode
& status
, UBool skipICUData
) {
305 if (U_FAILURE(status
)) {
308 LocalPointer
<DateTimePatternGenerator
> result(
309 new DateTimePatternGenerator(locale
, status
, skipICUData
), status
);
310 return U_SUCCESS(status
) ? result
.orphan() : nullptr;
313 DateTimePatternGenerator
* U_EXPORT2
314 DateTimePatternGenerator::createEmptyInstance(UErrorCode
& status
) {
315 if (U_FAILURE(status
)) {
318 LocalPointer
<DateTimePatternGenerator
> result(
319 new DateTimePatternGenerator(status
), status
);
320 return U_SUCCESS(status
) ? result
.orphan() : nullptr;
323 DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode
&status
) :
324 skipMatcher(nullptr),
325 fAvailableFormatKeyHash(nullptr),
326 internalErrorCode(U_ZERO_ERROR
)
328 fp
= new FormatParser();
329 dtMatcher
= new DateTimeMatcher();
330 distanceInfo
= new DistanceInfo();
331 patternMap
= new PatternMap();
332 if (fp
== nullptr || dtMatcher
== nullptr || distanceInfo
== nullptr || patternMap
== nullptr) {
333 internalErrorCode
= status
= U_MEMORY_ALLOCATION_ERROR
;
337 DateTimePatternGenerator::DateTimePatternGenerator(const Locale
& locale
, UErrorCode
&status
, UBool skipICUData
) :
338 skipMatcher(nullptr),
339 fAvailableFormatKeyHash(nullptr),
340 internalErrorCode(U_ZERO_ERROR
)
342 fp
= new FormatParser();
343 dtMatcher
= new DateTimeMatcher();
344 distanceInfo
= new DistanceInfo();
345 patternMap
= new PatternMap();
346 if (fp
== nullptr || dtMatcher
== nullptr || distanceInfo
== nullptr || patternMap
== nullptr) {
347 internalErrorCode
= status
= U_MEMORY_ALLOCATION_ERROR
;
350 initData(locale
, status
, skipICUData
);
354 DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator
& other
) :
356 skipMatcher(nullptr),
357 fAvailableFormatKeyHash(nullptr),
358 internalErrorCode(U_ZERO_ERROR
)
360 fp
= new FormatParser();
361 dtMatcher
= new DateTimeMatcher();
362 distanceInfo
= new DistanceInfo();
363 patternMap
= new PatternMap();
364 if (fp
== nullptr || dtMatcher
== nullptr || distanceInfo
== nullptr || patternMap
== nullptr) {
365 internalErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
370 DateTimePatternGenerator
&
371 DateTimePatternGenerator::operator=(const DateTimePatternGenerator
& other
) {
373 if (&other
== this) {
376 internalErrorCode
= other
.internalErrorCode
;
377 pLocale
= other
.pLocale
;
378 fDefaultHourFormatChar
= other
.fDefaultHourFormatChar
;
380 dtMatcher
->copyFrom(other
.dtMatcher
->skeleton
);
381 *distanceInfo
= *(other
.distanceInfo
);
382 dateTimeFormat
= other
.dateTimeFormat
;
383 decimal
= other
.decimal
;
384 // NUL-terminate for the C API.
385 dateTimeFormat
.getTerminatedBuffer();
386 decimal
.getTerminatedBuffer();
388 if ( other
.skipMatcher
== nullptr ) {
389 skipMatcher
= nullptr;
392 skipMatcher
= new DateTimeMatcher(*other
.skipMatcher
);
393 if (skipMatcher
== nullptr)
395 internalErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
399 for (int32_t i
=0; i
< UDATPG_FIELD_COUNT
; ++i
) {
400 appendItemFormats
[i
] = other
.appendItemFormats
[i
];
401 appendItemFormats
[i
].getTerminatedBuffer(); // NUL-terminate for the C API.
402 for (int32_t j
=0; j
< UDATPG_WIDTH_COUNT
; ++j
) {
403 fieldDisplayNames
[i
][j
] = other
.fieldDisplayNames
[i
][j
];
404 fieldDisplayNames
[i
][j
].getTerminatedBuffer(); // NUL-terminate for the C API.
407 patternMap
->copyFrom(*other
.patternMap
, internalErrorCode
);
408 copyHashtable(other
.fAvailableFormatKeyHash
, internalErrorCode
);
414 DateTimePatternGenerator::operator==(const DateTimePatternGenerator
& other
) const {
415 if (this == &other
) {
418 if ((pLocale
==other
.pLocale
) && (patternMap
->equals(*other
.patternMap
)) &&
419 (dateTimeFormat
==other
.dateTimeFormat
) && (decimal
==other
.decimal
)) {
420 for ( int32_t i
=0 ; i
<UDATPG_FIELD_COUNT
; ++i
) {
421 if (appendItemFormats
[i
] != other
.appendItemFormats
[i
]) {
424 for (int32_t j
=0; j
< UDATPG_WIDTH_COUNT
; ++j
) {
425 if (fieldDisplayNames
[i
][j
] != other
.fieldDisplayNames
[i
][j
]) {
438 DateTimePatternGenerator::operator!=(const DateTimePatternGenerator
& other
) const {
439 return !operator==(other
);
442 DateTimePatternGenerator::~DateTimePatternGenerator() {
443 if (fAvailableFormatKeyHash
!=nullptr) {
444 delete fAvailableFormatKeyHash
;
447 if (fp
!= nullptr) delete fp
;
448 if (dtMatcher
!= nullptr) delete dtMatcher
;
449 if (distanceInfo
!= nullptr) delete distanceInfo
;
450 if (patternMap
!= nullptr) delete patternMap
;
451 if (skipMatcher
!= nullptr) delete skipMatcher
;
456 UInitOnce initOnce
= U_INITONCE_INITIALIZER
;
457 UHashtable
*localeToAllowedHourFormatsMap
= nullptr;
459 // Value deleter for hashmap.
460 U_CFUNC
void U_CALLCONV
deleteAllowedHourFormats(void *ptr
) {
464 // Close hashmap at cleanup.
465 U_CFUNC UBool U_CALLCONV
allowedHourFormatsCleanup() {
466 uhash_close(localeToAllowedHourFormatsMap
);
470 enum AllowedHourFormat
{
471 ALLOWED_HOUR_FORMAT_UNKNOWN
= -1,
472 ALLOWED_HOUR_FORMAT_h
,
473 ALLOWED_HOUR_FORMAT_H
,
474 ALLOWED_HOUR_FORMAT_K
, // Added ICU-20383, used by JP
475 ALLOWED_HOUR_FORMAT_k
, // Added ICU-20383, not currently used
476 ALLOWED_HOUR_FORMAT_hb
,
477 ALLOWED_HOUR_FORMAT_hB
,
478 ALLOWED_HOUR_FORMAT_Kb
, // Added ICU-20383, not currently used
479 ALLOWED_HOUR_FORMAT_KB
, // Added ICU-20383, not currently used
480 // ICU-20383 The following are unlikely and not currently used
481 ALLOWED_HOUR_FORMAT_Hb
,
482 ALLOWED_HOUR_FORMAT_HB
488 DateTimePatternGenerator::initData(const Locale
& locale
, UErrorCode
&status
, UBool skipICUData
) {
489 //const char *baseLangName = locale.getBaseName(); // unused
491 skipMatcher
= nullptr;
492 fAvailableFormatKeyHash
=nullptr;
493 addCanonicalItems(status
);
495 addICUPatterns(locale
, status
); // skip to prevent circular dependency when called from SimpleDateFormat::construct
497 addCLDRData(locale
, status
);
498 setDateTimeFromCalendar(locale
, status
);
499 setDecimalSymbols(locale
, status
);
500 umtx_initOnce(initOnce
, loadAllowedHourFormatsData
, status
);
501 getAllowedHourFormats(locale
, status
);
502 // If any of the above methods failed then the object is in an invalid state.
503 internalErrorCode
= status
;
504 } // DateTimePatternGenerator::initData
508 struct AllowedHourFormatsSink
: public ResourceSink
{
509 // Initialize sub-sinks.
510 AllowedHourFormatsSink() {}
511 virtual ~AllowedHourFormatsSink();
513 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
514 UErrorCode
&errorCode
) {
515 ResourceTable timeData
= value
.getTable(errorCode
);
516 if (U_FAILURE(errorCode
)) { return; }
517 for (int32_t i
= 0; timeData
.getKeyAndValue(i
, key
, value
); ++i
) {
518 const char *regionOrLocale
= key
;
519 ResourceTable formatList
= value
.getTable(errorCode
);
520 if (U_FAILURE(errorCode
)) { return; }
521 // below we construct a list[] that has an entry for the "preferred" value at [0],
522 // followed by 1 or more entries for the "allowed" values, terminated with an
523 // entry for ALLOWED_HOUR_FORMAT_UNKNOWN (not included in length below)
524 LocalMemory
<int32_t> list
;
526 int32_t preferredFormat
= ALLOWED_HOUR_FORMAT_UNKNOWN
;
527 for (int32_t j
= 0; formatList
.getKeyAndValue(j
, key
, value
); ++j
) {
528 if (uprv_strcmp(key
, "allowed") == 0) {
529 if (value
.getType() == URES_STRING
) {
530 length
= 2; // 1 preferred to add later, 1 allowed to add now
531 if (list
.allocateInsteadAndReset(length
+ 1) == nullptr) {
532 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
535 list
[1] = getHourFormatFromUnicodeString(value
.getUnicodeString(errorCode
));
538 ResourceArray allowedFormats
= value
.getArray(errorCode
);
539 length
= allowedFormats
.getSize() + 1; // 1 preferred, getSize allowed
540 if (list
.allocateInsteadAndReset(length
+ 1) == nullptr) {
541 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
544 for (int32_t k
= 1; k
< length
; ++k
) {
545 allowedFormats
.getValue(k
-1, value
);
546 list
[k
] = getHourFormatFromUnicodeString(value
.getUnicodeString(errorCode
));
549 } else if (uprv_strcmp(key
, "preferred") == 0) {
550 preferredFormat
= getHourFormatFromUnicodeString(value
.getUnicodeString(errorCode
));
554 list
[0] = (preferredFormat
!=ALLOWED_HOUR_FORMAT_UNKNOWN
)? preferredFormat
: list
[1];
556 // fallback handling for missing data
557 length
= 2; // 1 preferred, 1 allowed
558 if (list
.allocateInsteadAndReset(length
+ 1) == nullptr) {
559 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
562 list
[0] = (preferredFormat
!=ALLOWED_HOUR_FORMAT_UNKNOWN
)? preferredFormat
: ALLOWED_HOUR_FORMAT_H
;
565 list
[length
] = ALLOWED_HOUR_FORMAT_UNKNOWN
;
566 // At this point list[] will have at least two non-ALLOWED_HOUR_FORMAT_UNKNOWN entries,
567 // followed by ALLOWED_HOUR_FORMAT_UNKNOWN.
568 uhash_put(localeToAllowedHourFormatsMap
, const_cast<char *>(regionOrLocale
), list
.orphan(), &errorCode
);
569 if (U_FAILURE(errorCode
)) { return; }
573 AllowedHourFormat
getHourFormatFromUnicodeString(const UnicodeString
&s
) {
574 if (s
.length() == 1) {
575 if (s
[0] == LOW_H
) { return ALLOWED_HOUR_FORMAT_h
; }
576 if (s
[0] == CAP_H
) { return ALLOWED_HOUR_FORMAT_H
; }
577 if (s
[0] == CAP_K
) { return ALLOWED_HOUR_FORMAT_K
; }
578 if (s
[0] == LOW_K
) { return ALLOWED_HOUR_FORMAT_k
; }
579 } else if (s
.length() == 2) {
580 if (s
[0] == LOW_H
&& s
[1] == LOW_B
) { return ALLOWED_HOUR_FORMAT_hb
; }
581 if (s
[0] == LOW_H
&& s
[1] == CAP_B
) { return ALLOWED_HOUR_FORMAT_hB
; }
582 if (s
[0] == CAP_K
&& s
[1] == LOW_B
) { return ALLOWED_HOUR_FORMAT_Kb
; }
583 if (s
[0] == CAP_K
&& s
[1] == CAP_B
) { return ALLOWED_HOUR_FORMAT_KB
; }
584 if (s
[0] == CAP_H
&& s
[1] == LOW_B
) { return ALLOWED_HOUR_FORMAT_Hb
; }
585 if (s
[0] == CAP_H
&& s
[1] == CAP_B
) { return ALLOWED_HOUR_FORMAT_HB
; }
588 return ALLOWED_HOUR_FORMAT_UNKNOWN
;
594 AllowedHourFormatsSink::~AllowedHourFormatsSink() {}
596 U_CFUNC
void U_CALLCONV
DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode
&status
) {
597 if (U_FAILURE(status
)) { return; }
598 localeToAllowedHourFormatsMap
= uhash_open(
599 uhash_hashChars
, uhash_compareChars
, nullptr, &status
);
600 if (U_FAILURE(status
)) { return; }
602 uhash_setValueDeleter(localeToAllowedHourFormatsMap
, deleteAllowedHourFormats
);
603 ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS
, allowedHourFormatsCleanup
);
605 LocalUResourceBundlePointer
rb(ures_openDirect(nullptr, "supplementalData", &status
));
606 if (U_FAILURE(status
)) { return; }
608 AllowedHourFormatsSink sink
;
609 // TODO: Currently in the enumeration each table allocates a new array.
610 // Try to reduce the number of memory allocations. Consider storing a
611 // UVector32 with the concatenation of all of the sub-arrays, put the start index
612 // into the hashmap, store 6 single-value sub-arrays right at the beginning of the
613 // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime
614 // object. Remember to clean up the vector, too.
615 ures_getAllItemsWithFallback(rb
.getAlias(), "timeData", sink
, status
);
618 void DateTimePatternGenerator::getAllowedHourFormats(const Locale
&locale
, UErrorCode
&status
) {
619 if (U_FAILURE(status
)) { return; }
620 Locale
maxLocale(locale
);
621 const char *locName
= maxLocale
.getName();
622 if (*locName
==0 || uprv_strcmp(locName
,"root")==0 || uprv_strcmp(locName
,"und")==0) {
623 maxLocale
= Locale("und_001");
625 maxLocale
.addLikelySubtags(status
);
626 if (U_FAILURE(status
)) {
631 const char *country
= maxLocale
.getCountry();
632 if (*country
== '\0') { country
= "001"; }
633 const char *language
= maxLocale
.getLanguage();
635 CharString langCountry
;
636 langCountry
.append(language
, static_cast<int32_t>(uprv_strlen(language
)), status
);
637 langCountry
.append('_', status
);
638 langCountry
.append(country
, static_cast<int32_t>(uprv_strlen(country
)), status
);
640 int32_t *allowedFormats
;
641 allowedFormats
= (int32_t *)uhash_get(localeToAllowedHourFormatsMap
, langCountry
.data());
642 if (allowedFormats
== nullptr) {
643 allowedFormats
= (int32_t *)uhash_get(localeToAllowedHourFormatsMap
, const_cast<char *>(country
));
646 if (allowedFormats
!= nullptr) { // Lookup is successful
647 // Here allowedFormats points to a list consisting of key for preferredFormat,
648 // followed by one or more keys for allowedFormats, then followed by ALLOWED_HOUR_FORMAT_UNKNOWN.
649 switch (allowedFormats
[0]) {
650 case ALLOWED_HOUR_FORMAT_h
: fDefaultHourFormatChar
= LOW_H
; break;
651 case ALLOWED_HOUR_FORMAT_H
: fDefaultHourFormatChar
= CAP_H
; break;
652 case ALLOWED_HOUR_FORMAT_K
: fDefaultHourFormatChar
= CAP_K
; break;
653 case ALLOWED_HOUR_FORMAT_k
: fDefaultHourFormatChar
= LOW_K
; break;
654 default: fDefaultHourFormatChar
= CAP_H
; break;
656 for (int32_t i
= 0; i
< UPRV_LENGTHOF(fAllowedHourFormats
); ++i
) {
657 fAllowedHourFormats
[i
] = allowedFormats
[i
+ 1];
658 if (fAllowedHourFormats
[i
] == ALLOWED_HOUR_FORMAT_UNKNOWN
) {
662 } else { // Lookup failed, twice
663 fDefaultHourFormatChar
= CAP_H
;
664 fAllowedHourFormats
[0] = ALLOWED_HOUR_FORMAT_H
;
665 fAllowedHourFormats
[1] = ALLOWED_HOUR_FORMAT_UNKNOWN
;
670 DateTimePatternGenerator::getSkeleton(const UnicodeString
& pattern
, UErrorCode
&
673 DateTimeMatcher matcher
;
674 PtnSkeleton localSkeleton
;
675 matcher
.set(pattern
, &fp2
, localSkeleton
);
676 return localSkeleton
.getSkeleton();
680 DateTimePatternGenerator::staticGetSkeleton(
681 const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
683 DateTimeMatcher matcher
;
684 PtnSkeleton localSkeleton
;
685 matcher
.set(pattern
, &fp
, localSkeleton
);
686 return localSkeleton
.getSkeleton();
690 DateTimePatternGenerator::getBaseSkeleton(const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
692 DateTimeMatcher matcher
;
693 PtnSkeleton localSkeleton
;
694 matcher
.set(pattern
, &fp2
, localSkeleton
);
695 return localSkeleton
.getBaseSkeleton();
699 DateTimePatternGenerator::staticGetBaseSkeleton(
700 const UnicodeString
& pattern
, UErrorCode
& /*status*/) {
702 DateTimeMatcher matcher
;
703 PtnSkeleton localSkeleton
;
704 matcher
.set(pattern
, &fp
, localSkeleton
);
705 return localSkeleton
.getBaseSkeleton();
709 DateTimePatternGenerator::addICUPatterns(const Locale
& locale
, UErrorCode
& status
) {
710 if (U_FAILURE(status
)) { return; }
711 UnicodeString dfPattern
;
712 UnicodeString conflictingString
;
715 // Load with ICU patterns
716 for (int32_t i
=DateFormat::kFull
; i
<=DateFormat::kShort
; i
++) {
717 DateFormat::EStyle style
= (DateFormat::EStyle
)i
;
718 df
= DateFormat::createDateInstance(style
, locale
);
719 SimpleDateFormat
* sdf
;
720 if (df
!= nullptr && (sdf
= dynamic_cast<SimpleDateFormat
*>(df
)) != nullptr) {
721 sdf
->toPattern(dfPattern
);
722 addPattern(dfPattern
, FALSE
, conflictingString
, status
);
724 // TODO Maybe we should return an error when the date format isn't simple.
726 if (U_FAILURE(status
)) { return; }
728 df
= DateFormat::createTimeInstance(style
, locale
);
729 if (df
!= nullptr && (sdf
= dynamic_cast<SimpleDateFormat
*>(df
)) != nullptr) {
730 sdf
->toPattern(dfPattern
);
731 addPattern(dfPattern
, FALSE
, conflictingString
, status
);
733 // TODO: C++ and Java are inconsistent (see #12568).
734 // C++ uses MEDIUM, but Java uses SHORT.
735 if ( i
==DateFormat::kShort
&& !dfPattern
.isEmpty() ) {
736 consumeShortTimePattern(dfPattern
, status
);
739 // TODO Maybe we should return an error when the date format isn't simple.
741 if (U_FAILURE(status
)) { return; }
746 DateTimePatternGenerator::hackTimes(const UnicodeString
& hackPattern
, UErrorCode
& status
) {
747 UnicodeString conflictingString
;
749 fp
->set(hackPattern
);
752 for (int32_t i
=0; i
<fp
->itemNumber
; ++i
) {
753 UnicodeString field
= fp
->items
[i
];
754 if ( fp
->isQuoteLiteral(field
) ) {
756 UnicodeString quoteLiteral
;
757 fp
->getQuoteLiteral(quoteLiteral
, &i
);
758 mmss
+= quoteLiteral
;
762 if (fp
->isPatternSeparator(field
) && gotMm
) {
766 UChar ch
=field
.charAt(0);
777 addPattern(mmss
, FALSE
, conflictingString
, status
);
781 if (gotMm
|| ch
==LOW_Z
|| ch
==CAP_Z
|| ch
==LOW_V
|| ch
==CAP_V
) {
791 #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
794 DateTimePatternGenerator::getCalendarTypeToUse(const Locale
& locale
, CharString
& destination
, UErrorCode
& err
) {
795 destination
.clear().append(DT_DateTimeGregorianTag
, -1, err
); // initial default
796 if ( U_SUCCESS(err
) ) {
797 char localeWithCalendarKey
[ULOC_LOCALE_IDENTIFIER_CAPACITY
];
798 // obtain a locale that always has the calendar key value that should be used
799 ures_getFunctionalEquivalent(
800 localeWithCalendarKey
,
801 ULOC_LOCALE_IDENTIFIER_CAPACITY
,
809 if (U_FAILURE(err
)) { return; }
810 localeWithCalendarKey
[ULOC_LOCALE_IDENTIFIER_CAPACITY
-1] = 0; // ensure null termination
811 // now get the calendar key value from that locale
812 char calendarType
[ULOC_KEYWORDS_CAPACITY
];
813 int32_t calendarTypeLen
= uloc_getKeywordValue(
814 localeWithCalendarKey
,
817 ULOC_KEYWORDS_CAPACITY
,
819 if (U_FAILURE(err
)) { return; }
820 if (calendarTypeLen
< ULOC_KEYWORDS_CAPACITY
) {
821 destination
.clear().append(calendarType
, -1, err
);
822 if (U_FAILURE(err
)) { return; }
829 DateTimePatternGenerator::consumeShortTimePattern(const UnicodeString
& shortTimePattern
,
830 UErrorCode
& status
) {
831 if (U_FAILURE(status
)) { return; }
832 // ICU-20383 No longer set fDefaultHourFormatChar to the hour format character from
833 // this pattern; instead it is set from localeToAllowedHourFormatsMap which now
834 // includes entries for both preferred and allowed formats.
837 hackTimes(shortTimePattern
, status
);
840 struct DateTimePatternGenerator::AppendItemFormatsSink
: public ResourceSink
{
842 // Destination for data, modified via setters.
843 DateTimePatternGenerator
& dtpg
;
845 AppendItemFormatsSink(DateTimePatternGenerator
& _dtpg
) : dtpg(_dtpg
) {}
846 virtual ~AppendItemFormatsSink();
848 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
849 UErrorCode
&errorCode
) {
850 ResourceTable itemsTable
= value
.getTable(errorCode
);
851 if (U_FAILURE(errorCode
)) { return; }
852 for (int32_t i
= 0; itemsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
853 UDateTimePatternField field
= dtpg
.getAppendFormatNumber(key
);
854 if (field
== UDATPG_FIELD_COUNT
) { continue; }
855 const UnicodeString
& valueStr
= value
.getUnicodeString(errorCode
);
856 if (dtpg
.getAppendItemFormat(field
).isEmpty() && !valueStr
.isEmpty()) {
857 dtpg
.setAppendItemFormat(field
, valueStr
);
862 void fillInMissing() {
863 UnicodeString
defaultItemFormat(TRUE
, UDATPG_ItemFormat
, UPRV_LENGTHOF(UDATPG_ItemFormat
)-1); // Read-only alias.
864 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; i
++) {
865 UDateTimePatternField field
= (UDateTimePatternField
)i
;
866 if (dtpg
.getAppendItemFormat(field
).isEmpty()) {
867 dtpg
.setAppendItemFormat(field
, defaultItemFormat
);
873 struct DateTimePatternGenerator::AppendItemNamesSink
: public ResourceSink
{
875 // Destination for data, modified via setters.
876 DateTimePatternGenerator
& dtpg
;
878 AppendItemNamesSink(DateTimePatternGenerator
& _dtpg
) : dtpg(_dtpg
) {}
879 virtual ~AppendItemNamesSink();
881 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
882 UErrorCode
&errorCode
) {
883 ResourceTable itemsTable
= value
.getTable(errorCode
);
884 if (U_FAILURE(errorCode
)) { return; }
885 for (int32_t i
= 0; itemsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
886 UDateTimePGDisplayWidth width
;
887 UDateTimePatternField field
= dtpg
.getFieldAndWidthIndices(key
, &width
);
888 if (field
== UDATPG_FIELD_COUNT
) { continue; }
889 ResourceTable detailsTable
= value
.getTable(errorCode
);
890 if (U_FAILURE(errorCode
)) { return; }
891 for (int32_t j
= 0; detailsTable
.getKeyAndValue(j
, key
, value
); ++j
) {
892 if (uprv_strcmp(key
, "dn") != 0) { continue; }
893 const UnicodeString
& valueStr
= value
.getUnicodeString(errorCode
);
894 if (dtpg
.getFieldDisplayName(field
,width
).isEmpty() && !valueStr
.isEmpty()) {
895 dtpg
.setFieldDisplayName(field
,width
,valueStr
);
902 void fillInMissing() {
903 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; i
++) {
904 UnicodeString
& valueStr
= dtpg
.getMutableFieldDisplayName((UDateTimePatternField
)i
, UDATPG_WIDE
);
905 if (valueStr
.isEmpty()) {
910 valueStr
+= (UChar
)(i
+0x30);
913 valueStr
+= (UChar
)0x31;
914 valueStr
+= (UChar
)(i
-10 + 0x30);
916 // NUL-terminate for the C API.
917 valueStr
.getTerminatedBuffer();
919 for (int32_t j
= 1; j
< UDATPG_WIDTH_COUNT
; j
++) {
920 UnicodeString
& valueStr2
= dtpg
.getMutableFieldDisplayName((UDateTimePatternField
)i
, (UDateTimePGDisplayWidth
)j
);
921 if (valueStr2
.isEmpty()) {
922 valueStr2
= dtpg
.getFieldDisplayName((UDateTimePatternField
)i
, (UDateTimePGDisplayWidth
)(j
-1));
929 struct DateTimePatternGenerator::AvailableFormatsSink
: public ResourceSink
{
931 // Destination for data, modified via setters.
932 DateTimePatternGenerator
& dtpg
;
934 // Temporary variable, required for calling addPatternWithSkeleton.
935 UnicodeString conflictingPattern
;
937 AvailableFormatsSink(DateTimePatternGenerator
& _dtpg
) : dtpg(_dtpg
) {}
938 virtual ~AvailableFormatsSink();
940 virtual void put(const char *key
, ResourceValue
&value
, UBool isRoot
,
941 UErrorCode
&errorCode
) {
942 ResourceTable itemsTable
= value
.getTable(errorCode
);
943 if (U_FAILURE(errorCode
)) { return; }
944 for (int32_t i
= 0; itemsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
945 const UnicodeString
formatKey(key
, -1, US_INV
);
946 if (!dtpg
.isAvailableFormatSet(formatKey
) ) {
947 dtpg
.setAvailableFormat(formatKey
, errorCode
);
948 // Add pattern with its associated skeleton. Override any duplicate
949 // derived from std patterns, but not a previous availableFormats entry:
950 const UnicodeString
& formatValue
= value
.getUnicodeString(errorCode
);
951 conflictingPattern
.remove();
952 dtpg
.addPatternWithSkeleton(formatValue
, &formatKey
, !isRoot
, conflictingPattern
, errorCode
);
958 // Virtual destructors must be defined out of line.
959 DateTimePatternGenerator::AppendItemFormatsSink::~AppendItemFormatsSink() {}
960 DateTimePatternGenerator::AppendItemNamesSink::~AppendItemNamesSink() {}
961 DateTimePatternGenerator::AvailableFormatsSink::~AvailableFormatsSink() {}
964 DateTimePatternGenerator::addCLDRData(const Locale
& locale
, UErrorCode
& errorCode
) {
965 if (U_FAILURE(errorCode
)) { return; }
966 UnicodeString rbPattern
, value
, field
;
969 LocalUResourceBundlePointer
rb(ures_open(nullptr, locale
.getName(), &errorCode
));
970 if (U_FAILURE(errorCode
)) { return; }
972 CharString calendarTypeToUse
; // to be filled in with the type to use, if all goes well
973 getCalendarTypeToUse(locale
, calendarTypeToUse
, errorCode
);
974 if (U_FAILURE(errorCode
)) { return; }
976 // Local err to ignore resource not found exceptions
977 UErrorCode err
= U_ZERO_ERROR
;
979 // Load append item formats.
980 AppendItemFormatsSink
appendItemFormatsSink(*this);
982 .append(DT_DateTimeCalendarTag
, errorCode
)
983 .append('/', errorCode
)
984 .append(calendarTypeToUse
, errorCode
)
985 .append('/', errorCode
)
986 .append(DT_DateTimeAppendItemsTag
, errorCode
); // i.e., calendar/xxx/appendItems
987 if (U_FAILURE(errorCode
)) { return; }
988 ures_getAllItemsWithFallback(rb
.getAlias(), path
.data(), appendItemFormatsSink
, err
);
989 appendItemFormatsSink
.fillInMissing();
991 // Load CLDR item names.
993 AppendItemNamesSink
appendItemNamesSink(*this);
994 ures_getAllItemsWithFallback(rb
.getAlias(), DT_DateTimeFieldsTag
, appendItemNamesSink
, err
);
995 appendItemNamesSink
.fillInMissing();
997 // Load the available formats from CLDR.
999 initHashtable(errorCode
);
1000 if (U_FAILURE(errorCode
)) { return; }
1001 AvailableFormatsSink
availableFormatsSink(*this);
1003 .append(DT_DateTimeCalendarTag
, errorCode
)
1004 .append('/', errorCode
)
1005 .append(calendarTypeToUse
, errorCode
)
1006 .append('/', errorCode
)
1007 .append(DT_DateTimeAvailableFormatsTag
, errorCode
); // i.e., calendar/xxx/availableFormats
1008 if (U_FAILURE(errorCode
)) { return; }
1009 ures_getAllItemsWithFallback(rb
.getAlias(), path
.data(), availableFormatsSink
, err
);
1013 DateTimePatternGenerator::initHashtable(UErrorCode
& err
) {
1014 if (U_FAILURE(err
)) { return; }
1015 if (fAvailableFormatKeyHash
!=nullptr) {
1018 LocalPointer
<Hashtable
> hash(new Hashtable(FALSE
, err
), err
);
1019 if (U_SUCCESS(err
)) {
1020 fAvailableFormatKeyHash
= hash
.orphan();
1025 DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field
, const UnicodeString
& value
) {
1026 appendItemFormats
[field
] = value
;
1027 // NUL-terminate for the C API.
1028 appendItemFormats
[field
].getTerminatedBuffer();
1031 const UnicodeString
&
1032 DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field
) const {
1033 return appendItemFormats
[field
];
1037 DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field
, const UnicodeString
& value
) {
1038 setFieldDisplayName(field
, UDATPG_WIDTH_APPENDITEM
, value
);
1041 const UnicodeString
&
1042 DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field
) const {
1043 return fieldDisplayNames
[field
][UDATPG_WIDTH_APPENDITEM
];
1047 DateTimePatternGenerator::setFieldDisplayName(UDateTimePatternField field
, UDateTimePGDisplayWidth width
, const UnicodeString
& value
) {
1048 fieldDisplayNames
[field
][width
] = value
;
1049 // NUL-terminate for the C API.
1050 fieldDisplayNames
[field
][width
].getTerminatedBuffer();
1054 DateTimePatternGenerator::getFieldDisplayName(UDateTimePatternField field
, UDateTimePGDisplayWidth width
) const {
1055 return fieldDisplayNames
[field
][width
];
1059 DateTimePatternGenerator::getMutableFieldDisplayName(UDateTimePatternField field
, UDateTimePGDisplayWidth width
) {
1060 return fieldDisplayNames
[field
][width
];
1064 DateTimePatternGenerator::getAppendName(UDateTimePatternField field
, UnicodeString
& value
) {
1065 value
= SINGLE_QUOTE
;
1066 value
+= fieldDisplayNames
[field
][UDATPG_WIDTH_APPENDITEM
];
1067 value
+= SINGLE_QUOTE
;
1071 DateTimePatternGenerator::getBestPattern(const UnicodeString
& patternForm
, UErrorCode
& status
) {
1072 return getBestPattern(patternForm
, UDATPG_MATCH_NO_OPTIONS
, status
);
1076 DateTimePatternGenerator::getBestPattern(const UnicodeString
& patternForm
, UDateTimePatternMatchOptions options
, UErrorCode
& status
) {
1077 if (U_FAILURE(status
)) {
1078 return UnicodeString();
1080 if (U_FAILURE(internalErrorCode
)) {
1081 status
= internalErrorCode
;
1082 return UnicodeString();
1084 const UnicodeString
*bestPattern
= nullptr;
1085 UnicodeString dtFormat
;
1086 UnicodeString resultPattern
;
1087 int32_t flags
= kDTPGNoFlags
;
1089 int32_t dateMask
=(1<<UDATPG_DAYPERIOD_FIELD
) - 1;
1090 int32_t timeMask
=(1<<UDATPG_FIELD_COUNT
) - 1 - dateMask
;
1092 // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
1093 UnicodeString patternFormMapped
= mapSkeletonMetacharacters(patternForm
, &flags
, options
, status
);
1094 if (U_FAILURE(status
)) {
1095 return UnicodeString();
1098 resultPattern
.remove();
1099 dtMatcher
->set(patternFormMapped
, fp
);
1100 const PtnSkeleton
* specifiedSkeleton
= nullptr;
1101 bestPattern
=getBestRaw(*dtMatcher
, -1, distanceInfo
, status
, &specifiedSkeleton
);
1102 if (U_FAILURE(status
)) {
1103 return UnicodeString();
1106 if ( distanceInfo
->missingFieldMask
==0 && distanceInfo
->extraFieldMask
==0 ) {
1107 resultPattern
= adjustFieldTypes(*bestPattern
, specifiedSkeleton
, flags
, options
);
1109 return resultPattern
;
1111 int32_t neededFields
= dtMatcher
->getFieldMask();
1112 UnicodeString datePattern
=getBestAppending(neededFields
& dateMask
, flags
, status
, options
);
1113 UnicodeString timePattern
=getBestAppending(neededFields
& timeMask
, flags
, status
, options
);
1114 if (U_FAILURE(status
)) {
1115 return UnicodeString();
1117 if (datePattern
.length()==0) {
1118 if (timePattern
.length()==0) {
1119 resultPattern
.remove();
1125 if (timePattern
.length()==0) {
1128 resultPattern
.remove();
1129 status
= U_ZERO_ERROR
;
1130 dtFormat
=getDateTimeFormat();
1131 SimpleFormatter(dtFormat
, 2, 2, status
).format(timePattern
, datePattern
, resultPattern
, status
);
1132 return resultPattern
;
1136 * Map a skeleton that may have metacharacters jJC to one without, by replacing
1137 * the metacharacters with locale-appropriate fields of h/H/k/K and of a/b/B
1138 * (depends on fDefaultHourFormatChar and fAllowedHourFormats being set, which in
1139 * turn depends on initData having been run). This method also updates the flags
1140 * as necessary. Returns the updated skeleton.
1143 DateTimePatternGenerator::mapSkeletonMetacharacters(const UnicodeString
& patternForm
, int32_t* flags
, UDateTimePatternMatchOptions options
, UErrorCode
& status
) {
1144 UnicodeString patternFormMapped
;
1145 patternFormMapped
.remove();
1146 UChar hourFormatSkeletonCharForLowJ
= fDefaultHourFormatChar
;
1147 switch (options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) {
1148 case UADATPG_FORCE_12_HOUR_CYCLE
: hourFormatSkeletonCharForLowJ
= LOW_H
; break;
1149 case UADATPG_FORCE_24_HOUR_CYCLE
: hourFormatSkeletonCharForLowJ
= CAP_H
; break;
1152 UBool inQuoted
= FALSE
;
1153 int32_t patPos
, patLen
= patternForm
.length();
1154 for (patPos
= 0; patPos
< patLen
; patPos
++) {
1155 UChar patChr
= patternForm
.charAt(patPos
);
1156 if (patChr
== SINGLE_QUOTE
) {
1157 inQuoted
= !inQuoted
;
1158 } else if (!inQuoted
) {
1159 // Handle special mappings for 'j' and 'C' in which fields lengths
1160 // 1,3,5 => hour field length 1
1161 // 2,4,6 => hour field length 2
1162 // 1,2 => abbreviated dayPeriod (field length 1..3)
1163 // 3,4 => long dayPeriod (field length 4)
1164 // 5,6 => narrow dayPeriod (field length 5)
1165 if (patChr
== LOW_J
|| patChr
== CAP_C
) {
1166 int32_t extraLen
= 0; // 1 less than total field length
1167 while (patPos
+1 < patLen
&& patternForm
.charAt(patPos
+1)==patChr
) {
1171 int32_t hourLen
= 1 + (extraLen
& 1);
1172 int32_t dayPeriodLen
= (extraLen
< 2)? 1: 3 + (extraLen
>> 1);
1173 UChar hourChar
= LOW_H
;
1174 UChar dayPeriodChar
= LOW_A
;
1175 if (patChr
== LOW_J
) {
1176 hourChar
= hourFormatSkeletonCharForLowJ
;
1178 AllowedHourFormat bestAllowed
;
1179 if (fAllowedHourFormats
[0] != ALLOWED_HOUR_FORMAT_UNKNOWN
) {
1180 bestAllowed
= (AllowedHourFormat
)fAllowedHourFormats
[0];
1182 status
= U_INVALID_FORMAT_ERROR
;
1183 return UnicodeString();
1185 if (bestAllowed
== ALLOWED_HOUR_FORMAT_H
|| bestAllowed
== ALLOWED_HOUR_FORMAT_HB
|| bestAllowed
== ALLOWED_HOUR_FORMAT_Hb
) {
1187 } else if (bestAllowed
== ALLOWED_HOUR_FORMAT_K
|| bestAllowed
== ALLOWED_HOUR_FORMAT_KB
|| bestAllowed
== ALLOWED_HOUR_FORMAT_Kb
) {
1189 } else if (bestAllowed
== ALLOWED_HOUR_FORMAT_k
) {
1192 // in #13183 just add b/B to skeleton, no longer need to set special flags
1193 if (bestAllowed
== ALLOWED_HOUR_FORMAT_HB
|| bestAllowed
== ALLOWED_HOUR_FORMAT_hB
|| bestAllowed
== ALLOWED_HOUR_FORMAT_KB
) {
1194 dayPeriodChar
= CAP_B
;
1195 } else if (bestAllowed
== ALLOWED_HOUR_FORMAT_Hb
|| bestAllowed
== ALLOWED_HOUR_FORMAT_hb
|| bestAllowed
== ALLOWED_HOUR_FORMAT_Kb
) {
1196 dayPeriodChar
= LOW_B
;
1199 if (hourChar
==CAP_H
|| hourChar
==LOW_K
) {
1202 while (dayPeriodLen
-- > 0) {
1203 patternFormMapped
.append(dayPeriodChar
);
1205 while (hourLen
-- > 0) {
1206 patternFormMapped
.append(hourChar
);
1208 } else if (patChr
== CAP_J
) {
1209 // Get pattern for skeleton with H, then replace H or k
1210 // with fDefaultHourFormatChar (if different)
1211 patternFormMapped
.append(CAP_H
);
1212 *flags
|= kDTPGSkeletonUsesCapJ
;
1214 patternFormMapped
.append(patChr
);
1218 return patternFormMapped
;
1222 DateTimePatternGenerator::replaceFieldTypes(const UnicodeString
& pattern
,
1223 const UnicodeString
& skeleton
,
1224 UErrorCode
& status
) {
1225 return replaceFieldTypes(pattern
, skeleton
, UDATPG_MATCH_NO_OPTIONS
, status
);
1229 DateTimePatternGenerator::replaceFieldTypes(const UnicodeString
& pattern
,
1230 const UnicodeString
& skeleton
,
1231 UDateTimePatternMatchOptions options
,
1232 UErrorCode
& status
) {
1233 if (U_FAILURE(status
)) {
1234 return UnicodeString();
1236 if (U_FAILURE(internalErrorCode
)) {
1237 status
= internalErrorCode
;
1238 return UnicodeString();
1240 dtMatcher
->set(skeleton
, fp
);
1241 UnicodeString result
= adjustFieldTypes(pattern
, nullptr, kDTPGNoFlags
, options
);
1246 DateTimePatternGenerator::setDecimal(const UnicodeString
& newDecimal
) {
1247 this->decimal
= newDecimal
;
1248 // NUL-terminate for the C API.
1249 this->decimal
.getTerminatedBuffer();
1252 const UnicodeString
&
1253 DateTimePatternGenerator::getDecimal() const {
1258 DateTimePatternGenerator::addCanonicalItems(UErrorCode
& status
) {
1259 if (U_FAILURE(status
)) { return; }
1260 UnicodeString conflictingPattern
;
1262 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; i
++) {
1263 if (Canonical_Items
[i
] > 0) {
1264 addPattern(UnicodeString(Canonical_Items
[i
]), FALSE
, conflictingPattern
, status
);
1266 if (U_FAILURE(status
)) { return; }
1271 DateTimePatternGenerator::setDateTimeFormat(const UnicodeString
& dtFormat
) {
1272 dateTimeFormat
= dtFormat
;
1273 // NUL-terminate for the C API.
1274 dateTimeFormat
.getTerminatedBuffer();
1277 const UnicodeString
&
1278 DateTimePatternGenerator::getDateTimeFormat() const {
1279 return dateTimeFormat
;
1283 DateTimePatternGenerator::setDateTimeFromCalendar(const Locale
& locale
, UErrorCode
& status
) {
1284 if (U_FAILURE(status
)) { return; }
1286 const UChar
*resStr
;
1287 int32_t resStrLen
= 0;
1289 LocalPointer
<Calendar
> fCalendar(Calendar::createInstance(locale
, status
), status
);
1290 if (U_FAILURE(status
)) { return; }
1292 LocalUResourceBundlePointer
calData(ures_open(nullptr, locale
.getBaseName(), &status
));
1293 if (U_FAILURE(status
)) { return; }
1294 ures_getByKey(calData
.getAlias(), DT_DateTimeCalendarTag
, calData
.getAlias(), &status
);
1295 if (U_FAILURE(status
)) { return; }
1297 LocalUResourceBundlePointer dateTimePatterns
;
1298 if (fCalendar
->getType() != nullptr && *fCalendar
->getType() != '\0'
1299 && uprv_strcmp(fCalendar
->getType(), DT_DateTimeGregorianTag
) != 0) {
1300 dateTimePatterns
.adoptInstead(ures_getByKeyWithFallback(calData
.getAlias(), fCalendar
->getType(),
1302 ures_getByKeyWithFallback(dateTimePatterns
.getAlias(), DT_DateTimePatternsTag
,
1303 dateTimePatterns
.getAlias(), &status
);
1306 if (dateTimePatterns
.isNull() || status
== U_MISSING_RESOURCE_ERROR
) {
1307 status
= U_ZERO_ERROR
;
1308 dateTimePatterns
.adoptInstead(ures_getByKeyWithFallback(calData
.getAlias(), DT_DateTimeGregorianTag
,
1309 dateTimePatterns
.orphan(), &status
));
1310 ures_getByKeyWithFallback(dateTimePatterns
.getAlias(), DT_DateTimePatternsTag
,
1311 dateTimePatterns
.getAlias(), &status
);
1313 if (U_FAILURE(status
)) { return; }
1315 if (ures_getSize(dateTimePatterns
.getAlias()) <= DateFormat::kDateTime
)
1317 status
= U_INVALID_FORMAT_ERROR
;
1320 resStr
= ures_getStringByIndex(dateTimePatterns
.getAlias(), (int32_t)DateFormat::kDateTime
, &resStrLen
, &status
);
1321 setDateTimeFormat(UnicodeString(TRUE
, resStr
, resStrLen
));
1325 DateTimePatternGenerator::setDecimalSymbols(const Locale
& locale
, UErrorCode
& status
) {
1326 DecimalFormatSymbols dfs
= DecimalFormatSymbols(locale
, status
);
1327 if(U_SUCCESS(status
)) {
1328 decimal
= dfs
.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol
);
1329 // NUL-terminate for the C API.
1330 decimal
.getTerminatedBuffer();
1334 UDateTimePatternConflict
1335 DateTimePatternGenerator::addPattern(
1336 const UnicodeString
& pattern
,
1338 UnicodeString
&conflictingPattern
,
1341 if (U_FAILURE(internalErrorCode
)) {
1342 status
= internalErrorCode
;
1343 return UDATPG_NO_CONFLICT
;
1346 return addPatternWithSkeleton(pattern
, nullptr, override
, conflictingPattern
, status
);
1349 // For DateTimePatternGenerator::addPatternWithSkeleton -
1350 // If skeletonToUse is specified, then an availableFormats entry is being added. In this case:
1351 // 1. We pass that skeleton to matcher.set instead of having it derive a skeleton from the pattern.
1352 // 2. If the new entry's skeleton or basePattern does match an existing entry but that entry also had a skeleton specified
1353 // (i.e. it was also from availableFormats), then the new entry does not override it regardless of the value of the override
1354 // parameter. This prevents later availableFormats entries from a parent locale overriding earlier ones from the actual
1355 // specified locale. However, availableFormats entries *should* override entries with matching skeleton whose skeleton was
1356 // derived (i.e. entries derived from the standard date/time patters for the specified locale).
1357 // 3. When adding the pattern (patternMap->add), we set a new boolean to indicate that the added entry had a
1358 // specified skeleton (which sets a new field in the PtnElem in the PatternMap).
1359 UDateTimePatternConflict
1360 DateTimePatternGenerator::addPatternWithSkeleton(
1361 const UnicodeString
& pattern
,
1362 const UnicodeString
* skeletonToUse
,
1364 UnicodeString
& conflictingPattern
,
1367 if (U_FAILURE(internalErrorCode
)) {
1368 status
= internalErrorCode
;
1369 return UDATPG_NO_CONFLICT
;
1372 UnicodeString basePattern
;
1373 PtnSkeleton skeleton
;
1374 UDateTimePatternConflict conflictingStatus
= UDATPG_NO_CONFLICT
;
1376 DateTimeMatcher matcher
;
1377 if ( skeletonToUse
== nullptr ) {
1378 matcher
.set(pattern
, fp
, skeleton
);
1379 matcher
.getBasePattern(basePattern
);
1381 matcher
.set(*skeletonToUse
, fp
, skeleton
); // no longer trims skeleton fields to max len 3, per #7930
1382 matcher
.getBasePattern(basePattern
); // or perhaps instead: basePattern = *skeletonToUse;
1384 // We only care about base conflicts - and replacing the pattern associated with a base - if:
1385 // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous
1386 // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or
1387 // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen
1388 // if we are getting here from a subsequent call to addPattern).
1389 // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking
1390 // availableFormats items from root, which should not override any previous entry with the same base.
1391 UBool entryHadSpecifiedSkeleton
;
1392 const UnicodeString
*duplicatePattern
= patternMap
->getPatternFromBasePattern(basePattern
, entryHadSpecifiedSkeleton
);
1393 if (duplicatePattern
!= nullptr && (!entryHadSpecifiedSkeleton
|| (skeletonToUse
!= nullptr && !override
))) {
1394 conflictingStatus
= UDATPG_BASE_CONFLICT
;
1395 conflictingPattern
= *duplicatePattern
;
1397 return conflictingStatus
;
1400 // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats
1401 // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with
1402 // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for
1403 // the previously-specified conflicting item.
1404 const PtnSkeleton
* entrySpecifiedSkeleton
= nullptr;
1405 duplicatePattern
= patternMap
->getPatternFromSkeleton(skeleton
, &entrySpecifiedSkeleton
);
1406 if (duplicatePattern
!= nullptr ) {
1407 conflictingStatus
= UDATPG_CONFLICT
;
1408 conflictingPattern
= *duplicatePattern
;
1409 if (!override
|| (skeletonToUse
!= nullptr && entrySpecifiedSkeleton
!= nullptr)) {
1410 return conflictingStatus
;
1413 patternMap
->add(basePattern
, skeleton
, pattern
, skeletonToUse
!= nullptr, status
);
1414 if(U_FAILURE(status
)) {
1415 return conflictingStatus
;
1418 return UDATPG_NO_CONFLICT
;
1422 UDateTimePatternField
1423 DateTimePatternGenerator::getAppendFormatNumber(const char* field
) const {
1424 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1425 if (uprv_strcmp(CLDR_FIELD_APPEND
[i
], field
)==0) {
1426 return (UDateTimePatternField
)i
;
1429 return UDATPG_FIELD_COUNT
;
1432 UDateTimePatternField
1433 DateTimePatternGenerator::getFieldAndWidthIndices(const char* key
, UDateTimePGDisplayWidth
* widthP
) const {
1434 char cldrFieldKey
[UDATPG_FIELD_KEY_MAX
+ 1];
1435 uprv_strncpy(cldrFieldKey
, key
, UDATPG_FIELD_KEY_MAX
);
1436 cldrFieldKey
[UDATPG_FIELD_KEY_MAX
]=0; // ensure termination
1437 *widthP
= UDATPG_WIDE
;
1438 char* hyphenPtr
= uprv_strchr(cldrFieldKey
, '-');
1440 for (int32_t i
=UDATPG_WIDTH_COUNT
-1; i
>0; --i
) {
1441 if (uprv_strcmp(CLDR_FIELD_WIDTH
[i
], hyphenPtr
)==0) {
1442 *widthP
=(UDateTimePGDisplayWidth
)i
;
1446 *hyphenPtr
= 0; // now delete width portion of key
1448 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1449 if (uprv_strcmp(CLDR_FIELD_NAME
[i
],cldrFieldKey
)==0) {
1450 return (UDateTimePatternField
)i
;
1453 return UDATPG_FIELD_COUNT
;
1456 const UnicodeString
*
1457 DateTimePatternGenerator::getBestRaw(DateTimeMatcher
& source
,
1458 int32_t includeMask
,
1459 DistanceInfo
* missingFields
,
1461 const PtnSkeleton
** specifiedSkeletonPtr
) {
1462 int32_t bestDistance
= 0x7fffffff;
1463 DistanceInfo tempInfo
;
1464 const UnicodeString
*bestPattern
=nullptr;
1465 const PtnSkeleton
* specifiedSkeleton
=nullptr;
1467 PatternMapIterator
it(status
);
1468 if (U_FAILURE(status
)) { return nullptr; }
1470 for (it
.set(*patternMap
); it
.hasNext(); ) {
1471 DateTimeMatcher trial
= it
.next();
1472 if (trial
.equals(skipMatcher
)) {
1475 int32_t distance
=source
.getDistance(trial
, includeMask
, tempInfo
);
1476 if (distance
<bestDistance
) {
1477 bestDistance
=distance
;
1478 bestPattern
=patternMap
->getPatternFromSkeleton(*trial
.getSkeletonPtr(), &specifiedSkeleton
);
1479 missingFields
->setTo(tempInfo
);
1486 // If the best raw match had a specified skeleton and that skeleton was requested by the caller,
1487 // then return it too. This generally happens when the caller needs to pass that skeleton
1488 // through to adjustFieldTypes so the latter can do a better job.
1489 if (bestPattern
&& specifiedSkeletonPtr
) {
1490 *specifiedSkeletonPtr
= specifiedSkeleton
;
1496 DateTimePatternGenerator::adjustFieldTypes(const UnicodeString
& pattern
,
1497 const PtnSkeleton
* specifiedSkeleton
,
1499 UDateTimePatternMatchOptions options
) {
1500 UnicodeString newPattern
;
1502 for (int32_t i
=0; i
< fp
->itemNumber
; i
++) {
1503 UnicodeString field
= fp
->items
[i
];
1504 if ( fp
->isQuoteLiteral(field
) ) {
1506 UnicodeString quoteLiteral
;
1507 fp
->getQuoteLiteral(quoteLiteral
, &i
);
1508 newPattern
+= quoteLiteral
;
1511 if (fp
->isPatternSeparator(field
)) {
1515 int32_t canonicalIndex
= fp
->getCanonicalIndex(field
);
1516 if (canonicalIndex
< 0) {
1518 continue; // don't adjust
1520 const dtTypeElem
*row
= &dtTypes
[canonicalIndex
];
1521 int32_t typeValue
= row
->field
;
1523 // handle day periods - with #13183, no longer need special handling here, integrated with normal types
1525 if ((flags
& kDTPGFixFractionalSeconds
) != 0 && typeValue
== UDATPG_SECOND_FIELD
) {
1527 dtMatcher
->skeleton
.original
.appendFieldTo(UDATPG_FRACTIONAL_SECOND_FIELD
, field
);
1528 } else if (dtMatcher
->skeleton
.type
[typeValue
]!=0) {
1530 // - "reqField" is the field from the originally requested skeleton, with length
1532 // - "field" is the field from the found pattern.
1534 // The adjusted field should consist of characters from the originally requested
1535 // skeleton, except in the case of UDATPG_HOUR_FIELD or UDATPG_MONTH_FIELD or
1536 // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist
1537 // of characters from the found pattern.
1539 // The length of the adjusted field (adjFieldLen) should match that in the originally
1540 // requested skeleton, except that in the following cases the length of the adjusted field
1541 // should match that in the found pattern (i.e. the length of this pattern field should
1542 // not be adjusted):
1543 // 1. typeValue is UDATPG_HOUR_FIELD/MINUTE/SECOND and the corresponding bit in options is
1544 // not set (ticket #7180). Note, we may want to implement a similar change for other
1545 // numeric fields (MM, dd, etc.) so the default behavior is to get locale preference for
1546 // field length, but options bits can be used to override this.
1547 // 2. There is a specified skeleton for the found pattern and one of the following is true:
1548 // a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen.
1549 // b) The pattern field is numeric and the skeleton field is not, or vice versa.
1551 UChar reqFieldChar
= dtMatcher
->skeleton
.original
.getFieldChar(typeValue
);
1552 int32_t reqFieldLen
= dtMatcher
->skeleton
.original
.getFieldLength(typeValue
);
1553 if (reqFieldChar
== CAP_E
&& reqFieldLen
< 3)
1554 reqFieldLen
= 3; // 1-3 for E are equivalent to 3 for c,e
1555 int32_t adjFieldLen
= reqFieldLen
;
1556 if ( (typeValue
==UDATPG_HOUR_FIELD
&& (options
& UDATPG_MATCH_HOUR_FIELD_LENGTH
)==0) ||
1557 (typeValue
==UDATPG_MINUTE_FIELD
&& (options
& UDATPG_MATCH_MINUTE_FIELD_LENGTH
)==0) ||
1558 (typeValue
==UDATPG_SECOND_FIELD
&& (options
& UDATPG_MATCH_SECOND_FIELD_LENGTH
)==0) ) {
1559 adjFieldLen
= field
.length();
1560 } else if (specifiedSkeleton
) {
1561 int32_t skelFieldLen
= specifiedSkeleton
->original
.getFieldLength(typeValue
);
1562 UBool patFieldIsNumeric
= (row
->type
> 0);
1563 UBool skelFieldIsNumeric
= (specifiedSkeleton
->type
[typeValue
] > 0);
1564 if (skelFieldLen
== reqFieldLen
|| (patFieldIsNumeric
&& !skelFieldIsNumeric
) || (skelFieldIsNumeric
&& !patFieldIsNumeric
)) {
1565 // don't adjust the field length in the found pattern
1566 adjFieldLen
= field
.length();
1569 UChar c
= (typeValue
!= UDATPG_HOUR_FIELD
1570 && typeValue
!= UDATPG_MONTH_FIELD
1571 && typeValue
!= UDATPG_WEEKDAY_FIELD
1572 && (typeValue
!= UDATPG_YEAR_FIELD
|| reqFieldChar
==CAP_Y
))
1575 if (typeValue
== UDATPG_HOUR_FIELD
&& (flags
& kDTPGSkeletonUsesCapJ
) != 0) {
1576 c
= fDefaultHourFormatChar
;
1577 switch (options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) {
1578 case UADATPG_FORCE_12_HOUR_CYCLE
:
1579 if (c
== CAP_H
|| c
== LOW_K
) {
1580 // Have 24-hour cycle, change to 12-hour cycle.
1581 // Should have better way to pick 'h' or 'K'.
1585 case UADATPG_FORCE_24_HOUR_CYCLE
:
1586 if (c
== LOW_H
|| c
== CAP_K
) {
1587 // Have 12-hour cycle, change to 24-hour cycle.
1588 // Should have better way to pick 'H' or 'k'.
1597 for (int32_t j
=adjFieldLen
; j
>0; --j
) {
1608 DateTimePatternGenerator::getBestAppending(int32_t missingFields
, int32_t flags
, UErrorCode
&status
, UDateTimePatternMatchOptions options
) {
1609 if (U_FAILURE(status
)) {
1610 return UnicodeString();
1612 UnicodeString resultPattern
, tempPattern
;
1613 const UnicodeString
* tempPatternPtr
;
1614 int32_t lastMissingFieldMask
=0;
1615 if (missingFields
!=0) {
1616 resultPattern
=UnicodeString();
1617 const PtnSkeleton
* specifiedSkeleton
=nullptr;
1618 tempPatternPtr
= getBestRaw(*dtMatcher
, missingFields
, distanceInfo
, status
, &specifiedSkeleton
);
1619 if (U_FAILURE(status
)) {
1620 return UnicodeString();
1622 tempPattern
= *tempPatternPtr
;
1623 resultPattern
= adjustFieldTypes(tempPattern
, specifiedSkeleton
, flags
, options
);
1624 if ( distanceInfo
->missingFieldMask
==0 ) {
1625 return resultPattern
;
1627 while (distanceInfo
->missingFieldMask
!=0) { // precondition: EVERY single field must work!
1628 if ( lastMissingFieldMask
== distanceInfo
->missingFieldMask
) {
1629 break; // cannot find the proper missing field
1631 if (((distanceInfo
->missingFieldMask
& UDATPG_SECOND_AND_FRACTIONAL_MASK
)==UDATPG_FRACTIONAL_MASK
) &&
1632 ((missingFields
& UDATPG_SECOND_AND_FRACTIONAL_MASK
) == UDATPG_SECOND_AND_FRACTIONAL_MASK
)) {
1633 resultPattern
= adjustFieldTypes(resultPattern
, specifiedSkeleton
, flags
| kDTPGFixFractionalSeconds
, options
);
1634 distanceInfo
->missingFieldMask
&= ~UDATPG_FRACTIONAL_MASK
;
1637 int32_t startingMask
= distanceInfo
->missingFieldMask
;
1638 tempPatternPtr
= getBestRaw(*dtMatcher
, distanceInfo
->missingFieldMask
, distanceInfo
, status
, &specifiedSkeleton
);
1639 if (U_FAILURE(status
)) {
1640 return UnicodeString();
1642 tempPattern
= *tempPatternPtr
;
1643 tempPattern
= adjustFieldTypes(tempPattern
, specifiedSkeleton
, flags
, options
);
1644 int32_t foundMask
=startingMask
& ~distanceInfo
->missingFieldMask
;
1645 int32_t topField
=getTopBitNumber(foundMask
);
1647 if (appendItemFormats
[topField
].length() != 0) {
1648 UnicodeString appendName
;
1649 getAppendName((UDateTimePatternField
)topField
, appendName
);
1650 const UnicodeString
*values
[3] = {
1655 SimpleFormatter(appendItemFormats
[topField
], 2, 3, status
).
1656 formatAndReplace(values
, 3, resultPattern
, nullptr, 0, status
);
1658 lastMissingFieldMask
= distanceInfo
->missingFieldMask
;
1661 return resultPattern
;
1665 DateTimePatternGenerator::getTopBitNumber(int32_t foundMask
) const {
1666 if ( foundMask
==0 ) {
1670 while (foundMask
!=0) {
1674 if (i
-1 >UDATPG_ZONE_FIELD
) {
1675 return UDATPG_ZONE_FIELD
;
1682 DateTimePatternGenerator::setAvailableFormat(const UnicodeString
&key
, UErrorCode
& err
)
1684 fAvailableFormatKeyHash
->puti(key
, 1, err
);
1688 DateTimePatternGenerator::isAvailableFormatSet(const UnicodeString
&key
) const {
1689 return (UBool
)(fAvailableFormatKeyHash
->geti(key
) == 1);
1693 DateTimePatternGenerator::copyHashtable(Hashtable
*other
, UErrorCode
&status
) {
1694 if (other
== nullptr || U_FAILURE(status
)) {
1697 if (fAvailableFormatKeyHash
!= nullptr) {
1698 delete fAvailableFormatKeyHash
;
1699 fAvailableFormatKeyHash
= nullptr;
1701 initHashtable(status
);
1702 if(U_FAILURE(status
)){
1705 int32_t pos
= UHASH_FIRST
;
1706 const UHashElement
* elem
= nullptr;
1707 // walk through the hash table and create a deep clone
1708 while((elem
= other
->nextElement(pos
))!= nullptr){
1709 const UHashTok otherKeyTok
= elem
->key
;
1710 UnicodeString
* otherKey
= (UnicodeString
*)otherKeyTok
.pointer
;
1711 fAvailableFormatKeyHash
->puti(*otherKey
, 1, status
);
1712 if(U_FAILURE(status
)){
1719 DateTimePatternGenerator::getSkeletons(UErrorCode
& status
) const {
1720 if (U_FAILURE(status
)) {
1723 if (U_FAILURE(internalErrorCode
)) {
1724 status
= internalErrorCode
;
1727 LocalPointer
<StringEnumeration
> skeletonEnumerator(
1728 new DTSkeletonEnumeration(*patternMap
, DT_SKELETON
, status
), status
);
1730 return U_SUCCESS(status
) ? skeletonEnumerator
.orphan() : nullptr;
1733 const UnicodeString
&
1734 DateTimePatternGenerator::getPatternForSkeleton(const UnicodeString
& skeleton
) const {
1737 if (skeleton
.length() ==0) {
1740 curElem
= patternMap
->getHeader(skeleton
.charAt(0));
1741 while ( curElem
!= nullptr ) {
1742 if ( curElem
->skeleton
->getSkeleton()==skeleton
) {
1743 return curElem
->pattern
;
1745 curElem
= curElem
->next
.getAlias();
1751 DateTimePatternGenerator::getBaseSkeletons(UErrorCode
& status
) const {
1752 if (U_FAILURE(status
)) {
1755 if (U_FAILURE(internalErrorCode
)) {
1756 status
= internalErrorCode
;
1759 LocalPointer
<StringEnumeration
> baseSkeletonEnumerator(
1760 new DTSkeletonEnumeration(*patternMap
, DT_BASESKELETON
, status
), status
);
1762 return U_SUCCESS(status
) ? baseSkeletonEnumerator
.orphan() : nullptr;
1766 DateTimePatternGenerator::getRedundants(UErrorCode
& status
) {
1767 if (U_FAILURE(status
)) { return nullptr; }
1768 if (U_FAILURE(internalErrorCode
)) {
1769 status
= internalErrorCode
;
1772 LocalPointer
<StringEnumeration
> output(new DTRedundantEnumeration(), status
);
1773 if (U_FAILURE(status
)) { return nullptr; }
1774 const UnicodeString
*pattern
;
1775 PatternMapIterator
it(status
);
1776 if (U_FAILURE(status
)) { return nullptr; }
1778 for (it
.set(*patternMap
); it
.hasNext(); ) {
1779 DateTimeMatcher current
= it
.next();
1780 pattern
= patternMap
->getPatternFromSkeleton(*(it
.getSkeleton()));
1781 if ( isCanonicalItem(*pattern
) ) {
1784 if ( skipMatcher
== nullptr ) {
1785 skipMatcher
= new DateTimeMatcher(current
);
1786 if (skipMatcher
== nullptr) {
1787 status
= U_MEMORY_ALLOCATION_ERROR
;
1792 *skipMatcher
= current
;
1794 UnicodeString trial
= getBestPattern(current
.getPattern(), status
);
1795 if (U_FAILURE(status
)) { return nullptr; }
1796 if (trial
== *pattern
) {
1797 ((DTRedundantEnumeration
*)output
.getAlias())->add(*pattern
, status
);
1798 if (U_FAILURE(status
)) { return nullptr; }
1800 if (current
.equals(skipMatcher
)) {
1804 return output
.orphan();
1808 DateTimePatternGenerator::isCanonicalItem(const UnicodeString
& item
) const {
1809 if ( item
.length() != 1 ) {
1812 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
1813 if (item
.charAt(0)==Canonical_Items
[i
]) {
1821 DateTimePatternGenerator
*
1822 DateTimePatternGenerator::clone() const {
1823 return new DateTimePatternGenerator(*this);
1826 PatternMap::PatternMap() {
1827 for (int32_t i
=0; i
< MAX_PATTERN_ENTRIES
; ++i
) {
1830 isDupAllowed
= TRUE
;
1834 PatternMap::copyFrom(const PatternMap
& other
, UErrorCode
& status
) {
1835 if (U_FAILURE(status
)) {
1838 this->isDupAllowed
= other
.isDupAllowed
;
1839 for (int32_t bootIndex
= 0; bootIndex
< MAX_PATTERN_ENTRIES
; ++bootIndex
) {
1840 PtnElem
*curElem
, *otherElem
, *prevElem
=nullptr;
1841 otherElem
= other
.boot
[bootIndex
];
1842 while (otherElem
!= nullptr) {
1843 LocalPointer
<PtnElem
> newElem(new PtnElem(otherElem
->basePattern
, otherElem
->pattern
), status
);
1844 if (U_FAILURE(status
)) {
1845 return; // out of memory
1847 newElem
->skeleton
.adoptInsteadAndCheckErrorCode(new PtnSkeleton(*(otherElem
->skeleton
)), status
);
1848 if (U_FAILURE(status
)) {
1849 return; // out of memory
1851 newElem
->skeletonWasSpecified
= otherElem
->skeletonWasSpecified
;
1853 // Release ownership from the LocalPointer of the PtnElem object.
1854 // The PtnElem will now be owned by either the boot (for the first entry in the linked-list)
1855 // or owned by the previous PtnElem object in the linked-list.
1856 curElem
= newElem
.orphan();
1858 if (this->boot
[bootIndex
] == nullptr) {
1859 this->boot
[bootIndex
] = curElem
;
1861 if (prevElem
!= nullptr) {
1862 prevElem
->next
.adoptInstead(curElem
);
1868 otherElem
= otherElem
->next
.getAlias();
1875 PatternMap::getHeader(UChar baseChar
) const {
1878 if ( (baseChar
>= CAP_A
) && (baseChar
<= CAP_Z
) ) {
1879 curElem
= boot
[baseChar
-CAP_A
];
1882 if ( (baseChar
>=LOW_A
) && (baseChar
<= LOW_Z
) ) {
1883 curElem
= boot
[26+baseChar
-LOW_A
];
1892 PatternMap::~PatternMap() {
1893 for (int32_t i
=0; i
< MAX_PATTERN_ENTRIES
; ++i
) {
1894 if (boot
[i
] != nullptr ) {
1899 } // PatternMap destructor
1902 PatternMap::add(const UnicodeString
& basePattern
,
1903 const PtnSkeleton
& skeleton
,
1904 const UnicodeString
& value
,// mapped pattern value
1905 UBool skeletonWasSpecified
,
1906 UErrorCode
&status
) {
1907 UChar baseChar
= basePattern
.charAt(0);
1908 PtnElem
*curElem
, *baseElem
;
1909 status
= U_ZERO_ERROR
;
1911 // the baseChar must be A-Z or a-z
1912 if ((baseChar
>= CAP_A
) && (baseChar
<= CAP_Z
)) {
1913 baseElem
= boot
[baseChar
-CAP_A
];
1916 if ((baseChar
>=LOW_A
) && (baseChar
<= LOW_Z
)) {
1917 baseElem
= boot
[26+baseChar
-LOW_A
];
1920 status
= U_ILLEGAL_CHARACTER
;
1925 if (baseElem
== nullptr) {
1926 LocalPointer
<PtnElem
> newElem(new PtnElem(basePattern
, value
), status
);
1927 if (U_FAILURE(status
)) {
1928 return; // out of memory
1930 newElem
->skeleton
.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton
), status
);
1931 if (U_FAILURE(status
)) {
1932 return; // out of memory
1934 newElem
->skeletonWasSpecified
= skeletonWasSpecified
;
1935 if (baseChar
>= LOW_A
) {
1936 boot
[26 + (baseChar
- LOW_A
)] = newElem
.orphan(); // the boot array now owns the PtnElem.
1939 boot
[baseChar
- CAP_A
] = newElem
.orphan(); // the boot array now owns the PtnElem.
1942 if ( baseElem
!= nullptr ) {
1943 curElem
= getDuplicateElem(basePattern
, skeleton
, baseElem
);
1945 if (curElem
== nullptr) {
1946 // add new element to the list.
1948 while( curElem
-> next
!= nullptr )
1950 curElem
= curElem
->next
.getAlias();
1953 LocalPointer
<PtnElem
> newElem(new PtnElem(basePattern
, value
), status
);
1954 if (U_FAILURE(status
)) {
1955 return; // out of memory
1957 newElem
->skeleton
.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton
), status
);
1958 if (U_FAILURE(status
)) {
1959 return; // out of memory
1961 newElem
->skeletonWasSpecified
= skeletonWasSpecified
;
1962 curElem
->next
.adoptInstead(newElem
.orphan());
1963 curElem
= curElem
->next
.getAlias();
1966 // Pattern exists in the list already.
1967 if ( !isDupAllowed
) {
1970 // Overwrite the value.
1971 curElem
->pattern
= value
;
1972 // It was a bug that we were not doing the following previously,
1973 // though that bug hid other problems by making things partly work.
1974 curElem
->skeletonWasSpecified
= skeletonWasSpecified
;
1977 } // PatternMap::add
1979 // Find the pattern from the given basePattern string.
1980 const UnicodeString
*
1981 PatternMap::getPatternFromBasePattern(const UnicodeString
& basePattern
, UBool
& skeletonWasSpecified
) const { // key to search for
1984 if ((curElem
=getHeader(basePattern
.charAt(0)))==nullptr) {
1985 return nullptr; // no match
1989 if ( basePattern
.compare(curElem
->basePattern
)==0 ) {
1990 skeletonWasSpecified
= curElem
->skeletonWasSpecified
;
1991 return &(curElem
->pattern
);
1993 curElem
= curElem
->next
.getAlias();
1994 } while (curElem
!= nullptr);
1997 } // PatternMap::getFromBasePattern
2000 // Find the pattern from the given skeleton.
2001 // At least when this is called from getBestRaw & addPattern (in which case specifiedSkeletonPtr is non-NULL),
2002 // the comparison should be based on skeleton.original (which is unique and tied to the distance measurement in bestRaw)
2003 // and not skeleton.baseOriginal (which is not unique); otherwise we may pick a different skeleton than the one with the
2004 // optimum distance value in getBestRaw. When this is called from public getRedundants (specifiedSkeletonPtr is NULL),
2005 // for now it will continue to compare based on baseOriginal so as not to change the behavior unnecessarily.
2006 const UnicodeString
*
2007 PatternMap::getPatternFromSkeleton(const PtnSkeleton
& skeleton
, const PtnSkeleton
** specifiedSkeletonPtr
) const { // key to search for
2010 if (specifiedSkeletonPtr
) {
2011 *specifiedSkeletonPtr
= nullptr;
2015 UChar baseChar
= skeleton
.getFirstChar();
2016 if ((curElem
=getHeader(baseChar
))==nullptr) {
2017 return nullptr; // no match
2022 if (specifiedSkeletonPtr
!= nullptr) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original
2023 equal
= curElem
->skeleton
->original
== skeleton
.original
;
2024 } else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal
2025 equal
= curElem
->skeleton
->baseOriginal
== skeleton
.baseOriginal
;
2028 if (specifiedSkeletonPtr
&& curElem
->skeletonWasSpecified
) {
2029 *specifiedSkeletonPtr
= curElem
->skeleton
.getAlias();
2031 return &(curElem
->pattern
);
2033 curElem
= curElem
->next
.getAlias();
2034 } while (curElem
!= nullptr);
2040 PatternMap::equals(const PatternMap
& other
) const {
2041 if ( this==&other
) {
2044 for (int32_t bootIndex
= 0; bootIndex
< MAX_PATTERN_ENTRIES
; ++bootIndex
) {
2045 if (boot
[bootIndex
] == other
.boot
[bootIndex
]) {
2048 if ((boot
[bootIndex
] == nullptr) || (other
.boot
[bootIndex
] == nullptr)) {
2051 PtnElem
*otherElem
= other
.boot
[bootIndex
];
2052 PtnElem
*myElem
= boot
[bootIndex
];
2053 while ((otherElem
!= nullptr) || (myElem
!= nullptr)) {
2054 if ( myElem
== otherElem
) {
2057 if ((otherElem
== nullptr) || (myElem
== nullptr)) {
2060 if ( (myElem
->basePattern
!= otherElem
->basePattern
) ||
2061 (myElem
->pattern
!= otherElem
->pattern
) ) {
2064 if ((myElem
->skeleton
.getAlias() != otherElem
->skeleton
.getAlias()) &&
2065 !myElem
->skeleton
->equals(*(otherElem
->skeleton
))) {
2068 myElem
= myElem
->next
.getAlias();
2069 otherElem
= otherElem
->next
.getAlias();
2075 // find any key existing in the mapping table already.
2076 // return TRUE if there is an existing key, otherwise return FALSE.
2078 PatternMap::getDuplicateElem(
2079 const UnicodeString
&basePattern
,
2080 const PtnSkeleton
&skeleton
,
2081 PtnElem
*baseElem
) {
2084 if ( baseElem
== nullptr ) {
2091 if ( basePattern
.compare(curElem
->basePattern
)==0 ) {
2092 UBool isEqual
= TRUE
;
2093 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; ++i
) {
2094 if (curElem
->skeleton
->type
[i
] != skeleton
.type
[i
] ) {
2103 curElem
= curElem
->next
.getAlias();
2104 } while( curElem
!= nullptr );
2109 } // PatternMap::getDuplicateElem
2111 DateTimeMatcher::DateTimeMatcher(void) {
2114 DateTimeMatcher::~DateTimeMatcher() {}
2116 DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher
& other
) {
2117 copyFrom(other
.skeleton
);
2122 DateTimeMatcher::set(const UnicodeString
& pattern
, FormatParser
* fp
) {
2123 PtnSkeleton localSkeleton
;
2124 return set(pattern
, fp
, localSkeleton
);
2128 DateTimeMatcher::set(const UnicodeString
& pattern
, FormatParser
* fp
, PtnSkeleton
& skeletonResult
) {
2130 for (i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2131 skeletonResult
.type
[i
] = NONE
;
2133 skeletonResult
.original
.clear();
2134 skeletonResult
.baseOriginal
.clear();
2135 skeletonResult
.addedDefaultDayPeriod
= FALSE
;
2138 for (i
=0; i
< fp
->itemNumber
; i
++) {
2139 const UnicodeString
& value
= fp
->items
[i
];
2140 // don't skip 'a' anymore, dayPeriod handled specially below
2142 if ( fp
->isQuoteLiteral(value
) ) {
2143 UnicodeString quoteLiteral
;
2144 fp
->getQuoteLiteral(quoteLiteral
, &i
);
2147 int32_t canonicalIndex
= fp
->getCanonicalIndex(value
);
2148 if (canonicalIndex
< 0) {
2151 const dtTypeElem
*row
= &dtTypes
[canonicalIndex
];
2152 int32_t field
= row
->field
;
2153 skeletonResult
.original
.populate(field
, value
);
2154 UChar repeatChar
= row
->patternChar
;
2155 int32_t repeatCount
= row
->minLen
;
2156 skeletonResult
.baseOriginal
.populate(field
, repeatChar
, repeatCount
);
2157 int16_t subField
= row
->type
;
2158 if (row
->type
> 0) {
2159 U_ASSERT(value
.length() < INT16_MAX
);
2160 subField
+= static_cast<int16_t>(value
.length());
2162 skeletonResult
.type
[field
] = subField
;
2164 // #13183, handle special behavior for day period characters (a, b, B)
2165 if (!skeletonResult
.original
.isFieldEmpty(UDATPG_HOUR_FIELD
)) {
2166 if (skeletonResult
.original
.getFieldChar(UDATPG_HOUR_FIELD
)==LOW_H
|| skeletonResult
.original
.getFieldChar(UDATPG_HOUR_FIELD
)==CAP_K
) {
2167 // We have a skeleton with 12-hour-cycle format
2168 if (skeletonResult
.original
.isFieldEmpty(UDATPG_DAYPERIOD_FIELD
)) {
2169 // But we do not have a day period in the skeleton; add the default DAYPERIOD (currently "a")
2170 for (i
= 0; dtTypes
[i
].patternChar
!= 0; i
++) {
2171 if ( dtTypes
[i
].field
== UDATPG_DAYPERIOD_FIELD
) {
2172 // first entry for UDATPG_DAYPERIOD_FIELD
2173 skeletonResult
.original
.populate(UDATPG_DAYPERIOD_FIELD
, dtTypes
[i
].patternChar
, dtTypes
[i
].minLen
);
2174 skeletonResult
.baseOriginal
.populate(UDATPG_DAYPERIOD_FIELD
, dtTypes
[i
].patternChar
, dtTypes
[i
].minLen
);
2175 skeletonResult
.type
[UDATPG_DAYPERIOD_FIELD
] = dtTypes
[i
].type
;
2176 skeletonResult
.addedDefaultDayPeriod
= TRUE
;
2182 // Skeleton has 24-hour-cycle hour format and has dayPeriod, delete dayPeriod (i.e. ignore it)
2183 skeletonResult
.original
.clearField(UDATPG_DAYPERIOD_FIELD
);
2184 skeletonResult
.baseOriginal
.clearField(UDATPG_DAYPERIOD_FIELD
);
2185 skeletonResult
.type
[UDATPG_DAYPERIOD_FIELD
] = NONE
;
2188 copyFrom(skeletonResult
);
2192 DateTimeMatcher::getBasePattern(UnicodeString
&result
) {
2193 result
.remove(); // Reset the result first.
2194 skeleton
.baseOriginal
.appendTo(result
);
2198 DateTimeMatcher::getPattern() {
2199 UnicodeString result
;
2200 return skeleton
.original
.appendTo(result
);
2204 DateTimeMatcher::getDistance(const DateTimeMatcher
& other
, int32_t includeMask
, DistanceInfo
& distanceInfo
) const {
2206 distanceInfo
.clear();
2207 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2208 int32_t myType
= (includeMask
&(1<<i
))==0 ? 0 : skeleton
.type
[i
];
2209 int32_t otherType
= other
.skeleton
.type
[i
];
2210 if (myType
==otherType
) {
2213 if (myType
==0) {// and other is not
2214 result
+= EXTRA_FIELD
;
2215 distanceInfo
.addExtra(i
);
2219 result
+= MISSING_FIELD
;
2220 distanceInfo
.addMissing(i
);
2223 result
+= abs(myType
- otherType
);
2232 DateTimeMatcher::copyFrom(const PtnSkeleton
& newSkeleton
) {
2233 skeleton
.copyFrom(newSkeleton
);
2237 DateTimeMatcher::copyFrom() {
2243 DateTimeMatcher::equals(const DateTimeMatcher
* other
) const {
2244 if (other
==nullptr) { return FALSE
; }
2245 return skeleton
.original
== other
->skeleton
.original
;
2249 DateTimeMatcher::getFieldMask() const {
2252 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2253 if (skeleton
.type
[i
]!=0) {
2261 DateTimeMatcher::getSkeletonPtr() {
2265 FormatParser::FormatParser () {
2271 FormatParser::~FormatParser () {
2275 // Find the next token with the starting position and length
2276 // Note: the startPos may
2277 FormatParser::TokenStatus
2278 FormatParser::setTokens(const UnicodeString
& pattern
, int32_t startPos
, int32_t *len
) {
2279 int32_t curLoc
= startPos
;
2280 if ( curLoc
>= pattern
.length()) {
2283 // check the current char is between A-Z or a-z
2285 UChar c
=pattern
.charAt(curLoc
);
2286 if ( (c
>=CAP_A
&& c
<=CAP_Z
) || (c
>=LOW_A
&& c
<=LOW_Z
) ) {
2295 if ( pattern
.charAt(curLoc
)!= pattern
.charAt(startPos
) ) {
2296 break; // not the same token
2298 } while(curLoc
<= pattern
.length());
2299 *len
= curLoc
-startPos
;
2304 FormatParser::set(const UnicodeString
& pattern
) {
2305 int32_t startPos
= 0;
2306 TokenStatus result
= START
;
2311 result
= setTokens( pattern
, startPos
, &len
);
2312 if ( result
== ADD_TOKEN
)
2314 items
[itemNumber
++] = UnicodeString(pattern
, startPos
, len
);
2320 } while (result
==ADD_TOKEN
&& itemNumber
< MAX_DT_TOKEN
);
2324 FormatParser::getCanonicalIndex(const UnicodeString
& s
, UBool strict
) {
2325 int32_t len
= s
.length();
2329 UChar ch
= s
.charAt(0);
2331 // Verify that all are the same character.
2332 for (int32_t l
= 1; l
< len
; l
++) {
2333 if (ch
!= s
.charAt(l
)) {
2338 int32_t bestRow
= -1;
2339 while (dtTypes
[i
].patternChar
!= 0x0000) {
2340 if ( dtTypes
[i
].patternChar
!= ch
) {
2345 if (dtTypes
[i
].patternChar
!= dtTypes
[i
+1].patternChar
) {
2348 if (dtTypes
[i
+1].minLen
<= len
) {
2354 return strict
? -1 : bestRow
;
2358 FormatParser::isQuoteLiteral(const UnicodeString
& s
) {
2359 return (UBool
)(s
.charAt(0) == SINGLE_QUOTE
);
2362 // This function assumes the current itemIndex points to the quote literal.
2363 // Please call isQuoteLiteral prior to this function.
2365 FormatParser::getQuoteLiteral(UnicodeString
& quote
, int32_t *itemIndex
) {
2366 int32_t i
= *itemIndex
;
2369 if (items
[i
].charAt(0)==SINGLE_QUOTE
) {
2373 while ( i
< itemNumber
) {
2374 if ( items
[i
].charAt(0)==SINGLE_QUOTE
) {
2375 if ( (i
+1<itemNumber
) && (items
[i
+1].charAt(0)==SINGLE_QUOTE
)) {
2376 // two single quotes e.g. 'o''clock'
2377 quote
+= items
[i
++];
2378 quote
+= items
[i
++];
2395 FormatParser::isPatternSeparator(const UnicodeString
& field
) const {
2396 for (int32_t i
=0; i
<field
.length(); ++i
) {
2397 UChar c
= field
.charAt(i
);
2398 if ( (c
==SINGLE_QUOTE
) || (c
==BACKSLASH
) || (c
==SPACE
) || (c
==COLON
) ||
2399 (c
==QUOTATION_MARK
) || (c
==COMMA
) || (c
==HYPHEN
) ||(items
[i
].charAt(0)==DOT
) ) {
2409 DistanceInfo::~DistanceInfo() {}
2412 DistanceInfo::setTo(const DistanceInfo
& other
) {
2413 missingFieldMask
= other
.missingFieldMask
;
2414 extraFieldMask
= other
.extraFieldMask
;
2417 PatternMapIterator::PatternMapIterator(UErrorCode
& status
) :
2418 bootIndex(0), nodePtr(nullptr), matcher(nullptr), patternMap(nullptr)
2420 if (U_FAILURE(status
)) { return; }
2421 matcher
.adoptInsteadAndCheckErrorCode(new DateTimeMatcher(), status
);
2424 PatternMapIterator::~PatternMapIterator() {
2428 PatternMapIterator::set(PatternMap
& newPatternMap
) {
2429 this->patternMap
=&newPatternMap
;
2433 PatternMapIterator::getSkeleton() const {
2434 if ( nodePtr
== nullptr ) {
2438 return nodePtr
->skeleton
.getAlias();
2443 PatternMapIterator::hasNext() const {
2444 int32_t headIndex
= bootIndex
;
2445 PtnElem
*curPtr
= nodePtr
;
2447 if (patternMap
==nullptr) {
2450 while ( headIndex
< MAX_PATTERN_ENTRIES
) {
2451 if ( curPtr
!= nullptr ) {
2452 if ( curPtr
->next
!= nullptr ) {
2462 if ( patternMap
->boot
[headIndex
] != nullptr ) {
2475 PatternMapIterator::next() {
2476 while ( bootIndex
< MAX_PATTERN_ENTRIES
) {
2477 if ( nodePtr
!= nullptr ) {
2478 if ( nodePtr
->next
!= nullptr ) {
2479 nodePtr
= nodePtr
->next
.getAlias();
2489 if ( patternMap
->boot
[bootIndex
] != nullptr ) {
2490 nodePtr
= patternMap
->boot
[bootIndex
];
2499 if (nodePtr
!=nullptr) {
2500 matcher
->copyFrom(*nodePtr
->skeleton
);
2503 matcher
->copyFrom();
2509 SkeletonFields::SkeletonFields() {
2510 // Set initial values to zero
2514 void SkeletonFields::clear() {
2515 uprv_memset(chars
, 0, sizeof(chars
));
2516 uprv_memset(lengths
, 0, sizeof(lengths
));
2519 void SkeletonFields::copyFrom(const SkeletonFields
& other
) {
2520 uprv_memcpy(chars
, other
.chars
, sizeof(chars
));
2521 uprv_memcpy(lengths
, other
.lengths
, sizeof(lengths
));
2524 void SkeletonFields::clearField(int32_t field
) {
2529 UChar
SkeletonFields::getFieldChar(int32_t field
) const {
2530 return chars
[field
];
2533 int32_t SkeletonFields::getFieldLength(int32_t field
) const {
2534 return lengths
[field
];
2537 void SkeletonFields::populate(int32_t field
, const UnicodeString
& value
) {
2538 populate(field
, value
.charAt(0), value
.length());
2541 void SkeletonFields::populate(int32_t field
, UChar ch
, int32_t length
) {
2542 chars
[field
] = (int8_t) ch
;
2543 lengths
[field
] = (int8_t) length
;
2546 UBool
SkeletonFields::isFieldEmpty(int32_t field
) const {
2547 return lengths
[field
] == 0;
2550 UnicodeString
& SkeletonFields::appendTo(UnicodeString
& string
) const {
2551 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; ++i
) {
2552 appendFieldTo(i
, string
);
2557 UnicodeString
& SkeletonFields::appendFieldTo(int32_t field
, UnicodeString
& string
) const {
2558 UChar
ch(chars
[field
]);
2559 int32_t length
= (int32_t) lengths
[field
];
2561 for (int32_t i
=0; i
<length
; i
++) {
2567 UChar
SkeletonFields::getFirstChar() const {
2568 for (int32_t i
= 0; i
< UDATPG_FIELD_COUNT
; ++i
) {
2569 if (lengths
[i
] != 0) {
2577 PtnSkeleton::PtnSkeleton() {
2580 PtnSkeleton::PtnSkeleton(const PtnSkeleton
& other
) {
2584 void PtnSkeleton::copyFrom(const PtnSkeleton
& other
) {
2585 uprv_memcpy(type
, other
.type
, sizeof(type
));
2586 original
.copyFrom(other
.original
);
2587 baseOriginal
.copyFrom(other
.baseOriginal
);
2590 void PtnSkeleton::clear() {
2591 uprv_memset(type
, 0, sizeof(type
));
2593 baseOriginal
.clear();
2597 PtnSkeleton::equals(const PtnSkeleton
& other
) const {
2598 return (original
== other
.original
)
2599 && (baseOriginal
== other
.baseOriginal
)
2600 && (uprv_memcmp(type
, other
.type
, sizeof(type
)) == 0);
2604 PtnSkeleton::getSkeleton() const {
2605 UnicodeString result
;
2606 result
= original
.appendTo(result
);
2608 if (addedDefaultDayPeriod
&& (pos
= result
.indexOf(LOW_A
)) >= 0) {
2609 // for backward compatibility: if DateTimeMatcher.set added a single 'a' that
2610 // was not in the provided skeleton, remove it here before returning skeleton.
2611 result
.remove(pos
, 1);
2617 PtnSkeleton::getBaseSkeleton() const {
2618 UnicodeString result
;
2619 result
= baseOriginal
.appendTo(result
);
2621 if (addedDefaultDayPeriod
&& (pos
= result
.indexOf(LOW_A
)) >= 0) {
2622 // for backward compatibility: if DateTimeMatcher.set added a single 'a' that
2623 // was not in the provided skeleton, remove it here before returning skeleton.
2624 result
.remove(pos
, 1);
2630 PtnSkeleton::getFirstChar() const {
2631 return baseOriginal
.getFirstChar();
2634 PtnSkeleton::~PtnSkeleton() {
2637 PtnElem::PtnElem(const UnicodeString
&basePat
, const UnicodeString
&pat
) :
2638 basePattern(basePat
), skeleton(nullptr), pattern(pat
), next(nullptr)
2642 PtnElem::~PtnElem() {
2645 DTSkeletonEnumeration::DTSkeletonEnumeration(PatternMap
& patternMap
, dtStrEnum type
, UErrorCode
& status
) : fSkeletons(nullptr) {
2647 PtnSkeleton
*curSkeleton
;
2652 fSkeletons
.adoptInsteadAndCheckErrorCode(new UVector(status
), status
);
2653 if (U_FAILURE(status
)) {
2657 for (bootIndex
=0; bootIndex
<MAX_PATTERN_ENTRIES
; ++bootIndex
) {
2658 curElem
= patternMap
.boot
[bootIndex
];
2659 while (curElem
!=nullptr) {
2661 case DT_BASESKELETON
:
2662 s
=curElem
->basePattern
;
2668 curSkeleton
=curElem
->skeleton
.getAlias();
2669 s
=curSkeleton
->getSkeleton();
2672 if ( !isCanonicalItem(s
) ) {
2673 LocalPointer
<UnicodeString
> newElem(new UnicodeString(s
), status
);
2674 if (U_FAILURE(status
)) {
2677 fSkeletons
->addElement(newElem
.getAlias(), status
);
2678 if (U_FAILURE(status
)) {
2679 fSkeletons
.adoptInstead(nullptr);
2682 newElem
.orphan(); // fSkeletons vector now owns the UnicodeString.
2684 curElem
= curElem
->next
.getAlias();
2687 if ((bootIndex
==MAX_PATTERN_ENTRIES
) && (curElem
!=nullptr) ) {
2688 status
= U_BUFFER_OVERFLOW_ERROR
;
2692 const UnicodeString
*
2693 DTSkeletonEnumeration::snext(UErrorCode
& status
) {
2694 if (U_SUCCESS(status
) && fSkeletons
.isValid() && pos
< fSkeletons
->size()) {
2695 return (const UnicodeString
*)fSkeletons
->elementAt(pos
++);
2701 DTSkeletonEnumeration::reset(UErrorCode
& /*status*/) {
2706 DTSkeletonEnumeration::count(UErrorCode
& /*status*/) const {
2707 return (fSkeletons
.isNull()) ? 0 : fSkeletons
->size();
2711 DTSkeletonEnumeration::isCanonicalItem(const UnicodeString
& item
) {
2712 if ( item
.length() != 1 ) {
2715 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2716 if (item
.charAt(0)==Canonical_Items
[i
]) {
2723 DTSkeletonEnumeration::~DTSkeletonEnumeration() {
2725 if (fSkeletons
.isValid()) {
2726 for (int32_t i
= 0; i
< fSkeletons
->size(); ++i
) {
2727 if ((s
= (UnicodeString
*)fSkeletons
->elementAt(i
)) != nullptr) {
2734 DTRedundantEnumeration::DTRedundantEnumeration() : pos(0), fPatterns(nullptr) {
2738 DTRedundantEnumeration::add(const UnicodeString
& pattern
, UErrorCode
& status
) {
2739 if (U_FAILURE(status
)) { return; }
2740 if (fPatterns
.isNull()) {
2741 fPatterns
.adoptInsteadAndCheckErrorCode(new UVector(status
), status
);
2742 if (U_FAILURE(status
)) {
2746 LocalPointer
<UnicodeString
> newElem(new UnicodeString(pattern
), status
);
2747 if (U_FAILURE(status
)) {
2750 fPatterns
->addElement(newElem
.getAlias(), status
);
2751 if (U_FAILURE(status
)) {
2752 fPatterns
.adoptInstead(nullptr);
2755 newElem
.orphan(); // fPatterns now owns the string.
2758 const UnicodeString
*
2759 DTRedundantEnumeration::snext(UErrorCode
& status
) {
2760 if (U_SUCCESS(status
) && fPatterns
.isValid() && pos
< fPatterns
->size()) {
2761 return (const UnicodeString
*)fPatterns
->elementAt(pos
++);
2767 DTRedundantEnumeration::reset(UErrorCode
& /*status*/) {
2772 DTRedundantEnumeration::count(UErrorCode
& /*status*/) const {
2773 return (fPatterns
.isNull()) ? 0 : fPatterns
->size();
2777 DTRedundantEnumeration::isCanonicalItem(const UnicodeString
& item
) const {
2778 if ( item
.length() != 1 ) {
2781 for (int32_t i
=0; i
<UDATPG_FIELD_COUNT
; ++i
) {
2782 if (item
.charAt(0)==Canonical_Items
[i
]) {
2789 DTRedundantEnumeration::~DTRedundantEnumeration() {
2791 if (fPatterns
.isValid()) {
2792 for (int32_t i
= 0; i
< fPatterns
->size(); ++i
) {
2793 if ((s
= (UnicodeString
*)fPatterns
->elementAt(i
)) != nullptr) {
2803 #endif /* #if !UCONFIG_NO_FORMATTING */