2 *******************************************************************************
4 * Copyright (C) 2009-2015, 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::staticGetSkeleton(
101 patternString
, *pErrorCode
);
102 return result
.extract(skeleton
, capacity
, *pErrorCode
);
105 U_CAPI
int32_t U_EXPORT2
106 udatpg_getBaseSkeleton(UDateTimePatternGenerator
* /* dtpg */,
107 const UChar
*pattern
, int32_t length
,
108 UChar
*skeleton
, int32_t capacity
,
109 UErrorCode
*pErrorCode
) {
110 if(U_FAILURE(*pErrorCode
)) {
113 if(pattern
==NULL
&& length
!=0) {
114 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
117 UnicodeString
patternString((UBool
)(length
<0), pattern
, length
);
118 UnicodeString result
=DateTimePatternGenerator::staticGetBaseSkeleton(
119 patternString
, *pErrorCode
);
120 return result
.extract(skeleton
, capacity
, *pErrorCode
);
123 U_CAPI UDateTimePatternConflict U_EXPORT2
124 udatpg_addPattern(UDateTimePatternGenerator
*dtpg
,
125 const UChar
*pattern
, int32_t patternLength
,
127 UChar
*conflictingPattern
, int32_t capacity
, int32_t *pLength
,
128 UErrorCode
*pErrorCode
) {
129 if(U_FAILURE(*pErrorCode
)) {
130 return UDATPG_NO_CONFLICT
;
132 if(pattern
==NULL
&& patternLength
!=0) {
133 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
134 return UDATPG_NO_CONFLICT
;
136 UnicodeString
patternString((UBool
)(patternLength
<0), pattern
, patternLength
);
137 UnicodeString conflictingPatternString
;
138 UDateTimePatternConflict result
=((DateTimePatternGenerator
*)dtpg
)->
139 addPattern(patternString
, override
, conflictingPatternString
, *pErrorCode
);
140 int32_t length
=conflictingPatternString
.extract(conflictingPattern
, capacity
, *pErrorCode
);
147 U_CAPI
void U_EXPORT2
148 udatpg_setAppendItemFormat(UDateTimePatternGenerator
*dtpg
,
149 UDateTimePatternField field
,
150 const UChar
*value
, int32_t length
) {
151 UnicodeString
valueString((UBool
)(length
<0), value
, length
);
152 ((DateTimePatternGenerator
*)dtpg
)->setAppendItemFormat(field
, valueString
);
155 U_CAPI
const UChar
* U_EXPORT2
156 udatpg_getAppendItemFormat(const UDateTimePatternGenerator
*dtpg
,
157 UDateTimePatternField field
,
159 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getAppendItemFormat(field
);
161 *pLength
=result
.length();
163 return result
.getBuffer();
166 U_CAPI
void U_EXPORT2
167 udatpg_setAppendItemName(UDateTimePatternGenerator
*dtpg
,
168 UDateTimePatternField field
,
169 const UChar
*value
, int32_t length
) {
170 UnicodeString
valueString((UBool
)(length
<0), value
, length
);
171 ((DateTimePatternGenerator
*)dtpg
)->setAppendItemName(field
, valueString
);
174 U_CAPI
const UChar
* U_EXPORT2
175 udatpg_getAppendItemName(const UDateTimePatternGenerator
*dtpg
,
176 UDateTimePatternField field
,
178 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getAppendItemName(field
);
180 *pLength
=result
.length();
182 return result
.getBuffer();
185 U_CAPI
void U_EXPORT2
186 udatpg_setDateTimeFormat(const UDateTimePatternGenerator
*dtpg
,
187 const UChar
*dtFormat
, int32_t length
) {
188 UnicodeString
dtFormatString((UBool
)(length
<0), dtFormat
, length
);
189 ((DateTimePatternGenerator
*)dtpg
)->setDateTimeFormat(dtFormatString
);
192 U_CAPI
const UChar
* U_EXPORT2
193 udatpg_getDateTimeFormat(const UDateTimePatternGenerator
*dtpg
,
195 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getDateTimeFormat();
197 *pLength
=result
.length();
199 return result
.getBuffer();
202 U_CAPI
void U_EXPORT2
203 udatpg_setDecimal(UDateTimePatternGenerator
*dtpg
,
204 const UChar
*decimal
, int32_t length
) {
205 UnicodeString
decimalString((UBool
)(length
<0), decimal
, length
);
206 ((DateTimePatternGenerator
*)dtpg
)->setDecimal(decimalString
);
209 U_CAPI
const UChar
* U_EXPORT2
210 udatpg_getDecimal(const UDateTimePatternGenerator
*dtpg
,
212 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getDecimal();
214 *pLength
=result
.length();
216 return result
.getBuffer();
219 U_CAPI
int32_t U_EXPORT2
220 udatpg_replaceFieldTypes(UDateTimePatternGenerator
*dtpg
,
221 const UChar
*pattern
, int32_t patternLength
,
222 const UChar
*skeleton
, int32_t skeletonLength
,
223 UChar
*dest
, int32_t destCapacity
,
224 UErrorCode
*pErrorCode
) {
225 return udatpg_replaceFieldTypesWithOptions(dtpg
, pattern
, patternLength
, skeleton
, skeletonLength
,
226 UDATPG_MATCH_NO_OPTIONS
,
227 dest
, destCapacity
, pErrorCode
);
230 U_CAPI
int32_t U_EXPORT2
231 udatpg_replaceFieldTypesWithOptions(UDateTimePatternGenerator
*dtpg
,
232 const UChar
*pattern
, int32_t patternLength
,
233 const UChar
*skeleton
, int32_t skeletonLength
,
234 UDateTimePatternMatchOptions options
,
235 UChar
*dest
, int32_t destCapacity
,
236 UErrorCode
*pErrorCode
) {
237 if(U_FAILURE(*pErrorCode
)) {
240 if((pattern
==NULL
&& patternLength
!=0) || (skeleton
==NULL
&& skeletonLength
!=0)) {
241 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
244 UnicodeString
patternString((UBool
)(patternLength
<0), pattern
, patternLength
);
245 UnicodeString
skeletonString((UBool
)(skeletonLength
<0), skeleton
, skeletonLength
);
246 UnicodeString result
=((DateTimePatternGenerator
*)dtpg
)->replaceFieldTypes(patternString
, skeletonString
, options
, *pErrorCode
);
247 return result
.extract(dest
, destCapacity
, *pErrorCode
);
250 U_CAPI UEnumeration
* U_EXPORT2
251 udatpg_openSkeletons(const UDateTimePatternGenerator
*dtpg
, UErrorCode
*pErrorCode
) {
252 return uenum_openFromStringEnumeration(
253 ((DateTimePatternGenerator
*)dtpg
)->getSkeletons(*pErrorCode
),
257 U_CAPI UEnumeration
* U_EXPORT2
258 udatpg_openBaseSkeletons(const UDateTimePatternGenerator
*dtpg
, UErrorCode
*pErrorCode
) {
259 return uenum_openFromStringEnumeration(
260 ((DateTimePatternGenerator
*)dtpg
)->getBaseSkeletons(*pErrorCode
),
264 U_CAPI
const UChar
* U_EXPORT2
265 udatpg_getPatternForSkeleton(const UDateTimePatternGenerator
*dtpg
,
266 const UChar
*skeleton
, int32_t skeletonLength
,
268 UnicodeString
skeletonString((UBool
)(skeletonLength
<0), skeleton
, skeletonLength
);
269 const UnicodeString
&result
=((const DateTimePatternGenerator
*)dtpg
)->getPatternForSkeleton(skeletonString
);
271 *pLength
=result
.length();
273 return result
.getBuffer();
276 // Helper function for uadatpg_remapPatternWithOptionsLoc
278 _doReplaceAndReturnAdj( UDateTimePatternGenerator
*dtpg
, uint32_t options
, UBool matchHourLen
,
279 UnicodeString
&patternString
, const UnicodeString
&skeleton
, const UnicodeString
&otherCycSkeleton
,
280 int32_t timePatStart
, int32_t timePatLimit
, int32_t timeNonHourStart
, int32_t timeNonHourLimit
,
281 UErrorCode
*pErrorCode
) {
283 options
|= UDATPG_MATCH_HOUR_FIELD_LENGTH
;
285 UnicodeString replacement
=((DateTimePatternGenerator
*)dtpg
)->getBestPattern(otherCycSkeleton
, (UDateTimePatternMatchOptions
)options
, *pErrorCode
);
286 if (U_FAILURE(*pErrorCode
)) {
289 UnicodeString stringForOrigSkeleton
=((DateTimePatternGenerator
*)dtpg
)->getBestPattern(skeleton
, (UDateTimePatternMatchOptions
)options
, *pErrorCode
);
290 if (U_SUCCESS(*pErrorCode
)) {
291 int32_t index
= patternString
.indexOf(stringForOrigSkeleton
);
293 int32_t stringForOrigSkelLen
= stringForOrigSkeleton
.length();
294 patternString
.replace(index
, stringForOrigSkelLen
, replacement
);
295 return replacement
.length() - stringForOrigSkelLen
;
298 *pErrorCode
= U_ZERO_ERROR
;
300 if (timeNonHourStart
>= 0 && timeNonHourLimit
> timeNonHourStart
) {
301 // find any minutes/seconds/milliseconds part of replacement, set that back to the
302 // minutes/seconds/milliseconds part of the original pattern.
303 // First get the minutes/seconds/milliseconds part of the original pattern.
304 UnicodeString nonHour
;
305 patternString
.extractBetween(timeNonHourStart
, timeNonHourLimit
, nonHour
);
306 // Now scan to find position from just after hours to end of minutes/seconds/milliseconds.
307 timeNonHourStart
= -1;
308 timeNonHourLimit
= 0;
309 UBool inQuoted
= FALSE
;
310 int32_t repPos
, repLen
= replacement
.length();
311 for (repPos
= 0; repPos
< repLen
; repPos
++) {
312 UChar repChr
= replacement
.charAt(repPos
);
313 if (repChr
== 0x27 /* ASCII-range single quote */) {
314 inQuoted
= !inQuoted
;
315 } else if (!inQuoted
) {
316 if (repChr
==LOW_H
|| repChr
==CAP_H
|| repChr
==CAP_K
|| repChr
==LOW_K
) { // hHKk, hour
317 timeNonHourStart
= repPos
+ 1;
318 } else if (timeNonHourStart
< 0 && (repChr
==LOW_M
|| repChr
==LOW_S
)) { // 'm' or 's' and we did not have hour
319 timeNonHourStart
= repPos
;
321 if (!u_isWhitespace(repChr
) && timeNonHourStart
>= 0 && repChr
!=LOW_A
) { // NonHour portion should not include 'a'
322 timeNonHourLimit
= repPos
+ 1;
326 // If we found minutes/seconds/milliseconds in replacement, restore that part to original.
327 if (timeNonHourStart
>= 0 && timeNonHourLimit
> timeNonHourStart
) {
328 replacement
.replaceBetween(timeNonHourStart
, timeNonHourLimit
, nonHour
);
331 patternString
.replaceBetween(timePatStart
, timePatLimit
, replacement
);
332 return replacement
.length() - (timePatLimit
- timePatStart
); // positive if replacement is longer
336 * uadatpg_remapPatternWithOptionsLoc
338 * Thee general idea is:
339 * 1. Scan the pattern for one or more time subpatterns
340 * 2. For each time subpattern, if the hour pattern characters don't match the
341 * time cycle that we want to force to, then:
342 * a) Save the nonHour portion of the subpattern (from just after hours to end
343 * of minutes/seconds/ milliseconds)
344 * b) Turn the pattern characters in that subpattern into a skeleton, but with
345 * the hour pattern characters switched to the desired time cycle
346 * c) Use that skeleton to get the locale's corresponding replacement pattern
347 * for the desired time cycle (with all desired elements - min, sec, etc.)
348 * d) In that replacement pattern, find the new nonHour portion, and restore
349 * that to the original nonHour portion
350 * e) Finally, replace the original time subpattern with the adjusted
353 U_CAPI
int32_t U_EXPORT2
354 uadatpg_remapPatternWithOptions(UDateTimePatternGenerator
*dtpg
,
355 const UChar
*pattern
, int32_t patternLength
,
356 UDateTimePatternMatchOptions options
,
357 UChar
*newPattern
, int32_t newPatternCapacity
,
358 UErrorCode
*pErrorCode
) {
359 if (U_FAILURE(*pErrorCode
)) {
362 if ( pattern
==NULL
|| ((newPattern
==NULL
)? newPatternCapacity
!=0: newPatternCapacity
<0) ) {
363 *pErrorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
366 UnicodeString
patternString((patternLength
< 0), pattern
, patternLength
);
367 UBool force12
= ((options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) == UADATPG_FORCE_12_HOUR_CYCLE
);
368 UBool force24
= ((options
& UADATPG_FORCE_HOUR_CYCLE_MASK
) == UADATPG_FORCE_24_HOUR_CYCLE
);
369 if (force12
|| force24
) {
370 UBool inQuoted
= FALSE
;
371 UBool inTimePat
= FALSE
;
372 UBool needReplacement
= FALSE
;
373 int32_t timePatStart
= 0;
374 int32_t timePatLimit
= 0;
375 int32_t timeNonHourStart
= -1;
376 int32_t timeNonHourLimit
= 0;
377 UnicodeString skeleton
, otherCycSkeleton
;
378 UnicodeString
timePatChars("abBhHKkmsSzZOvVXx", -1, US_INV
); // all pattern chars for times
379 int32_t numForcedH
= 0;
380 int32_t patPos
, patLen
= patternString
.length();
382 for (patPos
= 0; patPos
< patLen
; patPos
++) {
383 UChar patChr
= patternString
.charAt(patPos
);
384 UChar otherCycPatChr
= patChr
;
385 if (patChr
== 0x27 /* ASCII-range single quote */) {
386 inQuoted
= !inQuoted
;
387 } else if (!inQuoted
) {
388 if (timePatChars
.indexOf(patChr
) >= 0) {
392 timePatStart
= patPos
;
393 timeNonHourStart
= -1;
395 otherCycSkeleton
.remove();
398 if (patChr
==LOW_H
|| patChr
==CAP_K
) { // hK, hour, 12-hour cycle
400 otherCycPatChr
= CAP_H
; // force to H
401 needReplacement
= TRUE
;
402 timeNonHourStart
= patPos
+ 1;
403 // If we are switching from a 12-hour cycle to a 24-hour cycle
404 // and the pattern for 12-hour cycle was zero-padded to 2 digits,
405 // make sure the new pattern for 24-hour cycle is also padded to
406 // 2 digits regardless of locale default, to match the
407 // expectations of the pattern provider. However we don't need
408 // to do this going the other direction (from 24- to 12-hour
409 // cycles, don't require that the 12-hour cycle has zero padding
410 // just because the 24-hour cycle did; the 12-hour cycle will
411 // add other elements such as a, so there wil be a length change
415 } else if (patChr
==CAP_H
|| patChr
==LOW_K
) { // Hk, hour, 24-hour cycle
417 otherCycPatChr
= LOW_H
; // force to h
418 needReplacement
= TRUE
;
419 timeNonHourStart
= patPos
+ 1;
421 } else if (timeNonHourStart
< 0 && (patChr
==LOW_M
|| patChr
==LOW_S
)) { // 'm' or 's' and we did not have hour
422 timeNonHourStart
= patPos
;
424 skeleton
.append(patChr
);
425 otherCycSkeleton
.append(otherCycPatChr
);
426 } else if ((patChr
>= 0x41 && patChr
<= 0x5A) || (patChr
>= 0x61 && patChr
<= 0x7A)) {
427 // a non-time pattern character, forces end of any time pattern
430 if (needReplacement
) {
431 needReplacement
= FALSE
;
433 int32_t posAdjust
= _doReplaceAndReturnAdj(dtpg
, options
, numForcedH
>= 2, patternString
, skeleton
, otherCycSkeleton
,
434 timePatStart
, timePatLimit
, timeNonHourStart
, timeNonHourLimit
, pErrorCode
);
440 if (inTimePat
&& !u_isWhitespace(patChr
)) {
441 timePatLimit
= patPos
+ 1;
442 if (timeNonHourStart
>= 0 && patChr
!=LOW_A
&& patChr
!=LOW_B
&& patChr
!=CAP_B
) { // NonHour portion should not include 'a','b','B'
443 timeNonHourLimit
= timePatLimit
;
449 if (needReplacement
) {
451 _doReplaceAndReturnAdj(dtpg
, options
, numForcedH
>= 2, patternString
, skeleton
, otherCycSkeleton
,
452 timePatStart
, timePatLimit
, timeNonHourStart
, timeNonHourLimit
, pErrorCode
);
455 return patternString
.extract(newPattern
, newPatternCapacity
, *pErrorCode
);