1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
6 * Copyright (C) 2009-2015, International Business Machines
7 * Corporation and others. All Rights Reserved.
9 *******************************************************************************
10 * file name: udatpg.cpp
12 * tab size: 8 (not used)
15 * created on: 2007jul30
16 * created by: Markus W. Scherer
19 #include "unicode/utypes.h"
21 #if !UCONFIG_NO_FORMATTING
23 #include "unicode/udatpg.h"
24 #include "unicode/uenum.h"
25 #include "unicode/strenum.h"
26 #include "unicode/unistr.h"
27 #include "unicode/dtptngen.h"
28 #include "unicode/uchar.h"
30 #include "dtitv_impl.h"
34 U_CAPI UDateTimePatternGenerator
* U_EXPORT2
35 udatpg_open(const char *locale
, UErrorCode
*pErrorCode
) {
37 return (UDateTimePatternGenerator
*)DateTimePatternGenerator::createInstance(*pErrorCode
);
39 return (UDateTimePatternGenerator
*)DateTimePatternGenerator::createInstance(Locale(locale
), *pErrorCode
);
43 U_CAPI UDateTimePatternGenerator
* U_EXPORT2
44 udatpg_openEmpty(UErrorCode
*pErrorCode
) {
45 return (UDateTimePatternGenerator
*)DateTimePatternGenerator::createEmptyInstance(*pErrorCode
);
49 udatpg_close(UDateTimePatternGenerator
*dtpg
) {
50 delete (DateTimePatternGenerator
*)dtpg
;
53 U_CAPI UDateTimePatternGenerator
* U_EXPORT2
54 udatpg_clone(const UDateTimePatternGenerator
*dtpg
, UErrorCode
*pErrorCode
) {
55 if(U_FAILURE(*pErrorCode
)) {
58 return (UDateTimePatternGenerator
*)(((const DateTimePatternGenerator
*)dtpg
)->clone());
61 U_CAPI
int32_t U_EXPORT2
62 udatpg_getBestPattern(UDateTimePatternGenerator
*dtpg
,
63 const UChar
*skeleton
, int32_t length
,
64 UChar
*bestPattern
, int32_t capacity
,
65 UErrorCode
*pErrorCode
) {
66 return udatpg_getBestPatternWithOptions(dtpg
, skeleton
, length
,
67 UDATPG_MATCH_NO_OPTIONS
,
68 bestPattern
, capacity
, pErrorCode
);
71 U_CAPI
int32_t U_EXPORT2
72 udatpg_getBestPatternWithOptions(UDateTimePatternGenerator
*dtpg
,
73 const UChar
*skeleton
, int32_t length
,
74 UDateTimePatternMatchOptions options
,
75 UChar
*bestPattern
, int32_t capacity
,
76 UErrorCode
*pErrorCode
) {
77 if(U_FAILURE(*pErrorCode
)) {
80 if(skeleton
==NULL
&& length
!=0) {
81 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
84 UnicodeString
skeletonString((UBool
)(length
<0), skeleton
, length
);
85 UnicodeString result
=((DateTimePatternGenerator
*)dtpg
)->getBestPattern(skeletonString
, options
, *pErrorCode
);
86 return result
.extract(bestPattern
, capacity
, *pErrorCode
);
89 U_CAPI
int32_t U_EXPORT2
90 udatpg_getSkeleton(UDateTimePatternGenerator
* /* dtpg */,
91 const UChar
*pattern
, int32_t length
,
92 UChar
*skeleton
, int32_t capacity
,
93 UErrorCode
*pErrorCode
) {
94 if(U_FAILURE(*pErrorCode
)) {
97 if(pattern
==NULL
&& length
!=0) {
98 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
101 UnicodeString
patternString((UBool
)(length
<0), pattern
, length
);
102 UnicodeString result
=DateTimePatternGenerator::staticGetSkeleton(
103 patternString
, *pErrorCode
);
104 return result
.extract(skeleton
, capacity
, *pErrorCode
);
107 U_CAPI
int32_t U_EXPORT2
108 udatpg_getBaseSkeleton(UDateTimePatternGenerator
* /* dtpg */,
109 const UChar
*pattern
, int32_t length
,
110 UChar
*skeleton
, int32_t capacity
,
111 UErrorCode
*pErrorCode
) {
112 if(U_FAILURE(*pErrorCode
)) {
115 if(pattern
==NULL
&& length
!=0) {
116 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
119 UnicodeString
patternString((UBool
)(length
<0), pattern
, length
);
120 UnicodeString result
=DateTimePatternGenerator::staticGetBaseSkeleton(
121 patternString
, *pErrorCode
);
122 return result
.extract(skeleton
, capacity
, *pErrorCode
);
125 U_CAPI UDateTimePatternConflict U_EXPORT2
126 udatpg_addPattern(UDateTimePatternGenerator
*dtpg
,
127 const UChar
*pattern
, int32_t patternLength
,
129 UChar
*conflictingPattern
, int32_t capacity
, int32_t *pLength
,
130 UErrorCode
*pErrorCode
) {
131 if(U_FAILURE(*pErrorCode
)) {
132 return UDATPG_NO_CONFLICT
;
134 if(pattern
==NULL
&& patternLength
!=0) {
135 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
136 return UDATPG_NO_CONFLICT
;
138 UnicodeString
patternString((UBool
)(patternLength
<0), pattern
, patternLength
);
139 UnicodeString conflictingPatternString
;
140 UDateTimePatternConflict result
=((DateTimePatternGenerator
*)dtpg
)->
141 addPattern(patternString
, override
, conflictingPatternString
, *pErrorCode
);
142 int32_t length
=conflictingPatternString
.extract(conflictingPattern
, capacity
, *pErrorCode
);
149 U_CAPI
void U_EXPORT2
150 udatpg_setAppendItemFormat(UDateTimePatternGenerator
*dtpg
,
151 UDateTimePatternField field
,
152 const UChar
*value
, int32_t length
) {
153 UnicodeString
valueString((UBool
)(length
<0), value
, length
);
154 ((DateTimePatternGenerator
*)dtpg
)->setAppendItemFormat(field
, valueString
);
157 U_CAPI
const UChar
* U_EXPORT2
158 udatpg_getAppendItemFormat(const UDateTimePatternGenerator
*dtpg
,
159 UDateTimePatternField field
,
161 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getAppendItemFormat(field
);
163 *pLength
=result
.length();
165 return result
.getBuffer();
168 U_CAPI
void U_EXPORT2
169 udatpg_setAppendItemName(UDateTimePatternGenerator
*dtpg
,
170 UDateTimePatternField field
,
171 const UChar
*value
, int32_t length
) {
172 UnicodeString
valueString((UBool
)(length
<0), value
, length
);
173 ((DateTimePatternGenerator
*)dtpg
)->setAppendItemName(field
, valueString
);
176 U_CAPI
const UChar
* U_EXPORT2
177 udatpg_getAppendItemName(const UDateTimePatternGenerator
*dtpg
,
178 UDateTimePatternField field
,
180 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getAppendItemName(field
);
182 *pLength
=result
.length();
184 return result
.getBuffer();
187 U_CAPI
int32_t U_EXPORT2
188 udatpg_getFieldDisplayName(const UDateTimePatternGenerator
*dtpg
,
189 UDateTimePatternField field
,
190 UDateTimePGDisplayWidth width
,
191 UChar
*fieldName
, int32_t capacity
,
192 UErrorCode
*pErrorCode
) {
193 if (U_FAILURE(*pErrorCode
))
195 if (fieldName
== NULL
? capacity
!= 0 : capacity
< 0) {
196 *pErrorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
199 UnicodeString result
= ((const DateTimePatternGenerator
*)dtpg
)->getFieldDisplayName(field
,width
);
200 if (fieldName
== NULL
) {
201 return result
.length();
203 return result
.extract(fieldName
, capacity
, *pErrorCode
);
206 U_CAPI
void U_EXPORT2
207 udatpg_setDateTimeFormat(const UDateTimePatternGenerator
*dtpg
,
208 const UChar
*dtFormat
, int32_t length
) {
209 UnicodeString
dtFormatString((UBool
)(length
<0), dtFormat
, length
);
210 ((DateTimePatternGenerator
*)dtpg
)->setDateTimeFormat(dtFormatString
);
213 U_CAPI
const UChar
* U_EXPORT2
214 udatpg_getDateTimeFormat(const UDateTimePatternGenerator
*dtpg
,
216 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getDateTimeFormat();
218 *pLength
=result
.length();
220 return result
.getBuffer();
223 U_CAPI
void U_EXPORT2
224 udatpg_setDecimal(UDateTimePatternGenerator
*dtpg
,
225 const UChar
*decimal
, int32_t length
) {
226 UnicodeString
decimalString((UBool
)(length
<0), decimal
, length
);
227 ((DateTimePatternGenerator
*)dtpg
)->setDecimal(decimalString
);
230 U_CAPI
const UChar
* U_EXPORT2
231 udatpg_getDecimal(const UDateTimePatternGenerator
*dtpg
,
233 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getDecimal();
235 *pLength
=result
.length();
237 return result
.getBuffer();
240 U_CAPI
int32_t U_EXPORT2
241 udatpg_replaceFieldTypes(UDateTimePatternGenerator
*dtpg
,
242 const UChar
*pattern
, int32_t patternLength
,
243 const UChar
*skeleton
, int32_t skeletonLength
,
244 UChar
*dest
, int32_t destCapacity
,
245 UErrorCode
*pErrorCode
) {
246 return udatpg_replaceFieldTypesWithOptions(dtpg
, pattern
, patternLength
, skeleton
, skeletonLength
,
247 UDATPG_MATCH_NO_OPTIONS
,
248 dest
, destCapacity
, pErrorCode
);
251 U_CAPI
int32_t U_EXPORT2
252 udatpg_replaceFieldTypesWithOptions(UDateTimePatternGenerator
*dtpg
,
253 const UChar
*pattern
, int32_t patternLength
,
254 const UChar
*skeleton
, int32_t skeletonLength
,
255 UDateTimePatternMatchOptions options
,
256 UChar
*dest
, int32_t destCapacity
,
257 UErrorCode
*pErrorCode
) {
258 if(U_FAILURE(*pErrorCode
)) {
261 if((pattern
==NULL
&& patternLength
!=0) || (skeleton
==NULL
&& skeletonLength
!=0)) {
262 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
265 UnicodeString
patternString((UBool
)(patternLength
<0), pattern
, patternLength
);
266 UnicodeString
skeletonString((UBool
)(skeletonLength
<0), skeleton
, skeletonLength
);
267 UnicodeString result
=((DateTimePatternGenerator
*)dtpg
)->replaceFieldTypes(patternString
, skeletonString
, options
, *pErrorCode
);
268 return result
.extract(dest
, destCapacity
, *pErrorCode
);
271 U_CAPI UEnumeration
* U_EXPORT2
272 udatpg_openSkeletons(const UDateTimePatternGenerator
*dtpg
, UErrorCode
*pErrorCode
) {
273 return uenum_openFromStringEnumeration(
274 ((DateTimePatternGenerator
*)dtpg
)->getSkeletons(*pErrorCode
),
278 U_CAPI UEnumeration
* U_EXPORT2
279 udatpg_openBaseSkeletons(const UDateTimePatternGenerator
*dtpg
, UErrorCode
*pErrorCode
) {
280 return uenum_openFromStringEnumeration(
281 ((DateTimePatternGenerator
*)dtpg
)->getBaseSkeletons(*pErrorCode
),
285 U_CAPI
const UChar
* U_EXPORT2
286 udatpg_getPatternForSkeleton(const UDateTimePatternGenerator
*dtpg
,
287 const UChar
*skeleton
, int32_t skeletonLength
,
289 UnicodeString
skeletonString((UBool
)(skeletonLength
<0), skeleton
, skeletonLength
);
290 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getPatternForSkeleton(skeletonString
);
292 *pLength
=result
.length();
294 return result
.getBuffer();
297 // Helper function for uadatpg_remapPatternWithOptionsLoc
299 _doReplaceAndReturnAdj( UDateTimePatternGenerator
*dtpg
, uint32_t options
, UBool matchHourLen
,
300 UnicodeString
&patternString
, const UnicodeString
&skeleton
, const UnicodeString
&otherCycSkeleton
,
301 int32_t timePatStart
, int32_t timePatLimit
, int32_t timeNonHourStart
, int32_t timeNonHourLimit
,
302 UErrorCode
*pErrorCode
) {
304 options
|= UDATPG_MATCH_HOUR_FIELD_LENGTH
;
306 UnicodeString replacement
=((DateTimePatternGenerator
*)dtpg
)->getBestPattern(otherCycSkeleton
, (UDateTimePatternMatchOptions
)options
, *pErrorCode
);
307 if (U_FAILURE(*pErrorCode
)) {
310 UnicodeString stringForOrigSkeleton
=((DateTimePatternGenerator
*)dtpg
)->getBestPattern(skeleton
, UDATPG_MATCH_ALL_FIELDS_LENGTH
, *pErrorCode
); // match orig field lengths
311 if (U_SUCCESS(*pErrorCode
)) {
312 int32_t index
= patternString
.indexOf(stringForOrigSkeleton
);
314 int32_t stringForOrigSkelLen
= stringForOrigSkeleton
.length();
315 patternString
.replace(index
, stringForOrigSkelLen
, replacement
);
316 return replacement
.length() - stringForOrigSkelLen
;
319 *pErrorCode
= U_ZERO_ERROR
;
321 if (timeNonHourStart
>= 0 && timeNonHourLimit
> timeNonHourStart
) {
322 // find any minutes/seconds/milliseconds part of replacement, set that back to the
323 // minutes/seconds/milliseconds part of the original pattern.
324 // First get the minutes/seconds/milliseconds part of the original pattern.
325 UnicodeString nonHour
;
326 patternString
.extractBetween(timeNonHourStart
, timeNonHourLimit
, nonHour
);
327 // Now scan to find position from just after hours to end of minutes/seconds/milliseconds.
328 timeNonHourStart
= -1;
329 timeNonHourLimit
= 0;
330 UBool inQuoted
= FALSE
;
331 int32_t repPos
, repLen
= replacement
.length();
332 for (repPos
= 0; repPos
< repLen
; repPos
++) {
333 UChar repChr
= replacement
.charAt(repPos
);
334 if (repChr
== 0x27 /* ASCII-range single quote */) {
335 inQuoted
= !inQuoted
;
336 } else if (!inQuoted
) {
337 if (repChr
==LOW_H
|| repChr
==CAP_H
|| repChr
==CAP_K
|| repChr
==LOW_K
) { // hHKk, hour
338 timeNonHourStart
= repPos
+ 1;
339 } else if (timeNonHourStart
< 0 && (repChr
==LOW_M
|| repChr
==LOW_S
)) { // 'm' or 's' and we did not have hour
340 timeNonHourStart
= repPos
;
342 if (!u_isWhitespace(repChr
) && timeNonHourStart
>= 0 && repChr
!=LOW_A
) { // NonHour portion should not include 'a'
343 timeNonHourLimit
= repPos
+ 1;
347 // If we found minutes/seconds/milliseconds in replacement, restore that part to original.
348 if (timeNonHourStart
>= 0 && timeNonHourLimit
> timeNonHourStart
) {
349 replacement
.replaceBetween(timeNonHourStart
, timeNonHourLimit
, nonHour
);
352 patternString
.replaceBetween(timePatStart
, timePatLimit
, replacement
);
353 return replacement
.length() - (timePatLimit
- timePatStart
); // positive if replacement is longer
357 * uadatpg_remapPatternWithOptionsLoc
359 * Thee general idea is:
360 * 1. Scan the pattern for one or more time subpatterns
361 * 2. For each time subpattern, if the hour pattern characters don't match the
362 * time cycle that we want to force to, then:
363 * a) Save the nonHour portion of the subpattern (from just after hours to end
364 * of minutes/seconds/ milliseconds)
365 * b) Turn the pattern characters in that subpattern into a skeleton, but with
366 * the hour pattern characters switched to the desired time cycle
367 * c) Use that skeleton to get the locale's corresponding replacement pattern
368 * for the desired time cycle (with all desired elements - min, sec, etc.)
369 * d) In that replacement pattern, find the new nonHour portion, and restore
370 * that to the original nonHour portion
371 * e) Finally, replace the original time subpattern with the adjusted
374 U_CAPI
int32_t U_EXPORT2
375 uadatpg_remapPatternWithOptions(UDateTimePatternGenerator
*dtpg
,
376 const UChar
*pattern
, int32_t patternLength
,
377 UDateTimePatternMatchOptions options
,
378 UChar
*newPattern
, int32_t newPatternCapacity
,
379 UErrorCode
*pErrorCode
) {
380 if (U_FAILURE(*pErrorCode
)) {
383 if ( pattern
==NULL
|| ((newPattern
==NULL
)? newPatternCapacity
!=0: newPatternCapacity
<0) ) {
384 *pErrorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
387 UnicodeString
patternString((patternLength
< 0), pattern
, patternLength
);
388 UBool force12
= ((options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) == UADATPG_FORCE_12_HOUR_CYCLE
);
389 UBool force24
= ((options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) == UADATPG_FORCE_24_HOUR_CYCLE
);
390 if (force12
|| force24
) {
391 UBool inQuoted
= FALSE
;
392 UBool inTimePat
= FALSE
;
393 UBool needReplacement
= FALSE
;
394 int32_t timePatStart
= 0;
395 int32_t timePatLimit
= 0;
396 int32_t timeNonHourStart
= -1;
397 int32_t timeNonHourLimit
= 0;
398 UnicodeString skeleton
, otherCycSkeleton
;
399 UnicodeString
timePatChars("abBhHKkmsSzZOvVXx", -1, US_INV
); // all pattern chars for times
400 int32_t numForcedH
= 0;
401 int32_t patPos
, patLen
= patternString
.length();
403 for (patPos
= 0; patPos
< patLen
; patPos
++) {
404 UChar patChr
= patternString
.charAt(patPos
);
405 UChar otherCycPatChr
= patChr
;
406 if (patChr
== 0x27 /* ASCII-range single quote */) {
407 inQuoted
= !inQuoted
;
408 } else if (!inQuoted
) {
409 if (timePatChars
.indexOf(patChr
) >= 0) {
413 timePatStart
= patPos
;
414 timeNonHourStart
= -1;
416 otherCycSkeleton
.remove();
419 if (patChr
==LOW_H
|| patChr
==CAP_K
) { // hK, hour, 12-hour cycle
421 otherCycPatChr
= CAP_H
; // force to H
422 needReplacement
= TRUE
;
423 timeNonHourStart
= patPos
+ 1;
424 // If we are switching from a 12-hour cycle to a 24-hour cycle
425 // and the pattern for 12-hour cycle was zero-padded to 2 digits,
426 // make sure the new pattern for 24-hour cycle is also padded to
427 // 2 digits regardless of locale default, to match the
428 // expectations of the pattern provider. However we don't need
429 // to do this going the other direction (from 24- to 12-hour
430 // cycles, don't require that the 12-hour cycle has zero padding
431 // just because the 24-hour cycle did; the 12-hour cycle will
432 // add other elements such as a, so there wil be a length change
436 } else if (patChr
==CAP_H
|| patChr
==LOW_K
) { // Hk, hour, 24-hour cycle
438 otherCycPatChr
= LOW_H
; // force to h
439 needReplacement
= TRUE
;
440 timeNonHourStart
= patPos
+ 1;
442 } else if (timeNonHourStart
< 0 && (patChr
==LOW_M
|| patChr
==LOW_S
)) { // 'm' or 's' and we did not have hour
443 timeNonHourStart
= patPos
;
445 skeleton
.append(patChr
);
446 otherCycSkeleton
.append(otherCycPatChr
);
447 } else if ((patChr
>= 0x41 && patChr
<= 0x5A) || (patChr
>= 0x61 && patChr
<= 0x7A)) {
448 // a non-time pattern character, forces end of any time pattern
451 if (needReplacement
) {
452 needReplacement
= FALSE
;
454 int32_t posAdjust
= _doReplaceAndReturnAdj(dtpg
, options
, numForcedH
>= 2, patternString
, skeleton
, otherCycSkeleton
,
455 timePatStart
, timePatLimit
, timeNonHourStart
, timeNonHourLimit
, pErrorCode
);
461 if (inTimePat
&& !u_isWhitespace(patChr
)) {
462 timePatLimit
= patPos
+ 1;
463 if (timeNonHourStart
>= 0 && patChr
!=LOW_A
&& patChr
!=LOW_B
&& patChr
!=CAP_B
) { // NonHour portion should not include 'a','b','B'
464 timeNonHourLimit
= timePatLimit
;
470 if (needReplacement
) {
472 _doReplaceAndReturnAdj(dtpg
, options
, numForcedH
>= 2, patternString
, skeleton
, otherCycSkeleton
,
473 timePatStart
, timePatLimit
, timeNonHourStart
, timeNonHourLimit
, pErrorCode
);
476 return patternString
.extract(newPattern
, newPatternCapacity
, *pErrorCode
);