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