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