]> git.saurik.com Git - apple/cf.git/blob - CFDateFormatter.c
CF-855.11.tar.gz
[apple/cf.git] / CFDateFormatter.c
1 /*
2 * Copyright (c) 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFDateFormatter.c
25 Copyright (c) 2002-2013, Apple Inc. All rights reserved.
26 Responsibility: David Smith
27 */
28
29 #define U_SHOW_INTERNAL_API 1
30
31 #include <CoreFoundation/CFDateFormatter.h>
32 #include <CoreFoundation/CFDate.h>
33 #include <CoreFoundation/CFTimeZone.h>
34 #include <CoreFoundation/CFCalendar.h>
35 #include <CoreFoundation/CFNumber.h>
36 #include "CFPriv.h"
37 #include "CFInternal.h"
38 #include "CFLocaleInternal.h"
39 #include "CFICULogging.h"
40 #include <math.h>
41 #include <float.h>
42
43
44 typedef CF_ENUM(CFIndex, CFDateFormatterAmbiguousYearHandling) {
45 kCFDateFormatterAmbiguousYearFailToParse = 0, // fail the parse; the default formatter behavior
46 kCFDateFormatterAmbiguousYearAssumeToNone = 1, // default to assuming era 1, or the year 0-99
47 kCFDateFormatterAmbiguousYearAssumeToCurrent = 2, // default to assuming the current century or era
48 kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate = 3,
49 kCFDateFormatterAmbiguousYearAssumeToFuture = 4,
50 kCFDateFormatterAmbiguousYearAssumeToPast = 5,
51 kCFDateFormatterAmbiguousYearAssumeToLikelyFuture = 6,
52 kCFDateFormatterAmbiguousYearAssumeToLikelyPast = 7
53 };
54
55 extern UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz);
56
57 CF_EXPORT const CFStringRef kCFDateFormatterCalendarIdentifierKey;
58
59 #undef CFReleaseIfNotNull
60 #define CFReleaseIfNotNull(X) if (X) CFRelease(X)
61
62 #define BUFFER_SIZE 768
63
64 static CFStringRef __CFDateFormatterCreateForcedTemplate(CFLocaleRef locale, CFStringRef inString);
65
66 // If you pass in a string in tmplate, you get back NULL (failure) or a CFStringRef.
67 // If you pass in an array in tmplate, you get back NULL (global failure) or a CFArrayRef with CFStringRefs or kCFNulls (per-template failure) at each corresponding index.
68
69 CFArrayRef CFDateFormatterCreateDateFormatsFromTemplates(CFAllocatorRef allocator, CFArrayRef tmplates, CFOptionFlags options, CFLocaleRef locale) {
70 return (CFArrayRef)CFDateFormatterCreateDateFormatFromTemplate(allocator, (CFStringRef)tmplates, options, locale);
71 }
72
73 static Boolean useTemplatePatternGenerator(const char *localeName, void(^work)(UDateTimePatternGenerator *ptg)) {
74 static UDateTimePatternGenerator *ptg;
75 static pthread_mutex_t ptgLock = PTHREAD_MUTEX_INITIALIZER;
76 static const char *ptgLocaleName;
77
78 static void (^flushCache)() = ^{
79 __cficu_udatpg_close(ptg);
80 ptg = NULL;
81 free((void *)ptgLocaleName);
82 ptgLocaleName = NULL;
83 };
84 pthread_mutex_lock(&ptgLock);
85 if (ptgLocaleName && strcmp(ptgLocaleName, localeName) != 0) {
86 flushCache();
87 }
88 UErrorCode status = U_ZERO_ERROR;
89 if (!ptg) {
90 ptg = __cficu_udatpg_open(localeName, &status);
91 if (ptg && !U_FAILURE(status)) {
92 ptgLocaleName = strdup(localeName);
93 }
94 }
95 Boolean result = (NULL != ptg && !U_FAILURE(status));
96 if (result && work) {
97 work(ptg);
98 }
99 pthread_mutex_unlock(&ptgLock);
100 return result;
101 }
102
103
104 CFStringRef CFDateFormatterCreateDateFormatFromTemplate(CFAllocatorRef allocator, CFStringRef tmplate, CFOptionFlags options, CFLocaleRef locale) {
105 if (allocator) __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
106 if (locale) __CFGenericValidateType(locale, CFLocaleGetTypeID());
107 Boolean tmplateIsString = (CFStringGetTypeID() == CFGetTypeID(tmplate));
108 if (!tmplateIsString) {
109 __CFGenericValidateType(tmplate, CFArrayGetTypeID());
110 }
111
112 CFStringRef localeName = locale ? CFLocaleGetIdentifier(locale) : CFSTR("");
113 char buffer[BUFFER_SIZE];
114 const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII);
115 if (NULL == cstr) {
116 if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
117 }
118 if (NULL == cstr) {
119 return NULL;
120 }
121
122 __block CFTypeRef result = tmplateIsString ? NULL : (CFTypeRef)CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
123
124 Boolean success = useTemplatePatternGenerator(cstr, ^(UDateTimePatternGenerator *ptg) {
125
126
127 for (CFIndex idx = 0, cnt = tmplateIsString ? 1 : CFArrayGetCount((CFArrayRef)tmplate); idx < cnt; idx++) {
128 CFStringRef tmplateString = tmplateIsString ? (CFStringRef)tmplate : (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)tmplate, idx);
129 CFStringRef resultString = NULL;
130
131 tmplateString = __CFDateFormatterCreateForcedTemplate(locale ? locale : CFLocaleGetSystem(), tmplateString);
132
133 CFIndex jCount = 0; // the only interesting cases are 0, 1, and 2 (adjacent)
134 CFRange r = CFStringFind(tmplateString, CFSTR("j"), 0);
135 if (kCFNotFound != r.location) {
136 jCount++;
137 if ((r.location + 1 < CFStringGetLength(tmplateString)) && ('j' == CFStringGetCharacterAtIndex(tmplateString, r.location + 1))) {
138 jCount++;
139 }
140 }
141
142 UChar pattern[BUFFER_SIZE], skel[BUFFER_SIZE], bpat[BUFFER_SIZE];
143 CFIndex tmpltLen = CFStringGetLength(tmplateString);
144 if (BUFFER_SIZE < tmpltLen) tmpltLen = BUFFER_SIZE;
145 CFStringGetCharacters(tmplateString, CFRangeMake(0, tmpltLen), (UniChar *)pattern);
146 CFRelease(tmplateString);
147
148 int32_t patlen = tmpltLen;
149 UErrorCode status = U_ZERO_ERROR;
150 int32_t skellen = __cficu_udatpg_getSkeleton(ptg, pattern, patlen, skel, sizeof(skel) / sizeof(skel[0]), &status);
151 if (!U_FAILURE(status)) {
152 if ((0 < jCount) && (skellen + jCount < (sizeof(skel) / sizeof(skel[0])))) {
153 skel[skellen++] = 'j';
154 if (1 < jCount) skel[skellen++] = 'j';
155 }
156
157 status = U_ZERO_ERROR;
158 int32_t bpatlen = __cficu_udatpg_getBestPattern(ptg, skel, skellen, bpat, sizeof(bpat) / sizeof(bpat[0]), &status);
159 if (!U_FAILURE(status)) {
160 resultString = CFStringCreateWithCharacters(allocator, (const UniChar *)bpat, bpatlen);
161 }
162 }
163
164 if (tmplateIsString) {
165 result = (CFTypeRef)resultString;
166 } else {
167 CFArrayAppendValue((CFMutableArrayRef)result, resultString ? (CFTypeRef)resultString : (CFTypeRef)kCFNull);
168 if (resultString) CFRelease(resultString);
169 }
170 }
171 });
172
173 if (!success) {
174 CFRelease(result);
175 result = NULL;
176 }
177
178 return (CFStringRef)result;
179 }
180
181 struct __CFDateFormatter {
182 CFRuntimeBase _base;
183 UDateFormat *_df;
184 CFLocaleRef _locale;
185 CFDateFormatterStyle _timeStyle;
186 CFDateFormatterStyle _dateStyle;
187 CFStringRef _format;
188 CFStringRef _defformat;
189 struct {
190 CFBooleanRef _IsLenient;
191 CFBooleanRef _DoesRelativeDateFormatting;
192 CFBooleanRef _HasCustomFormat;
193 CFTimeZoneRef _TimeZone;
194 CFCalendarRef _Calendar;
195 CFStringRef _CalendarName;
196 CFDateRef _TwoDigitStartDate;
197 CFDateRef _DefaultDate;
198 CFDateRef _GregorianStartDate;
199 CFArrayRef _EraSymbols;
200 CFArrayRef _LongEraSymbols;
201 CFArrayRef _MonthSymbols;
202 CFArrayRef _ShortMonthSymbols;
203 CFArrayRef _VeryShortMonthSymbols;
204 CFArrayRef _StandaloneMonthSymbols;
205 CFArrayRef _ShortStandaloneMonthSymbols;
206 CFArrayRef _VeryShortStandaloneMonthSymbols;
207 CFArrayRef _WeekdaySymbols;
208 CFArrayRef _ShortWeekdaySymbols;
209 CFArrayRef _VeryShortWeekdaySymbols;
210 CFArrayRef _StandaloneWeekdaySymbols;
211 CFArrayRef _ShortStandaloneWeekdaySymbols;
212 CFArrayRef _VeryShortStandaloneWeekdaySymbols;
213 CFArrayRef _QuarterSymbols;
214 CFArrayRef _ShortQuarterSymbols;
215 CFArrayRef _StandaloneQuarterSymbols;
216 CFArrayRef _ShortStandaloneQuarterSymbols;
217 CFStringRef _AMSymbol;
218 CFStringRef _PMSymbol;
219 CFNumberRef _AmbiguousYearStrategy;
220 CFBooleanRef _UsesCharacterDirection;
221
222 // the following are from preferences
223 CFArrayRef _CustomEraSymbols;
224 CFArrayRef _CustomLongEraSymbols;
225 CFArrayRef _CustomMonthSymbols;
226 CFArrayRef _CustomShortMonthSymbols;
227 CFArrayRef _CustomVeryShortMonthSymbols;
228 CFArrayRef _CustomStandaloneMonthSymbols;
229 CFArrayRef _CustomShortStandaloneMonthSymbols;
230 CFArrayRef _CustomVeryShortStandaloneMonthSymbols;
231 CFArrayRef _CustomWeekdaySymbols;
232 CFArrayRef _CustomShortWeekdaySymbols;
233 CFArrayRef _CustomVeryShortWeekdaySymbols;
234 CFArrayRef _CustomStandaloneWeekdaySymbols;
235 CFArrayRef _CustomShortStandaloneWeekdaySymbols;
236 CFArrayRef _CustomVeryShortStandaloneWeekdaySymbols;
237 CFArrayRef _CustomQuarterSymbols;
238 CFArrayRef _CustomShortQuarterSymbols;
239 CFArrayRef _CustomStandaloneQuarterSymbols;
240 CFArrayRef _CustomShortStandaloneQuarterSymbols;
241 CFStringRef _CustomDateFormat;
242 CFStringRef _CustomTimeFormat;
243 CFBooleanRef _Custom24Hour;
244 CFBooleanRef _Custom12Hour;
245 CFStringRef _CustomAMSymbol;
246 CFStringRef _CustomPMSymbol;
247 CFDictionaryRef _CustomFirstWeekday;
248 CFDictionaryRef _CustomMinDaysInFirstWeek;
249
250 } _property;
251 };
252
253 static CFStringRef __CFDateFormatterCopyDescription(CFTypeRef cf) {
254 CFDateFormatterRef formatter = (CFDateFormatterRef)cf;
255 return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("<CFDateFormatter %p [%p]>"), cf, CFGetAllocator(formatter));
256 }
257
258 static void __CFDateFormatterDeallocate(CFTypeRef cf) {
259 CFDateFormatterRef formatter = (CFDateFormatterRef)cf;
260 if (formatter->_df) __cficu_udat_close(formatter->_df);
261 if (formatter->_locale) CFRelease(formatter->_locale);
262 if (formatter->_format) CFRelease(formatter->_format);
263 if (formatter->_defformat) CFRelease(formatter->_defformat);
264 CFReleaseIfNotNull(formatter->_property._IsLenient);
265 CFReleaseIfNotNull(formatter->_property._DoesRelativeDateFormatting);
266 CFReleaseIfNotNull(formatter->_property._TimeZone);
267 CFReleaseIfNotNull(formatter->_property._Calendar);
268 CFReleaseIfNotNull(formatter->_property._CalendarName);
269 CFReleaseIfNotNull(formatter->_property._TwoDigitStartDate);
270 CFReleaseIfNotNull(formatter->_property._DefaultDate);
271 CFReleaseIfNotNull(formatter->_property._GregorianStartDate);
272 CFReleaseIfNotNull(formatter->_property._EraSymbols);
273 CFReleaseIfNotNull(formatter->_property._LongEraSymbols);
274 CFReleaseIfNotNull(formatter->_property._MonthSymbols);
275 CFReleaseIfNotNull(formatter->_property._ShortMonthSymbols);
276 CFReleaseIfNotNull(formatter->_property._VeryShortMonthSymbols);
277 CFReleaseIfNotNull(formatter->_property._StandaloneMonthSymbols);
278 CFReleaseIfNotNull(formatter->_property._ShortStandaloneMonthSymbols);
279 CFReleaseIfNotNull(formatter->_property._VeryShortStandaloneMonthSymbols);
280 CFReleaseIfNotNull(formatter->_property._WeekdaySymbols);
281 CFReleaseIfNotNull(formatter->_property._ShortWeekdaySymbols);
282 CFReleaseIfNotNull(formatter->_property._VeryShortWeekdaySymbols);
283 CFReleaseIfNotNull(formatter->_property._StandaloneWeekdaySymbols);
284 CFReleaseIfNotNull(formatter->_property._ShortStandaloneWeekdaySymbols);
285 CFReleaseIfNotNull(formatter->_property._VeryShortStandaloneWeekdaySymbols);
286 CFReleaseIfNotNull(formatter->_property._QuarterSymbols);
287 CFReleaseIfNotNull(formatter->_property._ShortQuarterSymbols);
288 CFReleaseIfNotNull(formatter->_property._StandaloneQuarterSymbols);
289 CFReleaseIfNotNull(formatter->_property._ShortStandaloneQuarterSymbols);
290 CFReleaseIfNotNull(formatter->_property._AMSymbol);
291 CFReleaseIfNotNull(formatter->_property._PMSymbol);
292 CFReleaseIfNotNull(formatter->_property._AmbiguousYearStrategy);
293 CFReleaseIfNotNull(formatter->_property._UsesCharacterDirection);
294 CFReleaseIfNotNull(formatter->_property._CustomEraSymbols);
295 CFReleaseIfNotNull(formatter->_property._CustomMonthSymbols);
296 CFReleaseIfNotNull(formatter->_property._CustomShortMonthSymbols);
297 CFReleaseIfNotNull(formatter->_property._CustomWeekdaySymbols);
298 CFReleaseIfNotNull(formatter->_property._CustomShortWeekdaySymbols);
299 CFReleaseIfNotNull(formatter->_property._CustomLongEraSymbols);
300 CFReleaseIfNotNull(formatter->_property._CustomVeryShortMonthSymbols);
301 CFReleaseIfNotNull(formatter->_property._CustomVeryShortWeekdaySymbols);
302 CFReleaseIfNotNull(formatter->_property._CustomStandaloneMonthSymbols);
303 CFReleaseIfNotNull(formatter->_property._CustomShortStandaloneMonthSymbols);
304 CFReleaseIfNotNull(formatter->_property._CustomVeryShortStandaloneMonthSymbols);
305 CFReleaseIfNotNull(formatter->_property._CustomStandaloneWeekdaySymbols);
306 CFReleaseIfNotNull(formatter->_property._CustomShortStandaloneWeekdaySymbols);
307 CFReleaseIfNotNull(formatter->_property._CustomVeryShortStandaloneWeekdaySymbols);
308 CFReleaseIfNotNull(formatter->_property._CustomQuarterSymbols);
309 CFReleaseIfNotNull(formatter->_property._CustomShortQuarterSymbols);
310 CFReleaseIfNotNull(formatter->_property._CustomShortStandaloneQuarterSymbols);
311 CFReleaseIfNotNull(formatter->_property._CustomDateFormat);
312 CFReleaseIfNotNull(formatter->_property._CustomTimeFormat);
313 CFReleaseIfNotNull(formatter->_property._Custom24Hour);
314 CFReleaseIfNotNull(formatter->_property._Custom12Hour);
315 CFReleaseIfNotNull(formatter->_property._CustomAMSymbol);
316 CFReleaseIfNotNull(formatter->_property._CustomPMSymbol);
317 CFReleaseIfNotNull(formatter->_property._CustomFirstWeekday);
318 CFReleaseIfNotNull(formatter->_property._CustomMinDaysInFirstWeek);
319 }
320
321 static CFStringRef __CFDateFormatterCreateForcedString(CFDateFormatterRef formatter, CFStringRef inString);
322
323 static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value, Boolean directToICU);
324 static void __CFDateFormatterStoreSymbolPrefs(const void *key, const void *value, void *context);
325 extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale);
326 static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter);
327 static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter, bool doTime);
328 static void __CFDateFormatterSetSymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base, CFTypeRef value);
329
330 static void __ReadCustomUDateFormatProperty(CFDateFormatterRef formatter) {
331 CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale);
332 CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUDateTimeSymbols")) : NULL;
333 if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
334 CFDictionaryApplyFunction((CFDictionaryRef)metapref, __CFDateFormatterStoreSymbolPrefs, formatter);
335 }
336 metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleFirstWeekday")) : NULL;
337 if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
338 formatter->_property._CustomFirstWeekday = (CFDictionaryRef)CFRetain(metapref);
339 }
340 metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleMinDaysInFirstWeek")) : NULL;
341 if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
342 formatter->_property._CustomMinDaysInFirstWeek = (CFDictionaryRef)CFRetain(metapref);
343 }
344 metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce24HourTime")) : NULL;
345 if (NULL != metapref && CFGetTypeID(metapref) == CFBooleanGetTypeID()) {
346 formatter->_property._Custom24Hour = (CFBooleanRef)CFRetain(metapref);
347 }
348 metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce12HourTime")) : NULL;
349 if (NULL != metapref && CFGetTypeID(metapref) == CFBooleanGetTypeID()) {
350 formatter->_property._Custom12Hour = (CFBooleanRef)CFRetain(metapref);
351 }
352 metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUDateFormatStrings")) : NULL;
353 if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
354 CFStringRef key;
355 switch (formatter->_dateStyle) {
356 case kCFDateFormatterShortStyle: key = CFSTR("1"); break;
357 case kCFDateFormatterMediumStyle: key = CFSTR("2"); break;
358 case kCFDateFormatterLongStyle: key = CFSTR("3"); break;
359 case kCFDateFormatterFullStyle: key = CFSTR("4"); break;
360 default: key = CFSTR("0"); break;
361 }
362 CFStringRef dateFormat = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key);
363 if (NULL != dateFormat && CFGetTypeID(dateFormat) == CFStringGetTypeID()) {
364 formatter->_property._CustomDateFormat = (CFStringRef)CFRetain(dateFormat);
365 }
366 }
367 metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUTimeFormatStrings")) : NULL;
368 if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
369 CFStringRef key;
370 switch (formatter->_timeStyle) {
371 case kCFDateFormatterShortStyle: key = CFSTR("1"); break;
372 case kCFDateFormatterMediumStyle: key = CFSTR("2"); break;
373 case kCFDateFormatterLongStyle: key = CFSTR("3"); break;
374 case kCFDateFormatterFullStyle: key = CFSTR("4"); break;
375 default: key = CFSTR("0"); break;
376 }
377 CFStringRef timeFormat = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key);
378 if (NULL != timeFormat && CFGetTypeID(timeFormat) == CFStringGetTypeID()) {
379 formatter->_property._CustomTimeFormat = (CFStringRef)CFRetain(timeFormat);
380 }
381 }
382 }
383
384 static void __ApplyUDateFormatSymbol(CFDateFormatterRef formatter) {
385 UDateFormatSymbolType types[18] = {UDAT_ERAS,
386 UDAT_ERA_NAMES,
387 UDAT_MONTHS,
388 UDAT_SHORT_MONTHS,
389 UDAT_NARROW_MONTHS,
390 UDAT_STANDALONE_MONTHS,
391 UDAT_STANDALONE_SHORT_MONTHS,
392 UDAT_STANDALONE_NARROW_MONTHS,
393 UDAT_WEEKDAYS,
394 UDAT_SHORT_WEEKDAYS,
395 UDAT_NARROW_WEEKDAYS,
396 UDAT_STANDALONE_WEEKDAYS,
397 UDAT_STANDALONE_SHORT_WEEKDAYS,
398 UDAT_STANDALONE_NARROW_WEEKDAYS,
399 UDAT_QUARTERS,
400 UDAT_SHORT_QUARTERS,
401 UDAT_STANDALONE_QUARTERS,
402 UDAT_STANDALONE_SHORT_QUARTERS};
403 int offsets[18] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0};
404 CFArrayRef symbols[18] = {formatter->_property._EraSymbols,
405 formatter->_property._LongEraSymbols,
406 formatter->_property._MonthSymbols,
407 formatter->_property._ShortMonthSymbols,
408 formatter->_property._VeryShortMonthSymbols,
409 formatter->_property._StandaloneMonthSymbols,
410 formatter->_property._ShortStandaloneMonthSymbols,
411 formatter->_property._VeryShortStandaloneMonthSymbols,
412 formatter->_property._WeekdaySymbols,
413 formatter->_property._ShortWeekdaySymbols,
414 formatter->_property._VeryShortWeekdaySymbols,
415 formatter->_property._StandaloneWeekdaySymbols,
416 formatter->_property._ShortStandaloneWeekdaySymbols,
417 formatter->_property._VeryShortStandaloneWeekdaySymbols,
418 formatter->_property._QuarterSymbols,
419 formatter->_property._ShortQuarterSymbols,
420 formatter->_property._StandaloneQuarterSymbols,
421 formatter->_property._ShortStandaloneQuarterSymbols
422 };
423 CFArrayRef customSymbols[18] = {formatter->_property._CustomEraSymbols,
424 formatter->_property._CustomLongEraSymbols,
425 formatter->_property._CustomMonthSymbols,
426 formatter->_property._CustomShortMonthSymbols,
427 formatter->_property._CustomVeryShortMonthSymbols,
428 formatter->_property._CustomStandaloneMonthSymbols,
429 formatter->_property._CustomShortStandaloneMonthSymbols,
430 formatter->_property._CustomVeryShortStandaloneMonthSymbols,
431 formatter->_property._CustomWeekdaySymbols,
432 formatter->_property._CustomShortWeekdaySymbols,
433 formatter->_property._CustomVeryShortWeekdaySymbols,
434 formatter->_property._CustomStandaloneWeekdaySymbols,
435 formatter->_property._CustomShortStandaloneWeekdaySymbols,
436 formatter->_property._CustomVeryShortStandaloneWeekdaySymbols,
437 formatter->_property._CustomQuarterSymbols,
438 formatter->_property._CustomShortQuarterSymbols,
439 formatter->_property._CustomStandaloneQuarterSymbols,
440 formatter->_property._CustomShortStandaloneQuarterSymbols
441 };
442
443 for (CFIndex i = 0; i < 18; i++) {
444 if (symbols[i] != NULL) {
445 __CFDateFormatterSetSymbolsArray(formatter->_df, types[i], offsets[i], symbols[i]);
446 } else if (customSymbols[i] != NULL) {
447 __CFDateFormatterSetSymbolsArray(formatter->_df, types[i], offsets[i], customSymbols[i]);
448 }
449 }
450
451 CFStringRef ampm[2];
452 ampm[0] = NULL;
453 ampm[1] = NULL;
454
455 if (formatter->_property._AMSymbol != NULL) {
456 ampm[0] = formatter->_property._AMSymbol;
457 } else if (formatter->_property._CustomAMSymbol != NULL) {
458 ampm[0] = formatter->_property._CustomAMSymbol;
459 }
460 if (formatter->_property._PMSymbol != NULL) {
461 ampm[1] = formatter->_property._PMSymbol;
462 } else if (formatter->_property._CustomPMSymbol != NULL) {
463 ampm[1] = formatter->_property._CustomPMSymbol;
464 }
465 for (CFIndex i = 0; i < 2; i++) {
466 CFStringRef sym = ampm[i];
467 if (sym != NULL) {
468 CFIndex item_cnt = CFStringGetLength(sym);
469 STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt));
470 UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(sym);
471 if (NULL == item_ustr) {
472 item_cnt = __CFMin(BUFFER_SIZE, item_cnt);
473 CFStringGetCharacters(sym, CFRangeMake(0, item_cnt), (UniChar *)item_buffer);
474 item_ustr = item_buffer;
475 }
476 UErrorCode status = U_ZERO_ERROR;
477 __cficu_udat_setSymbols(formatter->_df, UDAT_AM_PMS, i, item_ustr, item_cnt, &status);
478 }
479 }
480 }
481
482 static void __SetCalendarProperties(CFDateFormatterRef df) {
483 CFStringRef calName = df->_property._CalendarName ? (df->_property._CalendarName) : NULL;
484 if (!calName) {
485 calName = (CFStringRef)CFLocaleGetValue(df->_locale, kCFLocaleCalendarIdentifierKey);
486 }
487 UErrorCode status = U_ZERO_ERROR;
488 const UCalendar *cal = __cficu_udat_getCalendar(df->_df);
489 UCalendar *new_cal = NULL;
490
491 if (df->_property._Calendar != NULL || df->_property._CalendarName != NULL) {
492 UCalendar *caltmp = __CFCalendarCreateUCalendar(NULL, CFLocaleGetIdentifier(df->_locale), df->_property._TimeZone);
493 if (caltmp) {
494 new_cal = caltmp;
495 }
496 }
497 if (new_cal == NULL) {
498 new_cal = __cficu_ucal_clone(cal, &status);
499 }
500
501 if (df->_property._IsLenient != NULL) {
502 status = U_ZERO_ERROR;
503 CFBooleanRef value = df->_property._IsLenient;
504 __cficu_ucal_setAttribute(new_cal, UCAL_LENIENT, (kCFBooleanTrue == value));
505 }
506 if (df->_property._TimeZone != NULL) {
507 status = U_ZERO_ERROR;
508 UChar ubuffer[BUFFER_SIZE];
509 CFStringRef tznam = CFTimeZoneGetName(df->_property._TimeZone);
510 CFIndex ucnt = CFStringGetLength(tznam);
511 if (BUFFER_SIZE < ucnt) ucnt = BUFFER_SIZE;
512 CFStringGetCharacters(tznam, CFRangeMake(0, ucnt), (UniChar *)ubuffer);
513 __cficu_ucal_setTimeZone(new_cal, ubuffer, ucnt, &status);
514 }
515 if (df->_property._GregorianStartDate != NULL) {
516 status = U_ZERO_ERROR;
517 CFAbsoluteTime at = CFDateGetAbsoluteTime((CFDateRef)df->_property._GregorianStartDate);
518 UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
519 __cficu_ucal_setGregorianChange(new_cal, udate, &status);
520 } else if (calName && CFEqual(calName, kCFCalendarIdentifierGregorian)) {
521 status = U_ZERO_ERROR;
522 UDate udate = __cficu_ucal_getGregorianChange(cal, &status);
523 CFAbsoluteTime at = U_SUCCESS(status) ? (udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970) : -13197600000.0; // Oct 15, 1582
524 udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
525 status = U_ZERO_ERROR;
526 __cficu_ucal_setGregorianChange(new_cal, udate, &status);
527 }
528 if (df->_property._Calendar != NULL) {
529 __cficu_ucal_setAttribute(new_cal, UCAL_FIRST_DAY_OF_WEEK, CFCalendarGetFirstWeekday((CFCalendarRef)df->_property._Calendar));
530 } else if (df->_property._CustomFirstWeekday != NULL) {
531 CFNumberRef firstWeekday = (CFNumberRef)CFDictionaryGetValue(df->_property._CustomFirstWeekday, calName);
532 if (NULL != firstWeekday && CFGetTypeID(firstWeekday) == CFNumberGetTypeID()) {
533 CFIndex wkdy;
534 if (CFNumberGetValue((CFNumberRef)firstWeekday, kCFNumberCFIndexType, &wkdy)) {
535 status = U_ZERO_ERROR;
536 __cficu_ucal_setAttribute(new_cal, UCAL_FIRST_DAY_OF_WEEK, wkdy);
537 }
538 }
539 }
540
541 if (df->_property._Calendar != NULL) {
542 __cficu_ucal_setAttribute(new_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, CFCalendarGetMinimumDaysInFirstWeek((CFCalendarRef)df->_property._Calendar));
543 } else if (df->_property._CustomMinDaysInFirstWeek != NULL) {
544 CFNumberRef minDays = (CFNumberRef)CFDictionaryGetValue(df->_property._CustomMinDaysInFirstWeek, calName);
545 if (NULL != minDays && CFGetTypeID(minDays) == CFNumberGetTypeID()) {
546 CFIndex mwd;
547 if (CFNumberGetValue((CFNumberRef)minDays, kCFNumberCFIndexType, &mwd)) {
548 status = U_ZERO_ERROR;
549 __cficu_ucal_setAttribute(new_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, mwd);
550 }
551 }
552 }
553 __cficu_udat_setCalendar(df->_df, new_cal);
554 __cficu_ucal_close(new_cal);
555 }
556
557 #define RESET_PROPERTY(C, K) \
558 if (df->_property. C) __CFDateFormatterSetProperty(df, K, df->_property. C, true);
559
560 static void __ResetUDateFormat(CFDateFormatterRef df, Boolean goingToHaveCustomFormat) {
561 if (df->_df) __cficu_udat_close(df->_df);
562 df->_df = NULL;
563
564 // uses _timeStyle, _dateStyle, _locale, _property._TimeZone; sets _df, _format, _defformat
565 char loc_buffer[BUFFER_SIZE];
566 loc_buffer[0] = 0;
567 CFStringRef tmpLocName = df->_locale ? CFLocaleGetIdentifier(df->_locale) : CFSTR("");
568 CFStringGetCString(tmpLocName, loc_buffer, BUFFER_SIZE, kCFStringEncodingASCII);
569
570 UChar tz_buffer[BUFFER_SIZE];
571 tz_buffer[0] = 0;
572 CFStringRef tmpTZName = df->_property._TimeZone ? CFTimeZoneGetName(df->_property._TimeZone) : CFSTR("GMT");
573 CFStringGetCharacters(tmpTZName, CFRangeMake(0, CFStringGetLength(tmpTZName)), (UniChar *)tz_buffer);
574
575 int32_t udstyle = 0, utstyle = 0; // effectively this makes UDAT_FULL the default for unknown dateStyle/timeStyle values
576 switch (df->_dateStyle) {
577 case kCFDateFormatterNoStyle: udstyle = UDAT_NONE; break;
578 case kCFDateFormatterShortStyle: udstyle = UDAT_SHORT; break;
579 case kCFDateFormatterMediumStyle: udstyle = UDAT_MEDIUM; break;
580 case kCFDateFormatterLongStyle: udstyle = UDAT_LONG; break;
581 case kCFDateFormatterFullStyle: udstyle = UDAT_FULL; break;
582 }
583 switch (df->_timeStyle) {
584 case kCFDateFormatterNoStyle: utstyle = UDAT_NONE; break;
585 case kCFDateFormatterShortStyle: utstyle = UDAT_SHORT; break;
586 case kCFDateFormatterMediumStyle: utstyle = UDAT_MEDIUM; break;
587 case kCFDateFormatterLongStyle: utstyle = UDAT_LONG; break;
588 case kCFDateFormatterFullStyle: utstyle = UDAT_FULL; break;
589 }
590 Boolean wantRelative = (NULL != df->_property._DoesRelativeDateFormatting && df->_property._DoesRelativeDateFormatting == kCFBooleanTrue);
591 Boolean hasFormat = (NULL != df->_property._HasCustomFormat && df->_property._HasCustomFormat == kCFBooleanTrue) || goingToHaveCustomFormat;
592 if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) {
593 udstyle |= UDAT_RELATIVE;
594 }
595
596 UErrorCode status = U_ZERO_ERROR;
597 UDateFormat *icudf = __cficu_udat_open((UDateFormatStyle)utstyle, (UDateFormatStyle)udstyle, loc_buffer, tz_buffer, CFStringGetLength(tmpTZName), NULL, 0, &status);
598 if (NULL == icudf || U_FAILURE(status)) {
599 return;
600 }
601 if (df->_property._IsLenient != NULL) {
602 __cficu_udat_setLenient(icudf, (kCFBooleanTrue == df->_property._IsLenient));
603 } else {
604 __cficu_udat_setLenient(icudf, 0);
605 }
606 if (kCFDateFormatterNoStyle == df->_dateStyle && kCFDateFormatterNoStyle == df->_timeStyle) {
607 if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) {
608 UErrorCode s = U_ZERO_ERROR;
609 __cficu_udat_applyPatternRelative(icudf, NULL, 0, NULL, 0, &s);
610 } else {
611 __cficu_udat_applyPattern(icudf, false, NULL, 0);
612 }
613 }
614 if (!wantRelative && df->_property._HasCustomFormat == kCFBooleanTrue) {
615 CFIndex cnt = CFStringGetLength(df->_format);
616 STACK_BUFFER_DECL(UChar, ubuffer, cnt);
617 const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)df->_format);
618 if (NULL == ustr) {
619 CFStringGetCharacters(df->_format, CFRangeMake(0, cnt), (UniChar *)ubuffer);
620 ustr = ubuffer;
621 }
622 __cficu_udat_applyPattern(icudf, false, ustr, cnt);
623 }
624
625 CFStringRef calident = (CFStringRef)CFLocaleGetValue(df->_locale, kCFLocaleCalendarIdentifierKey);
626 if (calident && CFEqual(calident, kCFCalendarIdentifierGregorian)) {
627 status = U_ZERO_ERROR;
628 __cficu_udat_set2DigitYearStart(icudf, -631152000000.0, &status); // 1950-01-01 00:00:00 GMT
629 }
630 df->_df = icudf;
631
632 __ReadCustomUDateFormatProperty(df);
633
634 __SetCalendarProperties(df);
635
636 if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) {
637 __substituteFormatStringFromPrefsDFRelative(df);
638 } else {
639 __substituteFormatStringFromPrefsDF(df, false);
640 __substituteFormatStringFromPrefsDF(df, true);
641 }
642
643 __ApplyUDateFormatSymbol(df);
644
645
646 if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) {
647 UChar dateBuffer[BUFFER_SIZE];
648 UChar timeBuffer[BUFFER_SIZE];
649 status = U_ZERO_ERROR;
650 CFIndex dateLen = __cficu_udat_toPatternRelativeDate(icudf, dateBuffer, BUFFER_SIZE, &status);
651 CFIndex timeLen = (utstyle != UDAT_NONE) ? __cficu_udat_toPatternRelativeTime(icudf, timeBuffer, BUFFER_SIZE, &status) : 0;
652 if (U_SUCCESS(status) && dateLen <= BUFFER_SIZE && timeLen <= BUFFER_SIZE) {
653 // We assume that the 12/24-hour forcing preferences only affect the Time component
654 CFStringRef newFormat = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)timeBuffer, timeLen);
655 CFStringRef formatString = __CFDateFormatterCreateForcedString(df, newFormat);
656 CFIndex cnt = CFStringGetLength(formatString);
657 CFAssert1(cnt <= BUFFER_SIZE, __kCFLogAssertion, "%s(): time format string too long", __PRETTY_FUNCTION__);
658 if (cnt <= BUFFER_SIZE) {
659 CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)timeBuffer);
660 timeLen = cnt;
661 status = U_ZERO_ERROR;
662 __cficu_udat_applyPatternRelative(icudf, dateBuffer, dateLen, timeBuffer, timeLen, &status);
663 // ignore error and proceed anyway, what else can be done?
664
665 UChar ubuffer[BUFFER_SIZE];
666 status = U_ZERO_ERROR;
667 int32_t ret = __cficu_udat_toPattern(icudf, false, ubuffer, BUFFER_SIZE, &status); // read out current pattern
668 if (U_SUCCESS(status) && ret <= BUFFER_SIZE) {
669 if (df->_format) CFRelease(df->_format);
670 df->_format = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ret);
671 }
672 }
673 CFRelease(formatString);
674 CFRelease(newFormat);
675 }
676 } else {
677 UChar ubuffer[BUFFER_SIZE];
678 status = U_ZERO_ERROR;
679 int32_t ret = __cficu_udat_toPattern(icudf, false, ubuffer, BUFFER_SIZE, &status);
680 if (U_SUCCESS(status) && ret <= BUFFER_SIZE) {
681 CFStringRef newFormat = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ret);
682 CFStringRef formatString = __CFDateFormatterCreateForcedString(df, newFormat);
683 CFIndex cnt = CFStringGetLength(formatString);
684 CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__);
685 if (df->_format != formatString && cnt <= 1024) {
686 STACK_BUFFER_DECL(UChar, ubuffer, cnt);
687 const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)formatString);
688 if (NULL == ustr) {
689 CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer);
690 ustr = ubuffer;
691 }
692 UErrorCode status = U_ZERO_ERROR;
693 // __cficu_udat_applyPattern(df->_df, false, ustr, cnt, &status);
694 __cficu_udat_applyPattern(df->_df, false, ustr, cnt);
695 if (U_SUCCESS(status)) {
696 if (df->_format) CFRelease(df->_format);
697 df->_format = (CFStringRef)CFStringCreateCopy(CFGetAllocator(df), formatString);
698 }
699 }
700 CFRelease(formatString);
701 CFRelease(newFormat);
702 }
703 }
704 if (df->_defformat) CFRelease(df->_defformat);
705 df->_defformat = df->_format ? (CFStringRef)CFRetain(df->_format) : NULL;
706
707 RESET_PROPERTY(_IsLenient, kCFDateFormatterIsLenientKey);
708 RESET_PROPERTY(_DoesRelativeDateFormatting, kCFDateFormatterDoesRelativeDateFormattingKey);
709 RESET_PROPERTY(_Calendar, kCFDateFormatterCalendarKey);
710 RESET_PROPERTY(_CalendarName, kCFDateFormatterCalendarIdentifierKey);
711 RESET_PROPERTY(_TimeZone, kCFDateFormatterTimeZoneKey);
712 RESET_PROPERTY(_TwoDigitStartDate, kCFDateFormatterTwoDigitStartDateKey);
713 RESET_PROPERTY(_DefaultDate, kCFDateFormatterDefaultDateKey);
714 RESET_PROPERTY(_GregorianStartDate, kCFDateFormatterGregorianStartDateKey);
715 RESET_PROPERTY(_AmbiguousYearStrategy, kCFDateFormatterAmbiguousYearStrategyKey);
716 RESET_PROPERTY(_UsesCharacterDirection, kCFDateFormatterUsesCharacterDirectionKey);
717 }
718
719 static CFTypeID __kCFDateFormatterTypeID = _kCFRuntimeNotATypeID;
720
721 static const CFRuntimeClass __CFDateFormatterClass = {
722 0,
723 "CFDateFormatter",
724 NULL, // init
725 NULL, // copy
726 __CFDateFormatterDeallocate,
727 NULL,
728 NULL,
729 NULL, //
730 __CFDateFormatterCopyDescription
731 };
732
733 static void __CFDateFormatterInitialize(void) {
734 __kCFDateFormatterTypeID = _CFRuntimeRegisterClass(&__CFDateFormatterClass);
735 }
736
737 CFTypeID CFDateFormatterGetTypeID(void) {
738 if (_kCFRuntimeNotATypeID == __kCFDateFormatterTypeID) __CFDateFormatterInitialize();
739 return __kCFDateFormatterTypeID;
740 }
741
742 CFDateFormatterRef CFDateFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFDateFormatterStyle dateStyle, CFDateFormatterStyle timeStyle) {
743 struct __CFDateFormatter *memory;
744 uint32_t size = sizeof(struct __CFDateFormatter) - sizeof(CFRuntimeBase);
745 if (allocator == NULL) allocator = __CFGetDefaultAllocator();
746 __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
747 if (locale) __CFGenericValidateType(locale, CFLocaleGetTypeID());
748 memory = (struct __CFDateFormatter *)_CFRuntimeCreateInstance(allocator, CFDateFormatterGetTypeID(), size, NULL);
749 if (NULL == memory) {
750 return NULL;
751 }
752 memory->_df = NULL;
753 memory->_locale = NULL;
754 memory->_format = NULL;
755 memory->_defformat = NULL;
756 memory->_dateStyle = dateStyle;
757 memory->_timeStyle = timeStyle;
758 memory->_property._IsLenient = NULL;
759 memory->_property._DoesRelativeDateFormatting = NULL;
760 memory->_property._HasCustomFormat = NULL;
761 memory->_property._TimeZone = NULL;
762 memory->_property._Calendar = NULL;
763 memory->_property._CalendarName = NULL;
764 memory->_property._TwoDigitStartDate = NULL;
765 memory->_property._DefaultDate = NULL;
766 memory->_property._GregorianStartDate = NULL;
767 memory->_property._EraSymbols = NULL;
768 memory->_property._LongEraSymbols = NULL;
769 memory->_property._MonthSymbols = NULL;
770 memory->_property._ShortMonthSymbols = NULL;
771 memory->_property._VeryShortMonthSymbols = NULL;
772 memory->_property._StandaloneMonthSymbols = NULL;
773 memory->_property._ShortStandaloneMonthSymbols = NULL;
774 memory->_property._VeryShortStandaloneMonthSymbols = NULL;
775 memory->_property._WeekdaySymbols = NULL;
776 memory->_property._ShortWeekdaySymbols = NULL;
777 memory->_property._VeryShortWeekdaySymbols = NULL;
778 memory->_property._StandaloneWeekdaySymbols = NULL;
779 memory->_property._ShortStandaloneWeekdaySymbols = NULL;
780 memory->_property._VeryShortStandaloneWeekdaySymbols = NULL;
781 memory->_property._QuarterSymbols = NULL;
782 memory->_property._ShortQuarterSymbols = NULL;
783 memory->_property._StandaloneQuarterSymbols = NULL;
784 memory->_property._ShortStandaloneQuarterSymbols = NULL;
785 memory->_property._AMSymbol = NULL;
786 memory->_property._PMSymbol = NULL;
787 memory->_property._AmbiguousYearStrategy = NULL;
788 memory->_property._UsesCharacterDirection = NULL;
789 memory->_property._CustomEraSymbols = NULL;
790 memory->_property._CustomMonthSymbols = NULL;
791 memory->_property._CustomShortMonthSymbols = NULL;
792 memory->_property._CustomWeekdaySymbols = NULL;
793 memory->_property._CustomShortWeekdaySymbols = NULL;
794 memory->_property._CustomLongEraSymbols = NULL;
795 memory->_property._CustomVeryShortMonthSymbols = NULL;
796 memory->_property._CustomVeryShortWeekdaySymbols = NULL;
797 memory->_property._CustomStandaloneMonthSymbols = NULL;
798 memory->_property._CustomShortStandaloneMonthSymbols = NULL;
799 memory->_property._CustomVeryShortStandaloneMonthSymbols = NULL;
800 memory->_property._CustomStandaloneWeekdaySymbols = NULL;
801 memory->_property._CustomShortStandaloneWeekdaySymbols = NULL;
802 memory->_property._CustomVeryShortStandaloneWeekdaySymbols = NULL;
803 memory->_property._CustomQuarterSymbols = NULL;
804 memory->_property._CustomShortQuarterSymbols = NULL;
805 memory->_property._CustomStandaloneQuarterSymbols = NULL;
806 memory->_property._CustomShortStandaloneQuarterSymbols = NULL;
807 memory->_property._CustomDateFormat = NULL;
808 memory->_property._CustomTimeFormat = NULL;
809 memory->_property._Custom24Hour = NULL;
810 memory->_property._Custom12Hour = NULL;
811 memory->_property._CustomAMSymbol = NULL;
812 memory->_property._CustomPMSymbol = NULL;
813 memory->_property._CustomFirstWeekday = NULL;
814 memory->_property._CustomMinDaysInFirstWeek = NULL;
815
816 switch (dateStyle) {
817 case kCFDateFormatterNoStyle:
818 case kCFDateFormatterShortStyle:
819 case kCFDateFormatterMediumStyle:
820 case kCFDateFormatterLongStyle:
821 case kCFDateFormatterFullStyle: break;
822 default:
823 CFAssert2(0, __kCFLogAssertion, "%s(): unknown date style %d", __PRETTY_FUNCTION__, dateStyle);
824 memory->_dateStyle = kCFDateFormatterMediumStyle;
825 break;
826 }
827 switch (timeStyle) {
828 case kCFDateFormatterNoStyle:
829 case kCFDateFormatterShortStyle:
830 case kCFDateFormatterMediumStyle:
831 case kCFDateFormatterLongStyle:
832 case kCFDateFormatterFullStyle: break;
833 default:
834 CFAssert2(0, __kCFLogAssertion, "%s(): unknown time style %d", __PRETTY_FUNCTION__, timeStyle);
835 memory->_timeStyle = kCFDateFormatterMediumStyle;
836 break;
837 }
838
839 memory->_locale = locale ? CFLocaleCreateCopy(allocator, locale) : (CFLocaleRef)CFRetain(CFLocaleGetSystem());
840 memory->_property._TimeZone = CFTimeZoneCopyDefault();
841
842 CFStringRef calident = (CFStringRef)CFLocaleGetValue(memory->_locale, kCFLocaleCalendarIdentifierKey);
843 if (calident && CFEqual(calident, kCFCalendarIdentifierGregorian)) {
844 memory->_property._TwoDigitStartDate = CFDateCreate(kCFAllocatorSystemDefault, -1609459200.0); // 1950-01-01 00:00:00 +0000
845 }
846
847 __ResetUDateFormat(memory, false);
848 if (!memory->_df) {
849 CFRelease(memory);
850 return NULL;
851 }
852 return (CFDateFormatterRef)memory;
853 }
854
855 static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter) {
856
857 CFIndex dateLen = -1;
858 UChar dateBuffer[BUFFER_SIZE];
859 if (kCFDateFormatterNoStyle != formatter->_dateStyle) {
860 if (formatter->_property._CustomDateFormat != NULL) {
861 dateLen = __CFMin(CFStringGetLength(formatter->_property._CustomDateFormat), BUFFER_SIZE);
862 CFStringGetCharacters(formatter->_property._CustomDateFormat, CFRangeMake(0, dateLen), (UniChar *)dateBuffer);
863 }
864 }
865 if (-1 == dateLen) {
866 UErrorCode status = U_ZERO_ERROR;
867 int32_t ret = __cficu_udat_toPatternRelativeDate(formatter->_df, dateBuffer, BUFFER_SIZE, &status);
868 if (!U_FAILURE(status)) {
869 dateLen = ret;
870 }
871 }
872
873 CFIndex timeLen = -1;
874 UChar timeBuffer[BUFFER_SIZE];
875 if (kCFDateFormatterNoStyle != formatter->_timeStyle) {
876 if (formatter->_property._CustomTimeFormat != NULL) {
877 timeLen = __CFMin(CFStringGetLength(formatter->_property._CustomTimeFormat), BUFFER_SIZE);
878 CFStringGetCharacters(formatter->_property._CustomTimeFormat, CFRangeMake(0, timeLen), (UniChar *)timeBuffer);
879 }
880 }
881 if (-1 == timeLen) {
882 UErrorCode status = U_ZERO_ERROR;
883 int32_t ret = __cficu_udat_toPatternRelativeTime(formatter->_df, timeBuffer, BUFFER_SIZE, &status);
884 if (!U_FAILURE(status)) {
885 timeLen = ret;
886 }
887 }
888
889 UErrorCode status = U_ZERO_ERROR;
890 __cficu_udat_applyPatternRelative(formatter->_df, (0 <= dateLen) ? dateBuffer : NULL, (0 <= dateLen) ? dateLen : 0, (0 <= timeLen) ? timeBuffer : NULL, (0 <= timeLen) ? timeLen : 0, &status);
891 }
892
893 static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter, bool doTime) {
894 CFIndex formatStyle = doTime ? formatter->_timeStyle : formatter->_dateStyle;
895 CFStringRef pref = doTime ? formatter->_property._CustomTimeFormat : formatter->_property._CustomDateFormat;
896 if (kCFDateFormatterNoStyle != formatStyle) {
897 if (NULL != pref) {
898 int32_t icustyle = UDAT_NONE;
899 switch (formatStyle) {
900 case kCFDateFormatterShortStyle: icustyle = UDAT_SHORT; break;
901 case kCFDateFormatterMediumStyle: icustyle = UDAT_MEDIUM; break;
902 case kCFDateFormatterLongStyle: icustyle = UDAT_LONG; break;
903 case kCFDateFormatterFullStyle: icustyle = UDAT_FULL; break;
904 }
905 CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale);
906 char buffer[BUFFER_SIZE];
907 const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII);
908 if (NULL == cstr) {
909 if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
910 }
911 UErrorCode status = U_ZERO_ERROR;
912 UDateFormat *df = __cficu_udat_open((UDateFormatStyle)(doTime ? icustyle : UDAT_NONE), (UDateFormatStyle)(doTime ? UDAT_NONE : icustyle), cstr, NULL, 0, NULL, 0, &status);
913 if (NULL != df) {
914 UChar ubuffer[BUFFER_SIZE];
915 status = U_ZERO_ERROR;
916 int32_t date_len = __cficu_udat_toPattern(df, false, ubuffer, BUFFER_SIZE, &status);
917 if (U_SUCCESS(status) && date_len <= BUFFER_SIZE) {
918 CFStringRef dateString = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)ubuffer, date_len);
919 status = U_ZERO_ERROR;
920 int32_t formatter_len = __cficu_udat_toPattern(formatter->_df, false, ubuffer, BUFFER_SIZE, &status);
921 if (U_SUCCESS(status) && formatter_len <= BUFFER_SIZE) {
922 CFMutableStringRef formatString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
923 CFStringAppendCharacters(formatString, (UniChar *)ubuffer, formatter_len);
924 // find dateString inside formatString, substitute the pref in that range
925 CFRange result;
926 if (CFStringFindWithOptions(formatString, dateString, CFRangeMake(0, formatter_len), 0, &result)) {
927 CFStringReplace(formatString, result, pref);
928 int32_t new_len = CFStringGetLength(formatString);
929 STACK_BUFFER_DECL(UChar, new_buffer, new_len);
930 const UChar *new_ustr = (UChar *)CFStringGetCharactersPtr(formatString);
931 if (NULL == new_ustr) {
932 CFStringGetCharacters(formatString, CFRangeMake(0, new_len), (UniChar *)new_buffer);
933 new_ustr = new_buffer;
934 }
935 status = U_ZERO_ERROR;
936 // __cficu_udat_applyPattern(formatter->_df, false, new_ustr, new_len, &status);
937 __cficu_udat_applyPattern(formatter->_df, false, new_ustr, new_len);
938 }
939 CFRelease(formatString);
940 }
941 CFRelease(dateString);
942 }
943 __cficu_udat_close(df);
944 }
945 }
946 }
947 }
948
949 static void __CFDateFormatterStoreSymbolPrefs(const void *key, const void *value, void *context) {
950 if (CFGetTypeID(key) == CFStringGetTypeID() && CFGetTypeID(value) == CFArrayGetTypeID()) {
951 CFDateFormatterRef formatter = (CFDateFormatterRef)context;
952 UDateFormatSymbolType sym = (UDateFormatSymbolType)CFStringGetIntValue((CFStringRef)key);
953 CFArrayRef array = (CFArrayRef)value;
954 CFIndex idx, cnt = CFArrayGetCount(array);
955 switch (sym) {
956 case UDAT_ERAS:
957 formatter->_property._CustomEraSymbols = (CFArrayRef)CFRetain(array);
958 break;
959 case UDAT_MONTHS:
960 formatter->_property._CustomMonthSymbols = (CFArrayRef)CFRetain(array);
961 break;
962 case UDAT_SHORT_MONTHS:
963 formatter->_property._CustomShortMonthSymbols = (CFArrayRef)CFRetain(array);
964 break;
965 case UDAT_WEEKDAYS:
966 formatter->_property._CustomWeekdaySymbols = (CFArrayRef)CFRetain(array);
967 break;
968 case UDAT_SHORT_WEEKDAYS:
969 formatter->_property._CustomShortWeekdaySymbols = (CFArrayRef)CFRetain(array);
970 break;
971 case UDAT_AM_PMS:
972 {
973 for (idx = 0; idx < cnt; idx++) {
974 CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx);
975 if (CFGetTypeID(item) != CFStringGetTypeID()) continue;
976 if (idx == 0) {
977 formatter->_property._CustomAMSymbol = (CFStringRef)CFRetain(item);
978 } else if (idx == 1) {
979 formatter->_property._CustomPMSymbol = (CFStringRef)CFRetain(item);
980 }
981 }
982 }
983 break;
984 case UDAT_ERA_NAMES:
985 formatter->_property._CustomLongEraSymbols = (CFArrayRef)CFRetain(array);
986 break;
987 case UDAT_NARROW_MONTHS:
988 formatter->_property._CustomVeryShortMonthSymbols = (CFArrayRef)CFRetain(array);
989 break;
990 case UDAT_NARROW_WEEKDAYS:
991 formatter->_property._CustomVeryShortWeekdaySymbols = (CFArrayRef)CFRetain(array);
992 break;
993 case UDAT_STANDALONE_MONTHS:
994 formatter->_property._CustomStandaloneMonthSymbols = (CFArrayRef)CFRetain(array);
995 break;
996 case UDAT_STANDALONE_SHORT_MONTHS:
997 formatter->_property._CustomShortStandaloneMonthSymbols = (CFArrayRef)CFRetain(array);
998 break;
999 case UDAT_STANDALONE_NARROW_MONTHS:
1000 formatter->_property._CustomVeryShortStandaloneMonthSymbols = (CFArrayRef)CFRetain(array);
1001 break;
1002 case UDAT_STANDALONE_WEEKDAYS:
1003 formatter->_property._CustomStandaloneWeekdaySymbols = (CFArrayRef)CFRetain(array);
1004 break;
1005 case UDAT_STANDALONE_SHORT_WEEKDAYS:
1006 formatter->_property._CustomShortStandaloneWeekdaySymbols = (CFArrayRef)CFRetain(array);
1007 break;
1008 case UDAT_STANDALONE_NARROW_WEEKDAYS:
1009 formatter->_property._CustomVeryShortStandaloneWeekdaySymbols = (CFArrayRef)CFRetain(array);
1010 break;
1011 case UDAT_QUARTERS:
1012 formatter->_property._CustomQuarterSymbols = (CFArrayRef)CFRetain(array);
1013 break;
1014 case UDAT_SHORT_QUARTERS:
1015 formatter->_property._CustomShortQuarterSymbols = (CFArrayRef)CFRetain(array);
1016 break;
1017 case UDAT_STANDALONE_QUARTERS:
1018 formatter->_property._CustomStandaloneQuarterSymbols = (CFArrayRef)CFRetain(array);
1019 break;
1020 case UDAT_STANDALONE_SHORT_QUARTERS:
1021 formatter->_property._CustomShortStandaloneQuarterSymbols = (CFArrayRef)CFRetain(array);
1022 break;
1023 default:
1024 break;
1025 }
1026 }
1027 }
1028
1029 static CFStringRef __CFDateFormatterCreateForcedTemplate(CFLocaleRef locale, CFStringRef inString) {
1030 if (!inString) return NULL;
1031
1032 Boolean doForce24 = false, doForce12 = false;
1033 CFDictionaryRef prefs = __CFLocaleGetPrefs(locale);
1034 CFPropertyListRef pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce24HourTime")) : NULL;
1035 if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) {
1036 doForce24 = CFBooleanGetValue((CFBooleanRef)pref);
1037 }
1038 pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce12HourTime")) : NULL;
1039 if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) {
1040 doForce12 = CFBooleanGetValue((CFBooleanRef)pref);
1041 }
1042 if (doForce24) doForce12 = false; // if both are set, Force24 wins, period
1043 if (!doForce24 && !doForce12) return (CFStringRef)CFRetain(inString);
1044
1045 CFMutableStringRef outString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
1046 CFIndex cnt = CFStringGetLength(inString);
1047 CFIndex lastSecond = -1, lastMinute = -1, firstHour = -1;
1048 Boolean isInQuote = false, hasA = false, had12Hour = false, had24Hour = false;
1049 for (CFIndex idx = 0; idx < cnt; idx++) {
1050 Boolean emit = true;
1051 UniChar ch = CFStringGetCharacterAtIndex(inString, idx);
1052 switch (ch) {
1053 case '\'': isInQuote = !isInQuote; break;
1054 case 'j': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); if (doForce24) ch = 'H'; else ch = 'h';} break;
1055 case 'h': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had12Hour = true; if (doForce24) ch = 'H';} break; // switch 12-hour to 24-hour
1056 case 'K': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had12Hour = true; if (doForce24) ch = 'k';} break; // switch 12-hour to 24-hour
1057 case 'H': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had24Hour = true; if (doForce12) ch = 'h';} break; // switch 24-hour to 12-hour
1058 case 'k': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had24Hour = true; if (doForce12) ch = 'K';} break; // switch 24-hour to 12-hour
1059 case 'm': if (!isInQuote) lastMinute = CFStringGetLength(outString); break;
1060 case 's': if (!isInQuote) lastSecond = CFStringGetLength(outString); break;
1061 case 'a': if (!isInQuote) {hasA = true; if (doForce24) emit = false;} break;
1062 break;
1063 }
1064 if (emit) CFStringAppendCharacters(outString, &ch, 1);
1065 }
1066
1067 return outString;
1068 }
1069
1070 /*
1071 Mapping H<->h and K<->k is not correct in all locales; Japanese 12 hour, for example, uses H<->k
1072 This gets an approximately correct replacement character for the locale and forcing direction in question
1073 <rdar://problem/14062096> [iCal] Incorrect time format is used for current time indicator line
1074 */
1075 static UChar __CFDateFormatterForcedHourCharacterForLocale(CFLocaleRef locale, Boolean doForce24, Boolean doForce12, Boolean *succeeded) {
1076 if (doForce24) doForce12 = false; // if both are set, Force24 wins, period
1077 if (!locale || (!doForce24 && !doForce12)) {
1078 *succeeded = false;
1079 return '\0';
1080 }
1081 CFStringRef localeName = locale ? CFLocaleGetIdentifier(locale) : CFSTR("");
1082 char buffer[BUFFER_SIZE] = {0};
1083 const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII);
1084 if (NULL == cstr) {
1085 if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
1086 }
1087
1088 __block UChar hourPatternChar = '\0';
1089
1090 useTemplatePatternGenerator(cstr, ^(UDateTimePatternGenerator *ptg) {
1091 UChar hourPattern[256] = {0};
1092 int32_t patternLen = -1;
1093 if (ptg) {
1094 UErrorCode errorCode = U_ZERO_ERROR;
1095 patternLen = __cficu_udatpg_getBestPattern(ptg, (const UChar *)(doForce12 ? "h" : "H"), 1, hourPattern, sizeof(hourPattern) / sizeof(UChar), &errorCode);
1096 if (errorCode != U_ZERO_ERROR) {
1097 memset(hourPattern, 0, sizeof(hourPattern)); //make sure there's nothing there if we failed
1098 patternLen = -1;
1099 }
1100 }
1101
1102 /*
1103 Blindly replacing HHHH with four copies of the entire udatpg_getBestPattern result is not going to work. Search for the first [hHkK] in the result and use just that
1104 */
1105 if (patternLen > 0) {
1106 for (CFIndex idx = 0; hourPattern[idx] != '\0' && idx < patternLen && idx < sizeof(hourPattern) / sizeof(UChar); idx++) {
1107 if (hourPattern[idx] == 'k' || hourPattern[idx] == 'K' || hourPattern[idx] == 'h' || hourPattern[idx] == 'H') {
1108 hourPatternChar = hourPattern[idx];
1109 break;
1110 }
1111 }
1112 }
1113 });
1114
1115 *succeeded = hourPatternChar != '\0';
1116 return hourPatternChar;
1117 }
1118
1119 #define FORCE_CHAR(replacement, oldType, newType) do { \
1120 if (!isInQuote) {\
1121 if (-1 == firstHour) {\
1122 firstHour = CFStringGetLength(outString);\
1123 }\
1124 had##oldType##Hour = true;\
1125 if (doForce##newType) {\
1126 ch = useSpecialHourChar ? hourPatternChar : replacement;\
1127 }\
1128 }\
1129 }while(0)
1130 #define FORCE_CHAR_12(replacement) FORCE_CHAR(replacement, 12, 24)
1131 #define FORCE_CHAR_24(replacement) FORCE_CHAR(replacement, 24, 12)
1132
1133 static CFStringRef __CFDateFormatterCreateForcedString(CFDateFormatterRef formatter, CFStringRef inString) {
1134 if (!inString) return NULL;
1135
1136 Boolean doForce24 = false, doForce12 = false;
1137 if (formatter->_property._Custom24Hour != NULL) {
1138 doForce24 = CFBooleanGetValue((CFBooleanRef)formatter->_property._Custom24Hour);
1139 }
1140 if (formatter->_property._Custom12Hour != NULL) {
1141 doForce12 = CFBooleanGetValue((CFBooleanRef)formatter->_property._Custom12Hour);
1142 }
1143 if (doForce24) doForce12 = false; // if both are set, Force24 wins, period
1144
1145 static CFCharacterSetRef hourCharacters;
1146 static dispatch_once_t onceToken;
1147 dispatch_once(&onceToken, ^{
1148 hourCharacters = CFCharacterSetCreateWithCharactersInString(kCFAllocatorSystemDefault, CFSTR("hHkK"));
1149 });
1150
1151 CFRange hourRange = CFRangeMake(kCFNotFound, 0);
1152 if (!CFStringFindCharacterFromSet(inString, hourCharacters, CFRangeMake(0, CFStringGetLength(inString)), 0, &hourRange) || hourRange.location == kCFNotFound) {
1153 doForce12 = false;
1154 doForce24 = false;
1155 }
1156
1157 if (!doForce24 && !doForce12) return (CFStringRef)CFRetain(inString);
1158
1159 Boolean useSpecialHourChar = false;
1160 UChar hourPatternChar = __CFDateFormatterForcedHourCharacterForLocale(formatter->_locale, doForce24, doForce12, &useSpecialHourChar);
1161
1162 CFMutableStringRef outString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
1163 CFIndex cnt = CFStringGetLength(inString);
1164 CFIndex lastSecond = -1, lastMinute = -1, firstHour = -1;
1165 Boolean isInQuote = false, hasA = false, had12Hour = false, had24Hour = false;
1166 for (CFIndex idx = 0; idx < cnt; idx++) {
1167 Boolean emit = true;
1168 UniChar ch = CFStringGetCharacterAtIndex(inString, idx);
1169 switch (ch) {
1170 case '\'': isInQuote = !isInQuote; break;
1171 case 'h': FORCE_CHAR_12('H'); break; // switch 12-hour to 24-hour
1172 case 'k': FORCE_CHAR_24('K'); break; // switch 24-hour to 12-hour
1173 case 'H': FORCE_CHAR_24('h'); break; // switch 24-hour to 12-hour
1174 case 'K': FORCE_CHAR_12('k'); break; // switch 12-hour to 24-hour
1175 case 'm': if (!isInQuote) lastMinute = CFStringGetLength(outString); break;
1176 case 's': if (!isInQuote) lastSecond = CFStringGetLength(outString); break;
1177 case 'a': if (!isInQuote) hasA = true;
1178 if (!isInQuote && doForce24) {
1179 // skip 'a' and one optional trailing space
1180 emit = false;
1181 if (idx + 1 < cnt && ' ' == CFStringGetCharacterAtIndex(inString, idx + 1)) idx++;
1182 }
1183 break;
1184 case ' ':
1185 if (!isInQuote && doForce24) {
1186 // if next character is 'a' AND we have seen the hour designator, skip space and 'a'
1187 if (idx + 1 < cnt && 'a' == CFStringGetCharacterAtIndex(inString, idx + 1) && -1 != firstHour) {
1188 emit = false;
1189 idx++;
1190 }
1191 }
1192 break;
1193 }
1194 if (emit) CFStringAppendCharacters(outString, &ch, 1);
1195 }
1196 if (doForce12 && !hasA && had24Hour) {
1197 CFStringRef locName = CFLocaleGetIdentifier(formatter->_locale);
1198 if (-1 != firstHour && (CFStringHasPrefix(locName, CFSTR("ko")) || CFEqual(locName, CFSTR("zh_SG")))) {
1199 CFStringInsert(outString, firstHour, CFSTR("a "));
1200 } else if (-1 != firstHour && (CFStringHasPrefix(locName, CFSTR("zh")) || CFStringHasPrefix(locName, CFSTR("ja")))) {
1201 CFStringInsert(outString, firstHour, CFSTR("a"));
1202 } else {
1203 CFIndex lastPos = (-1 != lastSecond) ? lastSecond : ((-1 != lastMinute) ? lastMinute : -1);
1204 if (-1 != lastPos) {
1205 cnt = CFStringGetLength(outString);
1206 lastPos++;
1207 UniChar ch = (lastPos < cnt) ? CFStringGetCharacterAtIndex(outString, lastPos) : 0;
1208 switch (ch) {
1209 case '\"': lastPos++; break;
1210 case '\'':;
1211 again:;
1212 do {
1213 lastPos++;
1214 ch = (lastPos < cnt) ? CFStringGetCharacterAtIndex(outString, lastPos) : 0;
1215 } while ('\'' != ch && '\0' != ch);
1216 if ('\'' == ch) lastPos++;
1217 ch = (lastPos < cnt) ? CFStringGetCharacterAtIndex(outString, lastPos) : 0;
1218 if ('\'' == ch) goto again;
1219 break;
1220 }
1221 CFStringInsert(outString, lastPos, CFSTR(" a"));
1222 }
1223 }
1224 }
1225 return outString;
1226 }
1227
1228 CFLocaleRef CFDateFormatterGetLocale(CFDateFormatterRef formatter) {
1229 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1230 return formatter->_locale;
1231 }
1232
1233 CFDateFormatterStyle CFDateFormatterGetDateStyle(CFDateFormatterRef formatter) {
1234 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1235 return formatter->_dateStyle;
1236 }
1237
1238 CFDateFormatterStyle CFDateFormatterGetTimeStyle(CFDateFormatterRef formatter) {
1239 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1240 return formatter->_timeStyle;
1241 }
1242
1243 CFStringRef CFDateFormatterGetFormat(CFDateFormatterRef formatter) {
1244 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1245 return formatter->_format;
1246 }
1247
1248 void CFDateFormatterSetFormat(CFDateFormatterRef formatter, CFStringRef formatString) {
1249 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1250 __CFGenericValidateType(formatString, CFStringGetTypeID());
1251 formatString = __CFDateFormatterCreateForcedString(formatter, formatString);
1252 CFIndex cnt = CFStringGetLength(formatString);
1253 CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__);
1254 if (formatter->_format != formatString && cnt <= 1024) {
1255 // When going from a situation where there is no custom format already,
1256 // and the "relative date formatting" property is set, we need to reset
1257 // the whole UDateFormat.
1258 if (formatter->_property._HasCustomFormat != kCFBooleanTrue && formatter->_property._DoesRelativeDateFormatting == kCFBooleanTrue) {
1259 __ResetUDateFormat(formatter, true);
1260 // the "true" results in: if you set a custom format string, you don't get relative date formatting
1261 }
1262 STACK_BUFFER_DECL(UChar, ubuffer, cnt);
1263 const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)formatString);
1264 if (NULL == ustr) {
1265 CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer);
1266 ustr = ubuffer;
1267 }
1268 UErrorCode status = U_ZERO_ERROR;
1269 // __cficu_udat_applyPattern(formatter->_df, false, ustr, cnt, &status);
1270 __cficu_udat_applyPattern(formatter->_df, false, ustr, cnt);
1271 if (U_SUCCESS(status)) {
1272 if (formatter->_format) CFRelease(formatter->_format);
1273 formatter->_format = (CFStringRef)CFStringCreateCopy(CFGetAllocator(formatter), formatString);
1274 formatter->_property._HasCustomFormat = kCFBooleanTrue;
1275 }
1276 }
1277 if (formatString) CFRelease(formatString);
1278 }
1279
1280 CFStringRef CFDateFormatterCreateStringWithDate(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFDateRef date) {
1281 if (allocator == NULL) allocator = __CFGetDefaultAllocator();
1282 __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
1283 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1284 __CFGenericValidateType(date, CFDateGetTypeID());
1285 return CFDateFormatterCreateStringWithAbsoluteTime(allocator, formatter, CFDateGetAbsoluteTime(date));
1286 }
1287
1288 CFStringRef CFDateFormatterCreateStringWithAbsoluteTime(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFAbsoluteTime at) {
1289 if (allocator == NULL) allocator = __CFGetDefaultAllocator();
1290 __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
1291 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1292 UChar *ustr = NULL, ubuffer[BUFFER_SIZE + 1];
1293 UErrorCode status = U_ZERO_ERROR;
1294 CFIndex used, cnt = BUFFER_SIZE;
1295 UDate ud = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0 + 0.5;
1296 used = __cficu_udat_format(formatter->_df, ud, ubuffer + 1, cnt, NULL, &status);
1297 if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) {
1298 cnt = used + 1 + 1; // leave room for RTL marker if needed
1299 ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0);
1300 status = U_ZERO_ERROR;
1301 used = __cficu_udat_format(formatter->_df, ud, ustr + 1, cnt, NULL, &status);
1302 }
1303 CFStringRef string = NULL;
1304 if (U_SUCCESS(status)) {
1305 UniChar *bufferToUse = ustr ? (UniChar *)ustr : (UniChar *)ubuffer;
1306 if (formatter->_property._UsesCharacterDirection == kCFBooleanTrue && CFLocaleGetLanguageCharacterDirection(CFLocaleGetIdentifier(formatter->_locale)) == kCFLocaleLanguageDirectionRightToLeft) {
1307 // Insert Unicode RTL marker
1308 bufferToUse[0] = 0x200F;
1309 used++;
1310 } else {
1311 // Move past direction marker
1312 bufferToUse++;
1313 }
1314 string = CFStringCreateWithCharacters(allocator, bufferToUse, used);
1315 }
1316 if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr);
1317 return string;
1318 }
1319
1320 static UDate __CFDateFormatterCorrectTimeWithTarget(UCalendar *calendar, UDate at, int32_t target, Boolean isEra, UErrorCode *status) {
1321 __cficu_ucal_setMillis(calendar, at, status);
1322 UCalendarDateFields field = isEra ? UCAL_ERA : UCAL_YEAR;
1323 __cficu_ucal_set(calendar, field, target);
1324 return __cficu_ucal_getMillis(calendar, status);
1325 }
1326
1327 static UDate __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(UCalendar *calendar, UDate at, CFIndex period, CFIndex pastYears, CFIndex futureYears, Boolean isEra, UErrorCode *status) {
1328 __cficu_ucal_setMillis(calendar, __cficu_ucal_getNow(), status);
1329 int32_t currYear = __cficu_ucal_get(calendar, UCAL_YEAR, status);
1330 UCalendarDateFields field = isEra ? UCAL_ERA : UCAL_YEAR;
1331 int32_t currEraOrCentury = __cficu_ucal_get(calendar, field, status);
1332 if (!isEra) {
1333 currYear %= 100;
1334 currEraOrCentury = currEraOrCentury / 100 * 100; // get century
1335 }
1336
1337 CFIndex futureMax = currYear + futureYears;
1338 CFIndex pastMin = currYear - pastYears;
1339
1340 CFRange currRange, futureRange, pastRange;
1341 currRange.location = futureRange.location = pastRange.location = kCFNotFound;
1342 currRange.length = futureRange.length = pastRange.length = 0;
1343 if (!isEra) {
1344 if (period < INT_MAX && futureMax >= period) {
1345 futureRange.location = 0;
1346 futureRange.length = futureMax - period + 1;
1347 }
1348 if (pastMin < 0) {
1349 pastRange.location = period + pastMin;
1350 pastRange.length = period - pastRange.location;
1351 }
1352 if (pastRange.location != kCFNotFound) {
1353 currRange.location = 0;
1354 } else {
1355 currRange.location = pastMin;
1356 }
1357 } else {
1358 if (period < INT_MAX && futureMax > period) {
1359 futureRange.location = 1,
1360 futureRange.length = futureMax - period;
1361 }
1362 if (pastMin <= 0) {
1363 pastRange.location = period + pastMin;
1364 pastRange.length = period - pastRange.location + 1;
1365 }
1366 if (pastRange.location != kCFNotFound) {
1367 currRange.location = 1;
1368 } else {
1369 currRange.location = pastMin;
1370 }
1371
1372 }
1373 currRange.length = period - pastRange.length - futureRange.length;
1374
1375 __cficu_ucal_setMillis(calendar, at, status);
1376 int32_t atYear = __cficu_ucal_get(calendar, UCAL_YEAR, status);
1377 if (!isEra) {
1378 atYear %= 100;
1379 currEraOrCentury += atYear;
1380 }
1381
1382 int32_t offset = 0; // current era or century
1383 if (pastRange.location != kCFNotFound && atYear >= pastRange.location && atYear - pastRange.location + 1 <= pastRange.length) {
1384 offset = -1; // past era or century
1385 } else if (futureRange.location != kCFNotFound && atYear >= futureRange.location && atYear - futureRange.location + 1 <= futureRange.length) {
1386 offset = 1; // next era or century
1387 }
1388 if (!isEra) offset *= 100;
1389 return __CFDateFormatterCorrectTimeWithTarget(calendar, at, currEraOrCentury+offset, isEra, status);
1390 }
1391
1392 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
1393 static int32_t __CFDateFormatterGetMaxYearGivenJapaneseEra(UCalendar *calendar, int32_t era, UErrorCode *status) {
1394 int32_t years = 0;
1395 __cficu_ucal_clear(calendar);
1396 __cficu_ucal_set(calendar, UCAL_ERA, era+1);
1397 UDate target = __cficu_ucal_getMillis(calendar, status);
1398 __cficu_ucal_set(calendar, UCAL_ERA, era);
1399 years = __cficu_ucal_getFieldDifference(calendar, target, UCAL_YEAR, status);
1400 return years+1;
1401 }
1402 #endif
1403
1404 static Boolean __CFDateFormatterHandleAmbiguousYear(CFDateFormatterRef formatter, CFStringRef calendar_id, UDateFormat *df, UCalendar *cal, UDate *at, const UChar *ustr, CFIndex length, UErrorCode *status) {
1405 Boolean success = true;
1406 int64_t ambigStrat = kCFDateFormatterAmbiguousYearAssumeToNone;
1407 if (formatter->_property._AmbiguousYearStrategy) {
1408 CFNumberGetValue(formatter->_property._AmbiguousYearStrategy, kCFNumberSInt64Type, &ambigStrat);
1409 }
1410 if (calendar_id == kCFCalendarIdentifierChinese) {
1411 // we default to era 1 if era is missing, however, we cannot just test if the era is 1 becuase we may get era 2 or larger if the year in the string is greater than 60
1412 // now I just assume that the year will not be greater than 600 in the string
1413 if (__cficu_ucal_get(cal, UCAL_ERA, status) < 10) {
1414 switch (ambigStrat) {
1415 case kCFDateFormatterAmbiguousYearFailToParse:
1416 success = false;
1417 break;
1418 case kCFDateFormatterAmbiguousYearAssumeToCurrent: {
1419 __cficu_ucal_setMillis(cal, __cficu_ucal_getNow(), status);
1420 int32_t currEra = __cficu_ucal_get(cal, UCAL_ERA, status);
1421 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1422 break;
1423 }
1424 case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate:
1425 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 29, 30, true, status);
1426 break;
1427 case kCFDateFormatterAmbiguousYearAssumeToFuture:
1428 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 0, 59, true, status);
1429 break;
1430 case kCFDateFormatterAmbiguousYearAssumeToPast:
1431 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 59, 0, true, status);
1432 break;
1433 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture:
1434 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 10, 49, true, status);
1435 break;
1436 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast:
1437 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 49, 10, true, status);
1438 break;
1439 case kCFDateFormatterAmbiguousYearAssumeToNone:
1440 default:
1441 break; // do nothing
1442 }
1443 }
1444 } else if (calendar_id == kCFCalendarIdentifierJapanese) { // ??? need more work
1445 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
1446 __cficu_ucal_clear(cal);
1447 __cficu_ucal_set(cal, UCAL_ERA, 1);
1448 __cficu_udat_parseCalendar(df, cal, ustr, length, NULL, status);
1449 UDate test = __cficu_ucal_getMillis(cal, status);
1450 if (test != *at) { // missing era
1451 __cficu_ucal_setMillis(cal, *at, status);
1452 int32_t givenYear = __cficu_ucal_get(cal, UCAL_YEAR, status);
1453 __cficu_ucal_setMillis(cal, __cficu_ucal_getNow(), status);
1454 int32_t currYear = __cficu_ucal_get(cal, UCAL_YEAR, status);
1455 int32_t currEra = __cficu_ucal_get(cal, UCAL_ERA, status);
1456 switch (ambigStrat) {
1457 case kCFDateFormatterAmbiguousYearFailToParse:
1458 success = false;
1459 break;
1460 case kCFDateFormatterAmbiguousYearAssumeToCurrent:
1461 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1462 break;
1463 case kCFDateFormatterAmbiguousYearAssumeToFuture:
1464 if (givenYear < currYear) { // we only consider current or the future
1465 success = false;
1466 } else { // current era
1467 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1468 }
1469 break;
1470 case kCFDateFormatterAmbiguousYearAssumeToPast:
1471 if (givenYear > currYear) { // past era
1472 success = false;
1473 // we find the closest era that has the given year
1474 // if no era has such given year, we fail the parse
1475 for (CFIndex era = currEra-1; era >= 234; era--) { // Showa era (234) is the longest era
1476 int32_t years = __CFDateFormatterGetMaxYearGivenJapaneseEra(cal, era, status);
1477 if (givenYear > years) {
1478 continue;
1479 }
1480 success = true;
1481 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, era, true, status);
1482 break;
1483 }
1484 } else { // current era
1485 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1486 }
1487 break;
1488 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture:
1489 if (givenYear < currYear - 10) { // we allow 10 years to the past
1490 success = false;
1491 } else {
1492 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1493 }
1494 break;
1495 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast:
1496 if (givenYear > currYear + 10) {
1497 success = false;
1498 // we find the closest era that has the given year
1499 // if no era has such given year, we fail the parse
1500 for (CFIndex era = currEra-1; era >= 234; era--) { // Showa era (234) is the longest era
1501 int32_t years = __CFDateFormatterGetMaxYearGivenJapaneseEra(cal, era, status);
1502 if (givenYear > years) {
1503 continue;
1504 }
1505 success = true;
1506 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, era, true, status);
1507 break;
1508 }
1509 } else { // current era
1510 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1511 }
1512 break;
1513 case kCFDateFormatterAmbiguousYearAssumeToNone:
1514 default:
1515 break; // do nothing
1516 }
1517 }
1518 #else
1519 success = false;
1520 #endif
1521 } else { // calenders other than chinese and japanese
1522 int32_t parsedYear = __cficu_ucal_get(cal, UCAL_YEAR, status);
1523 if (parsedYear >= 12000 && parsedYear <= 12099) { // most likely that the parsed string had a 2-digits year
1524 if (formatter->_property._TwoDigitStartDate != NULL) {
1525 UCalendar *tempCal = __cficu_ucal_clone(cal, status);
1526 __cficu_ucal_clear(tempCal);
1527 CFAbsoluteTime twoDigitAt = CFDateGetAbsoluteTime(formatter->_property._TwoDigitStartDate);
1528 UDate targetUdate = (twoDigitAt + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
1529 __cficu_ucal_setMillis(tempCal, targetUdate, status);
1530 int targetYear = __cficu_ucal_get(tempCal, UCAL_YEAR, status);
1531 parsedYear -= 12000;
1532 int targetYearM100 = targetYear % 100;
1533 if (targetYearM100 < parsedYear) {
1534 parsedYear = ((targetYear / 100) * 100) + parsedYear;
1535 } else if (parsedYear < targetYearM100) {
1536 parsedYear = ((targetYear / 100) * 100) + 100 + parsedYear;
1537 } else {
1538 __cficu_ucal_set(cal, UCAL_YEAR, targetYear);
1539 UDate parseUdate = __cficu_ucal_getMillis(cal, status);
1540 if (parseUdate >= targetUdate) {
1541 parsedYear = targetYear;
1542 } else {
1543 parsedYear = targetYear + 100;
1544 }
1545 }
1546 __cficu_ucal_close(tempCal);
1547 __cficu_ucal_set(cal, UCAL_YEAR, parsedYear);
1548 *at = __cficu_ucal_getMillis(cal, status);
1549 } else {
1550 switch (ambigStrat) {
1551 case kCFDateFormatterAmbiguousYearFailToParse:
1552 success = false;
1553 break;
1554 case kCFDateFormatterAmbiguousYearAssumeToCurrent:
1555 {
1556 // we can modify cal here because cal is just a temp cal from the caller
1557 __cficu_ucal_setMillis(cal, __cficu_ucal_getNow(), status);
1558 int32_t currYear = __cficu_ucal_get(cal, UCAL_YEAR, status);
1559 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, (currYear / 100 * 100) + parsedYear % 100, false, status);
1560 }
1561 break;
1562 case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate:
1563 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 50, 49, false, status);
1564 break;
1565 case kCFDateFormatterAmbiguousYearAssumeToFuture:
1566 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 0, 99, false, status);
1567 break;
1568 case kCFDateFormatterAmbiguousYearAssumeToPast:
1569 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 99, 0, false, status);
1570 break;
1571 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture:
1572 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 9, 90, false, status);
1573 break;
1574 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast:
1575 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 90, 9, false, status);
1576 break;
1577 case kCFDateFormatterAmbiguousYearAssumeToNone:
1578 default:
1579 if (calendar_id == kCFCalendarIdentifierGregorian) { // historical default behavior of 1950 - 2049
1580 int32_t twoDigits = parsedYear % 100;
1581 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, ((twoDigits < 50) ? 2000 : 1900) + twoDigits, false, status);
1582 }
1583 break; // do nothing
1584 }
1585 }
1586
1587 }
1588 }
1589 return success;
1590 }
1591
1592 CFDateRef CFDateFormatterCreateDateFromString(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep) {
1593 if (allocator == NULL) allocator = __CFGetDefaultAllocator();
1594 __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
1595 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1596 __CFGenericValidateType(string, CFStringGetTypeID());
1597 CFAbsoluteTime at;
1598 if (CFDateFormatterGetAbsoluteTimeFromString(formatter, string, rangep, &at)) {
1599 return CFDateCreate(allocator, at);
1600 }
1601 return NULL;
1602 }
1603
1604 Boolean CFDateFormatterGetAbsoluteTimeFromString(CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep, CFAbsoluteTime *atp) {
1605 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1606 __CFGenericValidateType(string, CFStringGetTypeID());
1607 CFRange range = {0, 0};
1608 if (rangep) {
1609 range = *rangep;
1610 } else {
1611 range.length = CFStringGetLength(string);
1612 }
1613 if (1024 < range.length) range.length = 1024;
1614 const UChar *ustr = (UChar *)CFStringGetCharactersPtr(string);
1615 STACK_BUFFER_DECL(UChar, ubuffer, (NULL == ustr) ? range.length : 1);
1616 if (NULL == ustr) {
1617 CFStringGetCharacters(string, range, (UniChar *)ubuffer);
1618 ustr = ubuffer;
1619 } else {
1620 ustr += range.location;
1621 }
1622 UDate udate;
1623 int32_t dpos = 0;
1624 UErrorCode status = U_ZERO_ERROR;
1625 UDateFormat *df2 = __cficu_udat_clone(formatter->_df, &status);
1626 const UCalendar *ucal2 = __cficu_udat_getCalendar(df2);
1627 UCalendar *cal2 = __cficu_ucal_clone(ucal2, &status);
1628 CFStringRef calendar_id = (CFStringRef) CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarIdentifierKey);
1629 if (calendar_id != kCFCalendarIdentifierChinese && calendar_id != kCFCalendarIdentifierJapanese) {
1630 __cficu_ucal_clear(cal2);
1631 // set both year, and 2DigitYearStart to year 12000
1632 __cficu_ucal_set(cal2, UCAL_YEAR, 12000);
1633 __cficu_udat_set2DigitYearStart(df2, 316516204800.0 * 1000.0, &status);
1634 } else if (calendar_id == kCFCalendarIdentifierChinese) {
1635 __cficu_ucal_clear(cal2);
1636 __cficu_ucal_set(cal2, UCAL_ERA, 1); // default to era 1 if no era info in the string for chinese
1637 } else if (calendar_id == kCFCalendarIdentifierJapanese) { // default to the current era
1638 __cficu_ucal_setMillis(cal2, __cficu_ucal_getNow(), &status);
1639 int32_t currEra = __cficu_ucal_get(cal2, UCAL_ERA, &status);
1640 __cficu_ucal_clear(cal2);
1641 __cficu_ucal_set(cal2, UCAL_ERA, currEra);
1642 }
1643 if (formatter->_property._DefaultDate) {
1644 CFAbsoluteTime at = CFDateGetAbsoluteTime(formatter->_property._DefaultDate);
1645 udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
1646 __cficu_ucal_setMillis(cal2, udate, &status);
1647 }
1648 __cficu_udat_parseCalendar(df2, cal2, ustr, range.length, &dpos, &status);
1649 udate = __cficu_ucal_getMillis(cal2, &status);
1650 if (rangep) rangep->length = dpos;
1651 Boolean success = false;
1652 // first status check is for parsing and the second status check is for the work done inside __CFDateFormatterHandleAmbiguousYear()
1653 if (!U_FAILURE(status) && (__CFDateFormatterHandleAmbiguousYear(formatter, calendar_id, df2, cal2, &udate, ustr, range.length, &status)) && !U_FAILURE(status)) {
1654 if (atp) {
1655 *atp = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
1656 }
1657 success = true;
1658 }
1659 CFRelease(calendar_id);
1660 __cficu_udat_close(df2);
1661 __cficu_ucal_close(cal2);
1662 return success;
1663 }
1664
1665 static void __CFDateFormatterSetSymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base, CFTypeRef value) {
1666 UErrorCode status = U_ZERO_ERROR;
1667 __CFGenericValidateType(value, CFArrayGetTypeID());
1668 CFArrayRef array = (CFArrayRef)value;
1669 CFIndex idx, cnt = CFArrayGetCount(array);
1670 for (idx = 0; idx < cnt; idx++) {
1671 CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx);
1672 __CFGenericValidateType(item, CFStringGetTypeID());
1673 CFIndex item_cnt = CFStringGetLength(item);
1674 STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt));
1675 UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item);
1676 if (NULL == item_ustr) {
1677 item_cnt = __CFMin(BUFFER_SIZE, item_cnt);
1678 CFStringGetCharacters(item, CFRangeMake(0, item_cnt), (UniChar *)item_buffer);
1679 item_ustr = item_buffer;
1680 }
1681 status = U_ZERO_ERROR;
1682 __cficu_udat_setSymbols(icudf, (UDateFormatSymbolType)icucode, idx + index_base, item_ustr, item_cnt, &status);
1683 }
1684 }
1685
1686 static CFArrayRef __CFDateFormatterGetSymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base) {
1687 UErrorCode status = U_ZERO_ERROR;
1688 CFIndex idx, cnt = __cficu_udat_countSymbols(icudf, (UDateFormatSymbolType)icucode);
1689 if (cnt <= index_base) return CFArrayCreate(kCFAllocatorSystemDefault, NULL, 0, &kCFTypeArrayCallBacks);
1690 cnt = cnt - index_base;
1691 STACK_BUFFER_DECL(CFStringRef, strings, cnt);
1692 for (idx = 0; idx < cnt; idx++) {
1693 UChar ubuffer[BUFFER_SIZE];
1694 CFStringRef str = NULL;
1695 status = U_ZERO_ERROR;
1696 CFIndex ucnt = __cficu_udat_getSymbols(icudf, (UDateFormatSymbolType)icucode, idx + index_base, ubuffer, BUFFER_SIZE, &status);
1697 if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1698 str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ucnt);
1699 }
1700 strings[idx] = !str ? (CFStringRef)CFRetain(CFSTR("<error>")) : str;
1701 }
1702 CFArrayRef array = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)strings, cnt, &kCFTypeArrayCallBacks);
1703 while (cnt--) {
1704 CFRelease(strings[cnt]);
1705 }
1706 return array;
1707 }
1708
1709 #define SET_SYMBOLS_ARRAY(A, B, C) \
1710 if (!directToICU) { \
1711 oldProperty = formatter->_property. C; \
1712 formatter->_property. C = NULL; \
1713 } \
1714 __CFDateFormatterSetSymbolsArray(formatter->_df, A, B, value); \
1715 if (!directToICU) { \
1716 formatter->_property. C = __CFDateFormatterGetSymbolsArray(formatter->_df, A, B); \
1717 }
1718
1719 static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value, Boolean directToICU) {
1720 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1721 __CFGenericValidateType(key, CFStringGetTypeID());
1722 CFTypeRef oldProperty = NULL;
1723 UErrorCode status = U_ZERO_ERROR;
1724
1725 if (kCFDateFormatterIsLenientKey == key) {
1726 if (!directToICU) {
1727 oldProperty = formatter->_property. _IsLenient;
1728 formatter->_property. _IsLenient = NULL;
1729 }
1730 __CFGenericValidateType(value, CFBooleanGetTypeID());
1731 if (!directToICU) {
1732 formatter->_property. _IsLenient = value ? (CFBooleanRef)CFRetain(value) : NULL;
1733 __ResetUDateFormat(formatter, false);
1734 }
1735 } else if (kCFDateFormatterDoesRelativeDateFormattingKey == key) {
1736 if (!directToICU) {
1737 oldProperty = formatter->_property. _DoesRelativeDateFormatting;
1738 formatter->_property. _DoesRelativeDateFormatting = NULL;
1739 }
1740 __CFGenericValidateType(value, CFBooleanGetTypeID());
1741 if (!directToICU) {
1742 if (kCFBooleanTrue != value) value = kCFBooleanFalse;
1743 formatter->_property. _DoesRelativeDateFormatting = value ? (CFBooleanRef)CFRetain(value) : NULL;
1744 __ResetUDateFormat(formatter, false);
1745 }
1746 } else if (kCFDateFormatterCalendarKey == key) {
1747 if (!directToICU) {
1748 oldProperty = formatter->_property. _Calendar;
1749 formatter->_property. _Calendar = NULL;
1750 }
1751 __CFGenericValidateType(value, CFCalendarGetTypeID());
1752 CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale);
1753 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeName);
1754 CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components);
1755 CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifierKey, CFCalendarGetIdentifier((CFCalendarRef)value));
1756 localeName = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents);
1757 CFRelease(mcomponents);
1758 CFRelease(components);
1759 CFLocaleRef newLocale = CFLocaleCreate(CFGetAllocator(formatter->_locale), localeName);
1760 // at this point, we should be setting the preferences if any into this new locale
1761 CFRelease(localeName);
1762 CFRelease(formatter->_locale);
1763 formatter->_locale = newLocale;
1764 if (!directToICU) {
1765 formatter->_property. _Calendar = (CFCalendarRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarKey);
1766 __ResetUDateFormat(formatter, false);
1767 }
1768 } else if (kCFDateFormatterCalendarIdentifierKey == key) {
1769 if (!directToICU) {
1770 oldProperty = formatter->_property. _CalendarName;
1771 formatter->_property. _CalendarName = NULL;
1772 }
1773 __CFGenericValidateType(value, CFStringGetTypeID());
1774 CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale);
1775 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeName);
1776 CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components);
1777 CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifierKey, value);
1778 localeName = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents);
1779 CFRelease(mcomponents);
1780 CFRelease(components);
1781 CFLocaleRef newLocale = CFLocaleCreate(CFGetAllocator(formatter->_locale), localeName);
1782 // at this point, we should be setting the preferences if any into this new locale
1783 CFRelease(localeName);
1784 CFRelease(formatter->_locale);
1785 formatter->_locale = newLocale;
1786 if (!directToICU) {
1787 formatter->_property. _CalendarName = (CFStringRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarIdentifierKey);
1788 __ResetUDateFormat(formatter, false);
1789 }
1790 } else if (kCFDateFormatterTimeZoneKey == key) {
1791 if (formatter->_property. _TimeZone != value) {
1792 if (!directToICU) {
1793 oldProperty = formatter->_property. _TimeZone;
1794 formatter->_property. _TimeZone = NULL;
1795 }
1796 __CFGenericValidateType(value, CFTimeZoneGetTypeID());
1797 CFTimeZoneRef old = formatter->_property._TimeZone;
1798 formatter->_property._TimeZone = value ? (CFTimeZoneRef)CFRetain(value) : CFTimeZoneCopyDefault();
1799 if (old) CFRelease(old);
1800 if (!directToICU) {
1801 old = formatter->_property._TimeZone;
1802 formatter->_property. _TimeZone = (CFTimeZoneRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterTimeZoneKey);
1803 __ResetUDateFormat(formatter, false);
1804 if (old) CFRelease(old);
1805 }
1806 }
1807 } else if (kCFDateFormatterDefaultFormatKey == key) {
1808 // read-only attribute
1809 } else if (kCFDateFormatterTwoDigitStartDateKey == key) {
1810 if (!directToICU) {
1811 oldProperty = formatter->_property. _TwoDigitStartDate;
1812 formatter->_property. _TwoDigitStartDate = NULL;
1813 }
1814 __CFGenericValidateType(value, CFDateGetTypeID());
1815 if (!directToICU) {
1816 formatter->_property. _TwoDigitStartDate = value ? (CFDateRef)CFRetain(value) : NULL;
1817 }
1818 } else if (kCFDateFormatterDefaultDateKey == key) {
1819 if (!directToICU) {
1820 oldProperty = formatter->_property. _DefaultDate;
1821 formatter->_property. _DefaultDate = NULL;
1822 }
1823 __CFGenericValidateType(value, CFDateGetTypeID());
1824 if (!directToICU) {
1825 formatter->_property._DefaultDate = value ? (CFDateRef)CFRetain(value) : NULL;
1826 }
1827 } else if (kCFDateFormatterGregorianStartDateKey == key) {
1828 if (!directToICU) {
1829 oldProperty = formatter->_property. _GregorianStartDate;
1830 formatter->_property. _GregorianStartDate = NULL;
1831 }
1832 __CFGenericValidateType(value, CFDateGetTypeID());
1833 if (!directToICU) {
1834 formatter->_property. _GregorianStartDate = value ? (CFDateRef)CFRetain(value) : NULL;
1835 __ResetUDateFormat(formatter, false);
1836 }
1837 } else if (kCFDateFormatterEraSymbolsKey == key) {
1838 SET_SYMBOLS_ARRAY(UDAT_ERAS, 0, _EraSymbols)
1839 } else if (kCFDateFormatterLongEraSymbolsKey == key) {
1840 SET_SYMBOLS_ARRAY(UDAT_ERA_NAMES, 0, _LongEraSymbols)
1841 } else if (kCFDateFormatterMonthSymbolsKey == key) {
1842 SET_SYMBOLS_ARRAY(UDAT_MONTHS, 0, _MonthSymbols)
1843 } else if (kCFDateFormatterShortMonthSymbolsKey == key) {
1844 SET_SYMBOLS_ARRAY(UDAT_SHORT_MONTHS, 0, _ShortMonthSymbols)
1845 } else if (kCFDateFormatterVeryShortMonthSymbolsKey == key) {
1846 SET_SYMBOLS_ARRAY(UDAT_NARROW_MONTHS, 0, _VeryShortMonthSymbols)
1847 } else if (kCFDateFormatterStandaloneMonthSymbolsKey == key) {
1848 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_MONTHS, 0, _StandaloneMonthSymbols)
1849 } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey == key) {
1850 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_MONTHS, 0, _ShortStandaloneMonthSymbols)
1851 } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey == key) {
1852 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_MONTHS, 0, _VeryShortStandaloneMonthSymbols)
1853 } else if (kCFDateFormatterWeekdaySymbolsKey == key) {
1854 SET_SYMBOLS_ARRAY(UDAT_WEEKDAYS, 1, _WeekdaySymbols)
1855 } else if (kCFDateFormatterShortWeekdaySymbolsKey == key) {
1856 SET_SYMBOLS_ARRAY(UDAT_SHORT_WEEKDAYS, 1, _ShortWeekdaySymbols)
1857 } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey == key) {
1858 SET_SYMBOLS_ARRAY(UDAT_NARROW_WEEKDAYS, 1, _VeryShortWeekdaySymbols)
1859 } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey == key) {
1860 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_WEEKDAYS, 1, _StandaloneWeekdaySymbols)
1861 } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey == key) {
1862 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_WEEKDAYS, 1, _ShortStandaloneWeekdaySymbols)
1863 } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey == key) {
1864 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_WEEKDAYS, 1, _VeryShortStandaloneWeekdaySymbols)
1865 } else if (kCFDateFormatterQuarterSymbolsKey == key) {
1866 SET_SYMBOLS_ARRAY(UDAT_QUARTERS, 0, _QuarterSymbols)
1867 } else if (kCFDateFormatterShortQuarterSymbolsKey == key) {
1868 SET_SYMBOLS_ARRAY(UDAT_SHORT_QUARTERS, 0, _ShortQuarterSymbols)
1869 } else if (kCFDateFormatterStandaloneQuarterSymbolsKey == key) {
1870 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_QUARTERS, 0, _StandaloneQuarterSymbols)
1871 } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey == key) {
1872 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_QUARTERS, 0, _ShortStandaloneQuarterSymbols)
1873 } else if (kCFDateFormatterAMSymbolKey == key) {
1874 if (!directToICU) {
1875 oldProperty = formatter->_property. _AMSymbol;
1876 formatter->_property. _AMSymbol = NULL;
1877 }
1878 __CFGenericValidateType(value, CFStringGetTypeID());
1879 CFIndex item_cnt = CFStringGetLength((CFStringRef)value);
1880 STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt));
1881 UChar *item_ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)value);
1882 if (NULL == item_ustr) {
1883 item_cnt = __CFMin(BUFFER_SIZE, item_cnt);
1884 CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, item_cnt), (UniChar *)item_buffer);
1885 item_ustr = item_buffer;
1886 }
1887 __cficu_udat_setSymbols(formatter->_df, UDAT_AM_PMS, 0, item_ustr, item_cnt, &status);
1888 if (!directToICU) {
1889 formatter->_property. _AMSymbol = value ? (CFStringRef)CFStringCreateCopy(NULL, value) : NULL;
1890 }
1891 } else if (kCFDateFormatterPMSymbolKey == key) {
1892 if (!directToICU) {
1893 oldProperty = formatter->_property. _PMSymbol;
1894 formatter->_property. _PMSymbol = NULL;
1895 }
1896 __CFGenericValidateType(value, CFStringGetTypeID());
1897 CFIndex item_cnt = CFStringGetLength((CFStringRef)value);
1898 STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt));
1899 UChar *item_ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)value);
1900 if (NULL == item_ustr) {
1901 item_cnt = __CFMin(BUFFER_SIZE, item_cnt);
1902 CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, item_cnt), (UniChar *)item_buffer);
1903 item_ustr = item_buffer;
1904 }
1905 __cficu_udat_setSymbols(formatter->_df, UDAT_AM_PMS, 1, item_ustr, item_cnt, &status);
1906 if (!directToICU) {
1907 formatter->_property. _PMSymbol = value ? (CFStringRef)CFStringCreateCopy(NULL, value) : NULL;
1908 }
1909 } else if (kCFDateFormatterAmbiguousYearStrategyKey == key) {
1910 oldProperty = formatter->_property._AmbiguousYearStrategy;
1911 formatter->_property._AmbiguousYearStrategy = NULL;
1912 __CFGenericValidateType(value, CFNumberGetTypeID());
1913 formatter->_property._AmbiguousYearStrategy = (CFNumberRef)CFRetain(value);
1914 } else if (kCFDateFormatterUsesCharacterDirectionKey == key) {
1915 __CFGenericValidateType(value, CFBooleanGetTypeID());
1916 oldProperty = formatter->_property._UsesCharacterDirection;
1917 formatter->_property._UsesCharacterDirection = (CFBooleanRef)CFRetain(value);
1918 } else {
1919 CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key);
1920 }
1921 if (oldProperty) CFRelease(oldProperty);
1922 }
1923
1924 void CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value) {
1925 __CFDateFormatterSetProperty(formatter, key, value, false);
1926 }
1927
1928 CFTypeRef CFDateFormatterCopyProperty(CFDateFormatterRef formatter, CFStringRef key) {
1929 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1930 __CFGenericValidateType(key, CFStringGetTypeID());
1931 UErrorCode status = U_ZERO_ERROR;
1932 UChar ubuffer[BUFFER_SIZE];
1933
1934 if (kCFDateFormatterIsLenientKey == key) {
1935 if (formatter->_property._IsLenient) return CFRetain(formatter->_property._IsLenient);
1936 return CFRetain(__cficu_udat_isLenient(formatter->_df) ? kCFBooleanTrue : kCFBooleanFalse);
1937 } else if (kCFDateFormatterDoesRelativeDateFormattingKey == key) {
1938 if (formatter->_property._DoesRelativeDateFormatting) return CFRetain(formatter->_property._DoesRelativeDateFormatting);
1939 return CFRetain(kCFBooleanFalse);
1940 } else if (kCFDateFormatterCalendarKey == key) {
1941 if (formatter->_property._Calendar) return CFRetain(formatter->_property._Calendar);
1942 CFCalendarRef calendar = (CFCalendarRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendarKey);
1943 return calendar ? CFRetain(calendar) : NULL;
1944 } else if (kCFDateFormatterCalendarIdentifierKey == key) {
1945 if (formatter->_property._CalendarName) return CFRetain(formatter->_property._CalendarName);
1946 CFStringRef ident = (CFStringRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendarIdentifierKey);
1947 return ident ? CFRetain(ident) : NULL;
1948 } else if (kCFDateFormatterTimeZoneKey == key) {
1949 return formatter->_property._TimeZone ? CFRetain(formatter->_property._TimeZone) : NULL;
1950 } else if (kCFDateFormatterDefaultFormatKey == key) {
1951 return formatter->_defformat ? CFRetain(formatter->_defformat) : NULL;
1952 } else if (kCFDateFormatterTwoDigitStartDateKey == key) {
1953 return formatter->_property._TwoDigitStartDate ? CFRetain(formatter->_property._TwoDigitStartDate) : NULL;
1954 } else if (kCFDateFormatterDefaultDateKey == key) {
1955 return formatter->_property._DefaultDate ? CFRetain(formatter->_property._DefaultDate) : NULL;
1956 } else if (kCFDateFormatterGregorianStartDateKey == key) {
1957 if (formatter->_property._GregorianStartDate) return CFRetain(formatter->_property._GregorianStartDate);
1958 const UCalendar *cal = __cficu_udat_getCalendar(formatter->_df);
1959 UDate udate = __cficu_ucal_getGregorianChange(cal, &status);
1960 if (U_SUCCESS(status)) {
1961 CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
1962 return CFDateCreate(CFGetAllocator(formatter), at);
1963 }
1964 } else if (kCFDateFormatterEraSymbolsKey == key) {
1965 if (formatter->_property._EraSymbols) return CFRetain(formatter->_property._EraSymbols);
1966 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_ERAS, 0);
1967 } else if (kCFDateFormatterLongEraSymbolsKey == key) {
1968 if (formatter->_property._LongEraSymbols) return CFRetain(formatter->_property._LongEraSymbols);
1969 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_ERA_NAMES, 0);
1970 } else if (kCFDateFormatterMonthSymbolsKey == key) {
1971 if (formatter->_property._MonthSymbols) return CFRetain(formatter->_property._MonthSymbols);
1972 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_MONTHS, 0);
1973 } else if (kCFDateFormatterShortMonthSymbolsKey == key) {
1974 if (formatter->_property._ShortMonthSymbols) return CFRetain(formatter->_property._ShortMonthSymbols);
1975 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_SHORT_MONTHS, 0);
1976 } else if (kCFDateFormatterVeryShortMonthSymbolsKey == key) {
1977 if (formatter->_property._VeryShortMonthSymbols) return CFRetain(formatter->_property._VeryShortMonthSymbols);
1978 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_NARROW_MONTHS, 0);
1979 } else if (kCFDateFormatterStandaloneMonthSymbolsKey == key) {
1980 if (formatter->_property._StandaloneMonthSymbols) return CFRetain(formatter->_property._StandaloneMonthSymbols);
1981 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_MONTHS, 0);
1982 } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey == key) {
1983 if (formatter->_property._ShortStandaloneMonthSymbols) return CFRetain(formatter->_property._ShortStandaloneMonthSymbols);
1984 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_MONTHS, 0);
1985 } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey == key) {
1986 if (formatter->_property._VeryShortStandaloneMonthSymbols) return CFRetain(formatter->_property._VeryShortStandaloneMonthSymbols);
1987 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_NARROW_MONTHS, 0);
1988 } else if (kCFDateFormatterWeekdaySymbolsKey == key) {
1989 if (formatter->_property._WeekdaySymbols) return CFRetain(formatter->_property._WeekdaySymbols);
1990 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_WEEKDAYS, 1);
1991 } else if (kCFDateFormatterShortWeekdaySymbolsKey == key) {
1992 if (formatter->_property._ShortWeekdaySymbols) return CFRetain(formatter->_property._ShortWeekdaySymbols);
1993 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_SHORT_WEEKDAYS, 1);
1994 } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey == key) {
1995 if (formatter->_property._VeryShortWeekdaySymbols) return CFRetain(formatter->_property._VeryShortWeekdaySymbols);
1996 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_NARROW_WEEKDAYS, 1);
1997 } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey == key) {
1998 if (formatter->_property._StandaloneWeekdaySymbols) return CFRetain(formatter->_property._StandaloneWeekdaySymbols);
1999 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_WEEKDAYS, 1);
2000 } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey == key) {
2001 if (formatter->_property._ShortStandaloneWeekdaySymbols) return CFRetain(formatter->_property._ShortStandaloneWeekdaySymbols);
2002 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_WEEKDAYS, 1);
2003 } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey == key) {
2004 if (formatter->_property._VeryShortStandaloneWeekdaySymbols) return CFRetain(formatter->_property._VeryShortStandaloneWeekdaySymbols);
2005 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_NARROW_WEEKDAYS, 1);
2006 } else if (kCFDateFormatterQuarterSymbolsKey == key) {
2007 if (formatter->_property._QuarterSymbols) return CFRetain(formatter->_property._QuarterSymbols);
2008 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_QUARTERS, 0);
2009 } else if (kCFDateFormatterShortQuarterSymbolsKey == key) {
2010 if (formatter->_property._ShortQuarterSymbols) return CFRetain(formatter->_property._ShortQuarterSymbols);
2011 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_SHORT_QUARTERS, 0);
2012 } else if (kCFDateFormatterStandaloneQuarterSymbolsKey == key) {
2013 if (formatter->_property._StandaloneQuarterSymbols) return CFRetain(formatter->_property._StandaloneQuarterSymbols);
2014 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_QUARTERS, 0);
2015 } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey == key) {
2016 if (formatter->_property._ShortStandaloneQuarterSymbols) return CFRetain(formatter->_property._ShortStandaloneQuarterSymbols);
2017 return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_QUARTERS, 0);
2018 } else if (kCFDateFormatterAMSymbolKey == key) {
2019 if (formatter->_property._AMSymbol) return CFRetain(formatter->_property._AMSymbol);
2020 CFIndex cnt = __cficu_udat_countSymbols(formatter->_df, UDAT_AM_PMS);
2021 if (2 <= cnt) {
2022 CFIndex ucnt = __cficu_udat_getSymbols(formatter->_df, UDAT_AM_PMS, 0, ubuffer, BUFFER_SIZE, &status);
2023 if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
2024 return CFStringCreateWithCharacters(CFGetAllocator(formatter), (UniChar *)ubuffer, ucnt);
2025 }
2026 }
2027 } else if (kCFDateFormatterPMSymbolKey == key) {
2028 if (formatter->_property._PMSymbol) return CFRetain(formatter->_property._PMSymbol);
2029 CFIndex cnt = __cficu_udat_countSymbols(formatter->_df, UDAT_AM_PMS);
2030 if (2 <= cnt) {
2031 CFIndex ucnt = __cficu_udat_getSymbols(formatter->_df, UDAT_AM_PMS, 1, ubuffer, BUFFER_SIZE, &status);
2032 if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
2033 return CFStringCreateWithCharacters(CFGetAllocator(formatter), (UniChar *)ubuffer, ucnt);
2034 }
2035 }
2036 } else if (kCFDateFormatterAmbiguousYearStrategyKey == key) {
2037 if (formatter->_property._AmbiguousYearStrategy) return CFRetain(formatter->_property._AmbiguousYearStrategy);
2038 } else if (kCFDateFormatterUsesCharacterDirectionKey == key) {
2039 return formatter->_property._UsesCharacterDirection ? CFRetain(formatter->_property._UsesCharacterDirection) : CFRetain(kCFBooleanFalse);
2040 } else {
2041 CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key);
2042 }
2043 return NULL;
2044 }
2045
2046