]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/udatpg.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / i18n / udatpg.cpp
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 * Copyright (C) 2009-2015, International Business Machines
7 * Corporation and others. All Rights Reserved.
8 *
9 *******************************************************************************
10 * file name: udatpg.cpp
11 * encoding: UTF-8
12 * tab size: 8 (not used)
13 * indentation:4
14 *
15 * created on: 2007jul30
16 * created by: Markus W. Scherer
17 */
18
19 #include "unicode/utypes.h"
20
21 #if !UCONFIG_NO_FORMATTING
22
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"
29 #include "ustrenum.h"
30 #include "dtitv_impl.h"
31
32 U_NAMESPACE_USE
33
34 U_CAPI UDateTimePatternGenerator * U_EXPORT2
35 udatpg_open(const char *locale, UErrorCode *pErrorCode) {
36 if(locale==NULL) {
37 return (UDateTimePatternGenerator *)DateTimePatternGenerator::createInstance(*pErrorCode);
38 } else {
39 return (UDateTimePatternGenerator *)DateTimePatternGenerator::createInstance(Locale(locale), *pErrorCode);
40 }
41 }
42
43 U_CAPI UDateTimePatternGenerator * U_EXPORT2
44 udatpg_openEmpty(UErrorCode *pErrorCode) {
45 return (UDateTimePatternGenerator *)DateTimePatternGenerator::createEmptyInstance(*pErrorCode);
46 }
47
48 U_CAPI void U_EXPORT2
49 udatpg_close(UDateTimePatternGenerator *dtpg) {
50 delete (DateTimePatternGenerator *)dtpg;
51 }
52
53 U_CAPI UDateTimePatternGenerator * U_EXPORT2
54 udatpg_clone(const UDateTimePatternGenerator *dtpg, UErrorCode *pErrorCode) {
55 if(U_FAILURE(*pErrorCode)) {
56 return NULL;
57 }
58 return (UDateTimePatternGenerator *)(((const DateTimePatternGenerator *)dtpg)->clone());
59 }
60
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);
69 }
70
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)) {
78 return 0;
79 }
80 if(skeleton==NULL && length!=0) {
81 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
82 return 0;
83 }
84 UnicodeString skeletonString((UBool)(length<0), skeleton, length);
85 UnicodeString result=((DateTimePatternGenerator *)dtpg)->getBestPattern(skeletonString, options, *pErrorCode);
86 return result.extract(bestPattern, capacity, *pErrorCode);
87 }
88
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)) {
95 return 0;
96 }
97 if(pattern==NULL && length!=0) {
98 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
99 return 0;
100 }
101 UnicodeString patternString((UBool)(length<0), pattern, length);
102 UnicodeString result=DateTimePatternGenerator::staticGetSkeleton(
103 patternString, *pErrorCode);
104 return result.extract(skeleton, capacity, *pErrorCode);
105 }
106
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)) {
113 return 0;
114 }
115 if(pattern==NULL && length!=0) {
116 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
117 return 0;
118 }
119 UnicodeString patternString((UBool)(length<0), pattern, length);
120 UnicodeString result=DateTimePatternGenerator::staticGetBaseSkeleton(
121 patternString, *pErrorCode);
122 return result.extract(skeleton, capacity, *pErrorCode);
123 }
124
125 U_CAPI UDateTimePatternConflict U_EXPORT2
126 udatpg_addPattern(UDateTimePatternGenerator *dtpg,
127 const UChar *pattern, int32_t patternLength,
128 UBool override,
129 UChar *conflictingPattern, int32_t capacity, int32_t *pLength,
130 UErrorCode *pErrorCode) {
131 if(U_FAILURE(*pErrorCode)) {
132 return UDATPG_NO_CONFLICT;
133 }
134 if(pattern==NULL && patternLength!=0) {
135 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
136 return UDATPG_NO_CONFLICT;
137 }
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);
143 if(pLength!=NULL) {
144 *pLength=length;
145 }
146 return result;
147 }
148
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);
155 }
156
157 U_CAPI const UChar * U_EXPORT2
158 udatpg_getAppendItemFormat(const UDateTimePatternGenerator *dtpg,
159 UDateTimePatternField field,
160 int32_t *pLength) {
161 const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getAppendItemFormat(field);
162 if(pLength!=NULL) {
163 *pLength=result.length();
164 }
165 return result.getBuffer();
166 }
167
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);
174 }
175
176 U_CAPI const UChar * U_EXPORT2
177 udatpg_getAppendItemName(const UDateTimePatternGenerator *dtpg,
178 UDateTimePatternField field,
179 int32_t *pLength) {
180 const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getAppendItemName(field);
181 if(pLength!=NULL) {
182 *pLength=result.length();
183 }
184 return result.getBuffer();
185 }
186
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))
194 return -1;
195 if (fieldName == NULL ? capacity != 0 : capacity < 0) {
196 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
197 return -1;
198 }
199 UnicodeString result = ((const DateTimePatternGenerator *)dtpg)->getFieldDisplayName(field,width);
200 if (fieldName == NULL) {
201 return result.length();
202 }
203 return result.extract(fieldName, capacity, *pErrorCode);
204 }
205
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);
211 }
212
213 U_CAPI const UChar * U_EXPORT2
214 udatpg_getDateTimeFormat(const UDateTimePatternGenerator *dtpg,
215 int32_t *pLength) {
216 const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getDateTimeFormat();
217 if(pLength!=NULL) {
218 *pLength=result.length();
219 }
220 return result.getBuffer();
221 }
222
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);
228 }
229
230 U_CAPI const UChar * U_EXPORT2
231 udatpg_getDecimal(const UDateTimePatternGenerator *dtpg,
232 int32_t *pLength) {
233 const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getDecimal();
234 if(pLength!=NULL) {
235 *pLength=result.length();
236 }
237 return result.getBuffer();
238 }
239
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);
249 }
250
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)) {
259 return 0;
260 }
261 if((pattern==NULL && patternLength!=0) || (skeleton==NULL && skeletonLength!=0)) {
262 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
263 return 0;
264 }
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);
269 }
270
271 U_CAPI UEnumeration * U_EXPORT2
272 udatpg_openSkeletons(const UDateTimePatternGenerator *dtpg, UErrorCode *pErrorCode) {
273 return uenum_openFromStringEnumeration(
274 ((DateTimePatternGenerator *)dtpg)->getSkeletons(*pErrorCode),
275 pErrorCode);
276 }
277
278 U_CAPI UEnumeration * U_EXPORT2
279 udatpg_openBaseSkeletons(const UDateTimePatternGenerator *dtpg, UErrorCode *pErrorCode) {
280 return uenum_openFromStringEnumeration(
281 ((DateTimePatternGenerator *)dtpg)->getBaseSkeletons(*pErrorCode),
282 pErrorCode);
283 }
284
285 U_CAPI const UChar * U_EXPORT2
286 udatpg_getPatternForSkeleton(const UDateTimePatternGenerator *dtpg,
287 const UChar *skeleton, int32_t skeletonLength,
288 int32_t *pLength) {
289 UnicodeString skeletonString((UBool)(skeletonLength<0), skeleton, skeletonLength);
290 const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getPatternForSkeleton(skeletonString);
291 if(pLength!=NULL) {
292 *pLength=result.length();
293 }
294 return result.getBuffer();
295 }
296
297 // Helper function for uadatpg_remapPatternWithOptionsLoc
298 static int32_t
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) {
303 if (matchHourLen) {
304 options |= UDATPG_MATCH_HOUR_FIELD_LENGTH;
305 }
306 UnicodeString replacement=((DateTimePatternGenerator *)dtpg)->getBestPattern(otherCycSkeleton, (UDateTimePatternMatchOptions)options, *pErrorCode);
307 if (U_FAILURE(*pErrorCode)) {
308 return 0;
309 }
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);
313 if (index >= 0) {
314 int32_t stringForOrigSkelLen = stringForOrigSkeleton.length();
315 patternString.replace(index, stringForOrigSkelLen, replacement);
316 return replacement.length() - stringForOrigSkelLen;
317 }
318 } else {
319 *pErrorCode = U_ZERO_ERROR;
320 }
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;
341 }
342 if (!u_isWhitespace(repChr) && timeNonHourStart >= 0 && repChr!=LOW_A) { // NonHour portion should not include 'a'
343 timeNonHourLimit = repPos + 1;
344 }
345 }
346 }
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);
350 }
351 }
352 patternString.replaceBetween(timePatStart, timePatLimit, replacement);
353 return replacement.length() - (timePatLimit - timePatStart); // positive if replacement is longer
354 }
355
356 /*
357 * uadatpg_remapPatternWithOptionsLoc
358 *
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
372 * replacement.
373 */
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)) {
381 return 0;
382 }
383 if ( pattern==NULL || ((newPattern==NULL)? newPatternCapacity!=0: newPatternCapacity<0) ) {
384 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
385 return 0;
386 }
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();
402
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) {
410 // in a time pattern
411 if (!inTimePat) {
412 inTimePat = TRUE;
413 timePatStart = patPos;
414 timeNonHourStart = -1;
415 skeleton.remove();
416 otherCycSkeleton.remove();
417 numForcedH = 0;
418 }
419 if (patChr==LOW_H || patChr==CAP_K) { // hK, hour, 12-hour cycle
420 if (force24) {
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
433 // anyway).
434 numForcedH++;
435 }
436 } else if (patChr==CAP_H || patChr==LOW_K) { // Hk, hour, 24-hour cycle
437 if (force12) {
438 otherCycPatChr = LOW_H; // force to h
439 needReplacement = TRUE;
440 timeNonHourStart = patPos + 1;
441 }
442 } else if (timeNonHourStart < 0 && (patChr==LOW_M || patChr==LOW_S)) { // 'm' or 's' and we did not have hour
443 timeNonHourStart = patPos;
444 }
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
449 if (inTimePat) {
450 inTimePat = FALSE;
451 if (needReplacement) {
452 needReplacement = FALSE;
453 // do replacement
454 int32_t posAdjust = _doReplaceAndReturnAdj(dtpg, options, numForcedH >= 2, patternString, skeleton, otherCycSkeleton,
455 timePatStart, timePatLimit, timeNonHourStart, timeNonHourLimit, pErrorCode);
456 patLen += posAdjust;
457 patPos += posAdjust;
458 }
459 }
460 }
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;
465 }
466 }
467 }
468 }
469 // end of string
470 if (needReplacement) {
471 // do replacement
472 _doReplaceAndReturnAdj(dtpg, options, numForcedH >= 2, patternString, skeleton, otherCycSkeleton,
473 timePatStart, timePatLimit, timeNonHourStart, timeNonHourLimit, pErrorCode);
474 }
475 }
476 return patternString.extract(newPattern, newPatternCapacity, *pErrorCode);
477 }
478
479 #endif