2 *******************************************************************************
4 * Copyright (C) 2009-2012,2014 International Business Machines
5 * Corporation and others. All Rights Reserved.
7 *******************************************************************************
8 * file name: udatpg.cpp
10 * tab size: 8 (not used)
13 * created on: 2007jul30
14 * created by: Markus W. Scherer
17 #include "unicode/utypes.h"
19 #if !UCONFIG_NO_FORMATTING
21 #include "unicode/udatpg.h"
22 #include "unicode/uenum.h"
23 #include "unicode/strenum.h"
24 #include "unicode/unistr.h"
25 #include "unicode/dtptngen.h"
26 #include "unicode/uchar.h"
28 #include "dtitv_impl.h"
32 U_CAPI UDateTimePatternGenerator
* U_EXPORT2
33 udatpg_open(const char *locale
, UErrorCode
*pErrorCode
) {
35 return (UDateTimePatternGenerator
*)DateTimePatternGenerator::createInstance(*pErrorCode
);
37 return (UDateTimePatternGenerator
*)DateTimePatternGenerator::createInstance(Locale(locale
), *pErrorCode
);
41 U_CAPI UDateTimePatternGenerator
* U_EXPORT2
42 udatpg_openEmpty(UErrorCode
*pErrorCode
) {
43 return (UDateTimePatternGenerator
*)DateTimePatternGenerator::createEmptyInstance(*pErrorCode
);
47 udatpg_close(UDateTimePatternGenerator
*dtpg
) {
48 delete (DateTimePatternGenerator
*)dtpg
;
51 U_CAPI UDateTimePatternGenerator
* U_EXPORT2
52 udatpg_clone(const UDateTimePatternGenerator
*dtpg
, UErrorCode
*pErrorCode
) {
53 if(U_FAILURE(*pErrorCode
)) {
56 return (UDateTimePatternGenerator
*)(((const DateTimePatternGenerator
*)dtpg
)->clone());
59 U_CAPI
int32_t U_EXPORT2
60 udatpg_getBestPattern(UDateTimePatternGenerator
*dtpg
,
61 const UChar
*skeleton
, int32_t length
,
62 UChar
*bestPattern
, int32_t capacity
,
63 UErrorCode
*pErrorCode
) {
64 return udatpg_getBestPatternWithOptions(dtpg
, skeleton
, length
,
65 UDATPG_MATCH_NO_OPTIONS
,
66 bestPattern
, capacity
, pErrorCode
);
69 U_CAPI
int32_t U_EXPORT2
70 udatpg_getBestPatternWithOptions(UDateTimePatternGenerator
*dtpg
,
71 const UChar
*skeleton
, int32_t length
,
72 UDateTimePatternMatchOptions options
,
73 UChar
*bestPattern
, int32_t capacity
,
74 UErrorCode
*pErrorCode
) {
75 if(U_FAILURE(*pErrorCode
)) {
78 if(skeleton
==NULL
&& length
!=0) {
79 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
82 UnicodeString
skeletonString((UBool
)(length
<0), skeleton
, length
);
83 UnicodeString result
=((DateTimePatternGenerator
*)dtpg
)->getBestPattern(skeletonString
, options
, *pErrorCode
);
84 return result
.extract(bestPattern
, capacity
, *pErrorCode
);
87 U_CAPI
int32_t U_EXPORT2
88 udatpg_getSkeleton(UDateTimePatternGenerator
*dtpg
,
89 const UChar
*pattern
, int32_t length
,
90 UChar
*skeleton
, int32_t capacity
,
91 UErrorCode
*pErrorCode
) {
92 if(U_FAILURE(*pErrorCode
)) {
95 if(pattern
==NULL
&& length
!=0) {
96 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
99 UnicodeString
patternString((UBool
)(length
<0), pattern
, length
);
100 UnicodeString result
=((DateTimePatternGenerator
*)dtpg
)->getSkeleton(patternString
, *pErrorCode
);
101 return result
.extract(skeleton
, capacity
, *pErrorCode
);
104 U_CAPI
int32_t U_EXPORT2
105 udatpg_getBaseSkeleton(UDateTimePatternGenerator
*dtpg
,
106 const UChar
*pattern
, int32_t length
,
107 UChar
*skeleton
, int32_t capacity
,
108 UErrorCode
*pErrorCode
) {
109 if(U_FAILURE(*pErrorCode
)) {
112 if(pattern
==NULL
&& length
!=0) {
113 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
116 UnicodeString
patternString((UBool
)(length
<0), pattern
, length
);
117 UnicodeString result
=((DateTimePatternGenerator
*)dtpg
)->getBaseSkeleton(patternString
, *pErrorCode
);
118 return result
.extract(skeleton
, capacity
, *pErrorCode
);
121 U_CAPI UDateTimePatternConflict U_EXPORT2
122 udatpg_addPattern(UDateTimePatternGenerator
*dtpg
,
123 const UChar
*pattern
, int32_t patternLength
,
125 UChar
*conflictingPattern
, int32_t capacity
, int32_t *pLength
,
126 UErrorCode
*pErrorCode
) {
127 if(U_FAILURE(*pErrorCode
)) {
128 return UDATPG_NO_CONFLICT
;
130 if(pattern
==NULL
&& patternLength
!=0) {
131 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
132 return UDATPG_NO_CONFLICT
;
134 UnicodeString
patternString((UBool
)(patternLength
<0), pattern
, patternLength
);
135 UnicodeString conflictingPatternString
;
136 UDateTimePatternConflict result
=((DateTimePatternGenerator
*)dtpg
)->
137 addPattern(patternString
, override
, conflictingPatternString
, *pErrorCode
);
138 int32_t length
=conflictingPatternString
.extract(conflictingPattern
, capacity
, *pErrorCode
);
145 U_CAPI
void U_EXPORT2
146 udatpg_setAppendItemFormat(UDateTimePatternGenerator
*dtpg
,
147 UDateTimePatternField field
,
148 const UChar
*value
, int32_t length
) {
149 UnicodeString
valueString((UBool
)(length
<0), value
, length
);
150 ((DateTimePatternGenerator
*)dtpg
)->setAppendItemFormat(field
, valueString
);
153 U_CAPI
const UChar
* U_EXPORT2
154 udatpg_getAppendItemFormat(const UDateTimePatternGenerator
*dtpg
,
155 UDateTimePatternField field
,
157 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getAppendItemFormat(field
);
159 *pLength
=result
.length();
161 return result
.getBuffer();
164 U_CAPI
void U_EXPORT2
165 udatpg_setAppendItemName(UDateTimePatternGenerator
*dtpg
,
166 UDateTimePatternField field
,
167 const UChar
*value
, int32_t length
) {
168 UnicodeString
valueString((UBool
)(length
<0), value
, length
);
169 ((DateTimePatternGenerator
*)dtpg
)->setAppendItemName(field
, valueString
);
172 U_CAPI
const UChar
* U_EXPORT2
173 udatpg_getAppendItemName(const UDateTimePatternGenerator
*dtpg
,
174 UDateTimePatternField field
,
176 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getAppendItemName(field
);
178 *pLength
=result
.length();
180 return result
.getBuffer();
183 U_CAPI
void U_EXPORT2
184 udatpg_setDateTimeFormat(const UDateTimePatternGenerator
*dtpg
,
185 const UChar
*dtFormat
, int32_t length
) {
186 UnicodeString
dtFormatString((UBool
)(length
<0), dtFormat
, length
);
187 ((DateTimePatternGenerator
*)dtpg
)->setDateTimeFormat(dtFormatString
);
190 U_CAPI
const UChar
* U_EXPORT2
191 udatpg_getDateTimeFormat(const UDateTimePatternGenerator
*dtpg
,
193 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getDateTimeFormat();
195 *pLength
=result
.length();
197 return result
.getBuffer();
200 U_CAPI
void U_EXPORT2
201 udatpg_setDecimal(UDateTimePatternGenerator
*dtpg
,
202 const UChar
*decimal
, int32_t length
) {
203 UnicodeString
decimalString((UBool
)(length
<0), decimal
, length
);
204 ((DateTimePatternGenerator
*)dtpg
)->setDecimal(decimalString
);
207 U_CAPI
const UChar
* U_EXPORT2
208 udatpg_getDecimal(const UDateTimePatternGenerator
*dtpg
,
210 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getDecimal();
212 *pLength
=result
.length();
214 return result
.getBuffer();
217 U_CAPI
int32_t U_EXPORT2
218 udatpg_replaceFieldTypes(UDateTimePatternGenerator
*dtpg
,
219 const UChar
*pattern
, int32_t patternLength
,
220 const UChar
*skeleton
, int32_t skeletonLength
,
221 UChar
*dest
, int32_t destCapacity
,
222 UErrorCode
*pErrorCode
) {
223 return udatpg_replaceFieldTypesWithOptions(dtpg
, pattern
, patternLength
, skeleton
, skeletonLength
,
224 UDATPG_MATCH_NO_OPTIONS
,
225 dest
, destCapacity
, pErrorCode
);
228 U_CAPI
int32_t U_EXPORT2
229 udatpg_replaceFieldTypesWithOptions(UDateTimePatternGenerator
*dtpg
,
230 const UChar
*pattern
, int32_t patternLength
,
231 const UChar
*skeleton
, int32_t skeletonLength
,
232 UDateTimePatternMatchOptions options
,
233 UChar
*dest
, int32_t destCapacity
,
234 UErrorCode
*pErrorCode
) {
235 if(U_FAILURE(*pErrorCode
)) {
238 if((pattern
==NULL
&& patternLength
!=0) || (skeleton
==NULL
&& skeletonLength
!=0)) {
239 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
242 UnicodeString
patternString((UBool
)(patternLength
<0), pattern
, patternLength
);
243 UnicodeString
skeletonString((UBool
)(skeletonLength
<0), skeleton
, skeletonLength
);
244 UnicodeString result
=((DateTimePatternGenerator
*)dtpg
)->replaceFieldTypes(patternString
, skeletonString
, options
, *pErrorCode
);
245 return result
.extract(dest
, destCapacity
, *pErrorCode
);
248 U_CAPI UEnumeration
* U_EXPORT2
249 udatpg_openSkeletons(const UDateTimePatternGenerator
*dtpg
, UErrorCode
*pErrorCode
) {
250 return uenum_openFromStringEnumeration(
251 ((DateTimePatternGenerator
*)dtpg
)->getSkeletons(*pErrorCode
),
255 U_CAPI UEnumeration
* U_EXPORT2
256 udatpg_openBaseSkeletons(const UDateTimePatternGenerator
*dtpg
, UErrorCode
*pErrorCode
) {
257 return uenum_openFromStringEnumeration(
258 ((DateTimePatternGenerator
*)dtpg
)->getBaseSkeletons(*pErrorCode
),
262 U_CAPI
const UChar
* U_EXPORT2
263 udatpg_getPatternForSkeleton(const UDateTimePatternGenerator
*dtpg
,
264 const UChar
*skeleton
, int32_t skeletonLength
,
266 UnicodeString
skeletonString((UBool
)(skeletonLength
<0), skeleton
, skeletonLength
);
267 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getPatternForSkeleton(skeletonString
);
269 *pLength
=result
.length();
271 return result
.getBuffer();
274 // Helper function for uadatpg_remapPatternWithOptionsLoc
276 _doReplaceAndReturnAdj( UDateTimePatternGenerator
*dtpg
, uint32_t options
, UBool matchHourLen
,
277 UnicodeString
&patternString
, const UnicodeString
&skeleton
, const UnicodeString
&otherCycSkeleton
,
278 int32_t timePatStart
, int32_t timePatLimit
, int32_t timeNonHourStart
, int32_t timeNonHourLimit
,
279 UErrorCode
*pErrorCode
) {
281 options
|= UDATPG_MATCH_HOUR_FIELD_LENGTH
;
283 UnicodeString replacement
=((DateTimePatternGenerator
*)dtpg
)->getBestPattern(otherCycSkeleton
, (UDateTimePatternMatchOptions
)options
, *pErrorCode
);
284 if (U_FAILURE(*pErrorCode
)) {
287 UnicodeString stringForOrigSkeleton
=((DateTimePatternGenerator
*)dtpg
)->getBestPattern(skeleton
, (UDateTimePatternMatchOptions
)options
, *pErrorCode
);
288 if (U_SUCCESS(*pErrorCode
)) {
289 int32_t index
= patternString
.indexOf(stringForOrigSkeleton
);
291 int32_t stringForOrigSkelLen
= stringForOrigSkeleton
.length();
292 patternString
.replace(index
, stringForOrigSkelLen
, replacement
);
293 return replacement
.length() - stringForOrigSkelLen
;
296 *pErrorCode
= U_ZERO_ERROR
;
298 if (timeNonHourStart
>= 0 && timeNonHourLimit
> timeNonHourStart
) {
299 // find any minutes/seconds/milliseconds part of replacement, set that back to the
300 // minutes/seconds/milliseconds part of the original pattern.
301 // First get the minutes/seconds/milliseconds part of the original pattern.
302 UnicodeString nonHour
;
303 patternString
.extractBetween(timeNonHourStart
, timeNonHourLimit
, nonHour
);
304 // Now scan to find position from just after hours to end of minutes/seconds/milliseconds.
305 timeNonHourStart
= -1;
306 timeNonHourLimit
= 0;
307 UBool inQuoted
= FALSE
;
308 int32_t repPos
, repLen
= replacement
.length();
309 for (repPos
= 0; repPos
< repLen
; repPos
++) {
310 UChar repChr
= replacement
.charAt(repPos
);
311 if (repChr
== 0x27 /* ASCII-range single quote */) {
312 inQuoted
= !inQuoted
;
313 } else if (!inQuoted
) {
314 if (repChr
==LOW_H
|| repChr
==CAP_H
|| repChr
==CAP_K
|| repChr
==LOW_K
) { // hHKk, hour
315 timeNonHourStart
= repPos
+ 1;
316 } else if (timeNonHourStart
< 0 && (repChr
==LOW_M
|| repChr
==LOW_S
)) { // 'm' or 's' and we did not have hour
317 timeNonHourStart
= repPos
;
319 if (!u_isWhitespace(repChr
) && timeNonHourStart
>= 0 && repChr
!=LOW_A
) { // NonHour portion should not include 'a'
320 timeNonHourLimit
= repPos
+ 1;
324 // If we found minutes/seconds/milliseconds in replacement, restore that part to original.
325 if (timeNonHourStart
>= 0 && timeNonHourLimit
> timeNonHourStart
) {
326 replacement
.replaceBetween(timeNonHourStart
, timeNonHourLimit
, nonHour
);
329 patternString
.replaceBetween(timePatStart
, timePatLimit
, replacement
);
330 return replacement
.length() - (timePatLimit
- timePatStart
); // positive if replacement is longer
334 * uadatpg_remapPatternWithOptionsLoc
336 * Thee general idea is:
337 * 1. Scan the pattern for one or more time subpatterns
338 * 2. For each time subpattern, if the hour pattern characters don't match the
339 * time cycle that we want to force to, then:
340 * a) Save the nonHour portion of the subpattern (from just after hours to end
341 * of minutes/seconds/ milliseconds)
342 * b) Turn the pattern characters in that subpattern into a skeleton, but with
343 * the hour pattern characters switched to the desired time cycle
344 * c) Use that skeleton to get the locale's corresponding replacement pattern
345 * for the desired time cycle (with all desired elements - min, sec, etc.)
346 * d) In that replacement pattern, find the new nonHour portion, and restore
347 * that to the original nonHour portion
348 * e) Finally, replace the original time subpattern with the adjusted
351 U_CAPI
int32_t U_EXPORT2
352 uadatpg_remapPatternWithOptions(UDateTimePatternGenerator
*dtpg
,
353 const UChar
*pattern
, int32_t patternLength
,
354 UDateTimePatternMatchOptions options
,
355 UChar
*newPattern
, int32_t newPatternCapacity
,
356 UErrorCode
*pErrorCode
) {
357 if (U_FAILURE(*pErrorCode
)) {
360 if ( pattern
==NULL
|| ((newPattern
==NULL
)? newPatternCapacity
!=0: newPatternCapacity
<0) ) {
361 *pErrorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
364 UnicodeString
patternString((patternLength
< 0), pattern
, patternLength
);
365 UBool force12
= ((options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) == UADATPG_FORCE_12_HOUR_CYCLE
);
366 UBool force24
= ((options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) == UADATPG_FORCE_24_HOUR_CYCLE
);
367 if (force12
|| force24
) {
368 UBool inQuoted
= FALSE
;
369 UBool inTimePat
= FALSE
;
370 UBool needReplacement
= FALSE
;
371 int32_t timePatStart
= 0;
372 int32_t timePatLimit
= 0;
373 int32_t timeNonHourStart
= -1;
374 int32_t timeNonHourLimit
= 0;
375 UnicodeString skeleton
, otherCycSkeleton
;
376 UnicodeString
timePatChars(":ahHKkmsSzZOvVXx", -1, US_INV
); // all pattern chars for times
377 int32_t numForcedH
= 0;
378 int32_t patPos
, patLen
= patternString
.length();
380 for (patPos
= 0; patPos
< patLen
; patPos
++) {
381 UChar patChr
= patternString
.charAt(patPos
);
382 UChar otherCycPatChr
= patChr
;
383 if (patChr
== 0x27 /* ASCII-range single quote */) {
384 inQuoted
= !inQuoted
;
385 } else if (!inQuoted
) {
386 if (timePatChars
.indexOf(patChr
) >= 0) {
390 timePatStart
= patPos
;
391 timeNonHourStart
= -1;
393 otherCycSkeleton
.remove();
396 if (patChr
==LOW_H
|| patChr
==CAP_K
) { // hK, hour, 12-hour cycle
398 otherCycPatChr
= CAP_H
; // force to H
399 needReplacement
= TRUE
;
400 timeNonHourStart
= patPos
+ 1;
401 // If we are switching from a 12-hour cycle to a 24-hour cycle
402 // and the pattern for 12-hour cycle was zero-padded to 2 digits,
403 // make sure the new pattern for 24-hour cycle is also padded to
404 // 2 digits regardless of locale default, to match the
405 // expectations of the pattern provider. However we don't need
406 // to do this going the other direction (from 24- to 12-hour
407 // cycles, don't require that the 12-hour cycle has zero padding
408 // just because the 24-hour cycle did; the 12-hour cycle will
409 // add other elements such as a, so there wil be a length change
413 } else if (patChr
==CAP_H
|| patChr
==LOW_K
) { // Hk, hour, 24-hour cycle
415 otherCycPatChr
= LOW_H
; // force to h
416 needReplacement
= TRUE
;
417 timeNonHourStart
= patPos
+ 1;
419 } else if (timeNonHourStart
< 0 && (patChr
==LOW_M
|| patChr
==LOW_S
)) { // 'm' or 's' and we did not have hour
420 timeNonHourStart
= patPos
;
422 skeleton
.append(patChr
);
423 otherCycSkeleton
.append(otherCycPatChr
);
424 } else if ((patChr
>= 0x41 && patChr
<= 0x5A) || (patChr
>= 0x61 && patChr
<= 0x7A)) {
425 // a non-time pattern character, forces end of any time pattern
428 if (needReplacement
) {
429 needReplacement
= FALSE
;
431 int32_t posAdjust
= _doReplaceAndReturnAdj(dtpg
, options
, numForcedH
>= 2, patternString
, skeleton
, otherCycSkeleton
,
432 timePatStart
, timePatLimit
, timeNonHourStart
, timeNonHourLimit
, pErrorCode
);
438 if (inTimePat
&& !u_isWhitespace(patChr
)) {
439 timePatLimit
= patPos
+ 1;
440 if (timeNonHourStart
>= 0 && patChr
!=LOW_A
) { // NonHour portion should not include 'a'
441 timeNonHourLimit
= timePatLimit
;
447 if (needReplacement
) {
449 _doReplaceAndReturnAdj(dtpg
, options
, numForcedH
>= 2, patternString
, skeleton
, otherCycSkeleton
,
450 timePatStart
, timePatLimit
, timeNonHourStart
, timeNonHourLimit
, pErrorCode
);
453 return patternString
.extract(newPattern
, newPatternCapacity
, *pErrorCode
);