2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 2002-2014, Apple Inc. All rights reserved.
26 Responsibility: David Smith
29 #define U_SHOW_INTERNAL_API 1
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>
37 #include "CFInternal.h"
38 #include "CFLocaleInternal.h"
39 #include "CFICULogging.h"
44 typedef CF_ENUM(CFIndex
, CFDateFormatterAmbiguousYearHandling
) {
45 kCFDateFormatterAmbiguousYearFailToParse
= 0, // fail the parse; the default formatter behavior
46 kCFDateFormatterAmbiguousYearAssumeToNone
= 1, // default to assuming era 1, or the year 0-99
47 kCFDateFormatterAmbiguousYearAssumeToCurrent
= 2, // default to assuming the current century or era
48 kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate
= 3,
49 kCFDateFormatterAmbiguousYearAssumeToFuture
= 4,
50 kCFDateFormatterAmbiguousYearAssumeToPast
= 5,
51 kCFDateFormatterAmbiguousYearAssumeToLikelyFuture
= 6,
52 kCFDateFormatterAmbiguousYearAssumeToLikelyPast
= 7
55 extern UCalendar
*__CFCalendarCreateUCalendar(CFStringRef calendarID
, CFStringRef localeID
, CFTimeZoneRef tz
);
57 static CONST_STRING_DECL(kCFDateFormatterFormattingContextKey
, "kCFDateFormatterFormattingContextKey");
59 CF_EXPORT
const CFStringRef kCFDateFormatterCalendarIdentifierKey
;
61 #undef CFReleaseIfNotNull
62 #define CFReleaseIfNotNull(X) if (X) CFRelease(X)
64 #define BUFFER_SIZE 768
66 static CFStringRef
__CFDateFormatterCreateForcedTemplate(CFLocaleRef locale
, CFStringRef inString
, Boolean stripAMPM
);
68 // If you pass in a string in tmplate, you get back NULL (failure) or a CFStringRef.
69 // If you pass in an array in tmplate, you get back NULL (global failure) or a CFArrayRef with CFStringRefs or kCFNulls (per-template failure) at each corresponding index.
71 CFArrayRef
CFDateFormatterCreateDateFormatsFromTemplates(CFAllocatorRef allocator
, CFArrayRef tmplates
, CFOptionFlags options
, CFLocaleRef locale
) {
72 return (CFArrayRef
)CFDateFormatterCreateDateFormatFromTemplate(allocator
, (CFStringRef
)tmplates
, options
, locale
);
75 static Boolean
useTemplatePatternGenerator(CFLocaleRef locale
, void(^work
)(UDateTimePatternGenerator
*ptg
)) {
76 static UDateTimePatternGenerator
*ptg
;
77 static pthread_mutex_t ptgLock
= PTHREAD_MUTEX_INITIALIZER
;
78 static const char *ptgLocaleName
;
79 CFStringRef ln
= locale
? CFLocaleGetIdentifier(locale
) : CFSTR("");
80 char buffer
[BUFFER_SIZE
];
81 const char *localeName
= CFStringGetCStringPtr(ln
, kCFStringEncodingASCII
);
82 if (NULL
== localeName
) {
83 if (CFStringGetCString(ln
, buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
)) localeName
= buffer
;
86 static void (^flushCache
)() = ^{
87 __cficu_udatpg_close(ptg
);
89 free((void *)ptgLocaleName
);
92 pthread_mutex_lock(&ptgLock
);
93 if (ptgLocaleName
&& strcmp(ptgLocaleName
, localeName
) != 0) {
96 UErrorCode status
= U_ZERO_ERROR
;
98 ptg
= __cficu_udatpg_open(localeName
, &status
);
99 if (ptg
&& !U_FAILURE(status
)) {
100 ptgLocaleName
= strdup(localeName
);
103 Boolean result
= (NULL
!= ptg
&& !U_FAILURE(status
));
104 if (result
&& work
) {
107 pthread_mutex_unlock(&ptgLock
);
112 1) Scan the string for an AM/PM indicator
113 2) Back up past any spaces in front of the AM/PM indicator
114 3) As long as the current character is whitespace, or an 'a', remove it and shift everything past it down
116 static void _CFDateFormatterStripAMPMIndicators(UniChar
**bpat
, int32_t *bpatlen
, CFIndex bufferSize
) {
119 for (CFIndex idx
= 0; idx
< *bpatlen
; idx
++) {
120 if ((*bpat
)[idx
] == 'a') {
123 while ((*bpat
)[idx
- 1] == ' ') {
128 for (; (*bpat
)[idx
] == ' ' || (*bpat
)[idx
] == 'a'; idx
++) {
129 for (CFIndex shiftIdx
= idx
; shiftIdx
< *bpatlen
&& shiftIdx
+ 1 < bufferSize
; shiftIdx
++) {
130 (*bpat
)[shiftIdx
] = (*bpat
)[shiftIdx
+ 1];
132 //compensate for the character we just removed
140 CFStringRef
CFDateFormatterCreateDateFormatFromTemplate(CFAllocatorRef allocator
, CFStringRef tmplate
, CFOptionFlags options
, CFLocaleRef locale
) {
141 if (allocator
) __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
142 if (locale
) __CFGenericValidateType(locale
, CFLocaleGetTypeID());
143 Boolean tmplateIsString
= (CFStringGetTypeID() == CFGetTypeID(tmplate
));
144 if (!tmplateIsString
) {
145 __CFGenericValidateType(tmplate
, CFArrayGetTypeID());
148 __block CFTypeRef result
= tmplateIsString
? NULL
: (CFTypeRef
)CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
150 Boolean success
= useTemplatePatternGenerator(locale
, ^(UDateTimePatternGenerator
*ptg
) {
153 for (CFIndex idx
= 0, cnt
= tmplateIsString
? 1 : CFArrayGetCount((CFArrayRef
)tmplate
); idx
< cnt
; idx
++) {
154 CFStringRef tmplateString
= tmplateIsString
? (CFStringRef
)tmplate
: (CFStringRef
)CFArrayGetValueAtIndex((CFArrayRef
)tmplate
, idx
);
155 CFStringRef resultString
= NULL
;
157 Boolean stripAMPM
= CFStringFind(tmplateString
, CFSTR("J"), 0).location
!= kCFNotFound
;
158 tmplateString
= __CFDateFormatterCreateForcedTemplate(locale
? locale
: CFLocaleGetSystem(), tmplateString
, stripAMPM
);
160 CFIndex jCount
= 0; // the only interesting cases are 0, 1, and 2 (adjacent)
161 UniChar adjacentJs
[2] = {-1, -1};
162 CFRange r
= CFStringFind(tmplateString
, CFSTR("j"), kCFCompareCaseInsensitive
);
163 if (kCFNotFound
!= r
.location
) {
164 adjacentJs
[0] = CFStringGetCharacterAtIndex(tmplateString
, r
.location
);
166 if ((r
.location
+ 1 < CFStringGetLength(tmplateString
)) && ('j' == CFStringGetCharacterAtIndex(tmplateString
, r
.location
+ 1) || 'J' == CFStringGetCharacterAtIndex(tmplateString
, r
.location
+ 1))) {
168 adjacentJs
[1] = CFStringGetCharacterAtIndex(tmplateString
, r
.location
+ 1);
172 UChar pattern
[BUFFER_SIZE
] = {0}, skel
[BUFFER_SIZE
] = {0}, bpat
[BUFFER_SIZE
] = {0};
173 CFIndex tmpltLen
= CFStringGetLength(tmplateString
);
174 if (BUFFER_SIZE
< tmpltLen
) tmpltLen
= BUFFER_SIZE
;
175 CFStringGetCharacters(tmplateString
, CFRangeMake(0, tmpltLen
), (UniChar
*)pattern
);
176 CFRelease(tmplateString
);
178 int32_t patlen
= tmpltLen
;
179 UErrorCode status
= U_ZERO_ERROR
;
180 int32_t skellen
= __cficu_udatpg_getSkeleton(ptg
, pattern
, patlen
, skel
, sizeof(skel
) / sizeof(skel
[0]), &status
);
181 if (!U_FAILURE(status
)) {
182 if ((0 < jCount
) && (skellen
+ jCount
< (sizeof(skel
) / sizeof(skel
[0])))) {
184 skel
[skellen
++] = 'j'; //adjacentJs[0];
185 if (1 < jCount
) skel
[skellen
++] = 'j'; //adjacentJs[1];
186 //stripAMPM = false; //'J' will take care of it. We only need to do it manually if we stripped the Js out ourselves while forcing 12/24 hour time
189 status
= U_ZERO_ERROR
;
190 int32_t bpatlen
= __cficu_udatpg_getBestPattern(ptg
, skel
, skellen
, bpat
, sizeof(bpat
) / sizeof(bpat
[0]), &status
);
191 if (!U_FAILURE(status
)) {
193 UniChar
*bpatptr
= (UniChar
*)bpat
;
194 _CFDateFormatterStripAMPMIndicators(&bpatptr
, &bpatlen
, BUFFER_SIZE
);
196 resultString
= CFStringCreateWithCharacters(allocator
, (const UniChar
*)bpat
, bpatlen
);
200 if (tmplateIsString
) {
201 result
= (CFTypeRef
)resultString
;
203 CFArrayAppendValue((CFMutableArrayRef
)result
, resultString
? (CFTypeRef
)resultString
: (CFTypeRef
)kCFNull
);
204 if (resultString
) CFRelease(resultString
);
210 if (result
) CFRelease(result
);
214 return (CFStringRef
)result
;
217 struct __CFDateFormatter
{
221 CFDateFormatterStyle _timeStyle
;
222 CFDateFormatterStyle _dateStyle
;
224 CFStringRef _defformat
;
226 CFBooleanRef _IsLenient
;
227 CFBooleanRef _DoesRelativeDateFormatting
;
228 CFBooleanRef _HasCustomFormat
;
229 CFTimeZoneRef _TimeZone
;
230 CFCalendarRef _Calendar
;
231 CFStringRef _CalendarName
;
232 CFDateRef _TwoDigitStartDate
;
233 CFDateRef _DefaultDate
;
234 CFDateRef _GregorianStartDate
;
235 CFArrayRef _EraSymbols
;
236 CFArrayRef _LongEraSymbols
;
237 CFArrayRef _MonthSymbols
;
238 CFArrayRef _ShortMonthSymbols
;
239 CFArrayRef _VeryShortMonthSymbols
;
240 CFArrayRef _StandaloneMonthSymbols
;
241 CFArrayRef _ShortStandaloneMonthSymbols
;
242 CFArrayRef _VeryShortStandaloneMonthSymbols
;
243 CFArrayRef _WeekdaySymbols
;
244 CFArrayRef _ShortWeekdaySymbols
;
245 CFArrayRef _VeryShortWeekdaySymbols
;
246 CFArrayRef _StandaloneWeekdaySymbols
;
247 CFArrayRef _ShortStandaloneWeekdaySymbols
;
248 CFArrayRef _VeryShortStandaloneWeekdaySymbols
;
249 CFArrayRef _QuarterSymbols
;
250 CFArrayRef _ShortQuarterSymbols
;
251 CFArrayRef _StandaloneQuarterSymbols
;
252 CFArrayRef _ShortStandaloneQuarterSymbols
;
253 CFStringRef _AMSymbol
;
254 CFStringRef _PMSymbol
;
255 CFNumberRef _AmbiguousYearStrategy
;
256 CFBooleanRef _UsesCharacterDirection
;
257 CFNumberRef _FormattingContext
;
259 // the following are from preferences
260 CFArrayRef _CustomEraSymbols
;
261 CFArrayRef _CustomLongEraSymbols
;
262 CFArrayRef _CustomMonthSymbols
;
263 CFArrayRef _CustomShortMonthSymbols
;
264 CFArrayRef _CustomVeryShortMonthSymbols
;
265 CFArrayRef _CustomStandaloneMonthSymbols
;
266 CFArrayRef _CustomShortStandaloneMonthSymbols
;
267 CFArrayRef _CustomVeryShortStandaloneMonthSymbols
;
268 CFArrayRef _CustomWeekdaySymbols
;
269 CFArrayRef _CustomShortWeekdaySymbols
;
270 CFArrayRef _CustomVeryShortWeekdaySymbols
;
271 CFArrayRef _CustomStandaloneWeekdaySymbols
;
272 CFArrayRef _CustomShortStandaloneWeekdaySymbols
;
273 CFArrayRef _CustomVeryShortStandaloneWeekdaySymbols
;
274 CFArrayRef _CustomQuarterSymbols
;
275 CFArrayRef _CustomShortQuarterSymbols
;
276 CFArrayRef _CustomStandaloneQuarterSymbols
;
277 CFArrayRef _CustomShortStandaloneQuarterSymbols
;
278 CFStringRef _CustomDateFormat
;
279 CFStringRef _CustomTimeFormat
;
280 CFBooleanRef _Custom24Hour
;
281 CFBooleanRef _Custom12Hour
;
282 CFStringRef _CustomAMSymbol
;
283 CFStringRef _CustomPMSymbol
;
284 CFDictionaryRef _CustomFirstWeekday
;
285 CFDictionaryRef _CustomMinDaysInFirstWeek
;
290 static CFStringRef
__CFDateFormatterCopyDescription(CFTypeRef cf
) {
291 CFDateFormatterRef formatter
= (CFDateFormatterRef
)cf
;
292 return CFStringCreateWithFormat(CFGetAllocator(formatter
), NULL
, CFSTR("<CFDateFormatter %p [%p]>"), cf
, CFGetAllocator(formatter
));
295 static void __CFDateFormatterDeallocate(CFTypeRef cf
) {
296 CFDateFormatterRef formatter
= (CFDateFormatterRef
)cf
;
297 if (formatter
->_df
) __cficu_udat_close(formatter
->_df
);
298 if (formatter
->_locale
) CFRelease(formatter
->_locale
);
299 if (formatter
->_format
) CFRelease(formatter
->_format
);
300 if (formatter
->_defformat
) CFRelease(formatter
->_defformat
);
301 CFReleaseIfNotNull(formatter
->_property
._IsLenient
);
302 CFReleaseIfNotNull(formatter
->_property
._DoesRelativeDateFormatting
);
303 CFReleaseIfNotNull(formatter
->_property
._TimeZone
);
304 CFReleaseIfNotNull(formatter
->_property
._Calendar
);
305 CFReleaseIfNotNull(formatter
->_property
._CalendarName
);
306 CFReleaseIfNotNull(formatter
->_property
._TwoDigitStartDate
);
307 CFReleaseIfNotNull(formatter
->_property
._DefaultDate
);
308 CFReleaseIfNotNull(formatter
->_property
._GregorianStartDate
);
309 CFReleaseIfNotNull(formatter
->_property
._EraSymbols
);
310 CFReleaseIfNotNull(formatter
->_property
._LongEraSymbols
);
311 CFReleaseIfNotNull(formatter
->_property
._MonthSymbols
);
312 CFReleaseIfNotNull(formatter
->_property
._ShortMonthSymbols
);
313 CFReleaseIfNotNull(formatter
->_property
._VeryShortMonthSymbols
);
314 CFReleaseIfNotNull(formatter
->_property
._StandaloneMonthSymbols
);
315 CFReleaseIfNotNull(formatter
->_property
._ShortStandaloneMonthSymbols
);
316 CFReleaseIfNotNull(formatter
->_property
._VeryShortStandaloneMonthSymbols
);
317 CFReleaseIfNotNull(formatter
->_property
._WeekdaySymbols
);
318 CFReleaseIfNotNull(formatter
->_property
._ShortWeekdaySymbols
);
319 CFReleaseIfNotNull(formatter
->_property
._VeryShortWeekdaySymbols
);
320 CFReleaseIfNotNull(formatter
->_property
._StandaloneWeekdaySymbols
);
321 CFReleaseIfNotNull(formatter
->_property
._ShortStandaloneWeekdaySymbols
);
322 CFReleaseIfNotNull(formatter
->_property
._VeryShortStandaloneWeekdaySymbols
);
323 CFReleaseIfNotNull(formatter
->_property
._QuarterSymbols
);
324 CFReleaseIfNotNull(formatter
->_property
._ShortQuarterSymbols
);
325 CFReleaseIfNotNull(formatter
->_property
._StandaloneQuarterSymbols
);
326 CFReleaseIfNotNull(formatter
->_property
._ShortStandaloneQuarterSymbols
);
327 CFReleaseIfNotNull(formatter
->_property
._AMSymbol
);
328 CFReleaseIfNotNull(formatter
->_property
._PMSymbol
);
329 CFReleaseIfNotNull(formatter
->_property
._AmbiguousYearStrategy
);
330 CFReleaseIfNotNull(formatter
->_property
._UsesCharacterDirection
);
331 CFReleaseIfNotNull(formatter
->_property
._FormattingContext
);
332 CFReleaseIfNotNull(formatter
->_property
._CustomEraSymbols
);
333 CFReleaseIfNotNull(formatter
->_property
._CustomMonthSymbols
);
334 CFReleaseIfNotNull(formatter
->_property
._CustomShortMonthSymbols
);
335 CFReleaseIfNotNull(formatter
->_property
._CustomWeekdaySymbols
);
336 CFReleaseIfNotNull(formatter
->_property
._CustomShortWeekdaySymbols
);
337 CFReleaseIfNotNull(formatter
->_property
._CustomLongEraSymbols
);
338 CFReleaseIfNotNull(formatter
->_property
._CustomVeryShortMonthSymbols
);
339 CFReleaseIfNotNull(formatter
->_property
._CustomVeryShortWeekdaySymbols
);
340 CFReleaseIfNotNull(formatter
->_property
._CustomStandaloneMonthSymbols
);
341 CFReleaseIfNotNull(formatter
->_property
._CustomShortStandaloneMonthSymbols
);
342 CFReleaseIfNotNull(formatter
->_property
._CustomVeryShortStandaloneMonthSymbols
);
343 CFReleaseIfNotNull(formatter
->_property
._CustomStandaloneWeekdaySymbols
);
344 CFReleaseIfNotNull(formatter
->_property
._CustomShortStandaloneWeekdaySymbols
);
345 CFReleaseIfNotNull(formatter
->_property
._CustomVeryShortStandaloneWeekdaySymbols
);
346 CFReleaseIfNotNull(formatter
->_property
._CustomQuarterSymbols
);
347 CFReleaseIfNotNull(formatter
->_property
._CustomShortQuarterSymbols
);
348 CFReleaseIfNotNull(formatter
->_property
._CustomShortStandaloneQuarterSymbols
);
349 CFReleaseIfNotNull(formatter
->_property
._CustomDateFormat
);
350 CFReleaseIfNotNull(formatter
->_property
._CustomTimeFormat
);
351 CFReleaseIfNotNull(formatter
->_property
._Custom24Hour
);
352 CFReleaseIfNotNull(formatter
->_property
._Custom12Hour
);
353 CFReleaseIfNotNull(formatter
->_property
._CustomAMSymbol
);
354 CFReleaseIfNotNull(formatter
->_property
._CustomPMSymbol
);
355 CFReleaseIfNotNull(formatter
->_property
._CustomFirstWeekday
);
356 CFReleaseIfNotNull(formatter
->_property
._CustomMinDaysInFirstWeek
);
359 static CFStringRef
__CFDateFormatterCreateForcedString(CFDateFormatterRef formatter
, CFStringRef inString
);
361 static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter
, CFStringRef key
, CFTypeRef value
, Boolean directToICU
);
362 static void __CFDateFormatterStoreSymbolPrefs(const void *key
, const void *value
, void *context
);
363 extern CFDictionaryRef
__CFLocaleGetPrefs(CFLocaleRef locale
);
364 static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter
);
365 static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter
, bool doTime
);
366 static void __CFDateFormatterSetSymbolsArray(UDateFormat
*icudf
, int32_t icucode
, int index_base
, CFTypeRef value
);
368 static void __ReadCustomUDateFormatProperty(CFDateFormatterRef formatter
) {
369 CFDictionaryRef prefs
= __CFLocaleGetPrefs(formatter
->_locale
);
370 CFPropertyListRef metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUDateTimeSymbols")) : NULL
;
371 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
372 CFDictionaryApplyFunction((CFDictionaryRef
)metapref
, __CFDateFormatterStoreSymbolPrefs
, formatter
);
374 metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleFirstWeekday")) : NULL
;
375 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
376 formatter
->_property
._CustomFirstWeekday
= (CFDictionaryRef
)CFRetain(metapref
);
378 metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleMinDaysInFirstWeek")) : NULL
;
379 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
380 formatter
->_property
._CustomMinDaysInFirstWeek
= (CFDictionaryRef
)CFRetain(metapref
);
382 metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUForce24HourTime")) : NULL
;
383 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFBooleanGetTypeID()) {
384 formatter
->_property
._Custom24Hour
= (CFBooleanRef
)CFRetain(metapref
);
386 metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUForce12HourTime")) : NULL
;
387 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFBooleanGetTypeID()) {
388 formatter
->_property
._Custom12Hour
= (CFBooleanRef
)CFRetain(metapref
);
390 metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUDateFormatStrings")) : NULL
;
391 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
393 switch (formatter
->_dateStyle
) {
394 case kCFDateFormatterShortStyle
: key
= CFSTR("1"); break;
395 case kCFDateFormatterMediumStyle
: key
= CFSTR("2"); break;
396 case kCFDateFormatterLongStyle
: key
= CFSTR("3"); break;
397 case kCFDateFormatterFullStyle
: key
= CFSTR("4"); break;
398 default: key
= CFSTR("0"); break;
400 CFStringRef dateFormat
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)metapref
, key
);
401 if (NULL
!= dateFormat
&& CFGetTypeID(dateFormat
) == CFStringGetTypeID()) {
402 formatter
->_property
._CustomDateFormat
= (CFStringRef
)CFRetain(dateFormat
);
405 metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUTimeFormatStrings")) : NULL
;
406 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
408 switch (formatter
->_timeStyle
) {
409 case kCFDateFormatterShortStyle
: key
= CFSTR("1"); break;
410 case kCFDateFormatterMediumStyle
: key
= CFSTR("2"); break;
411 case kCFDateFormatterLongStyle
: key
= CFSTR("3"); break;
412 case kCFDateFormatterFullStyle
: key
= CFSTR("4"); break;
413 default: key
= CFSTR("0"); break;
415 CFStringRef timeFormat
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)metapref
, key
);
416 if (NULL
!= timeFormat
&& CFGetTypeID(timeFormat
) == CFStringGetTypeID()) {
417 formatter
->_property
._CustomTimeFormat
= (CFStringRef
)CFRetain(timeFormat
);
422 static void __ApplyUDateFormatSymbol(CFDateFormatterRef formatter
) {
423 UDateFormatSymbolType types
[18] = {UDAT_ERAS
,
428 UDAT_STANDALONE_MONTHS
,
429 UDAT_STANDALONE_SHORT_MONTHS
,
430 UDAT_STANDALONE_NARROW_MONTHS
,
433 UDAT_NARROW_WEEKDAYS
,
434 UDAT_STANDALONE_WEEKDAYS
,
435 UDAT_STANDALONE_SHORT_WEEKDAYS
,
436 UDAT_STANDALONE_NARROW_WEEKDAYS
,
439 UDAT_STANDALONE_QUARTERS
,
440 UDAT_STANDALONE_SHORT_QUARTERS
};
441 int offsets
[18] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0};
442 CFArrayRef symbols
[18] = {formatter
->_property
._EraSymbols
,
443 formatter
->_property
._LongEraSymbols
,
444 formatter
->_property
._MonthSymbols
,
445 formatter
->_property
._ShortMonthSymbols
,
446 formatter
->_property
._VeryShortMonthSymbols
,
447 formatter
->_property
._StandaloneMonthSymbols
,
448 formatter
->_property
._ShortStandaloneMonthSymbols
,
449 formatter
->_property
._VeryShortStandaloneMonthSymbols
,
450 formatter
->_property
._WeekdaySymbols
,
451 formatter
->_property
._ShortWeekdaySymbols
,
452 formatter
->_property
._VeryShortWeekdaySymbols
,
453 formatter
->_property
._StandaloneWeekdaySymbols
,
454 formatter
->_property
._ShortStandaloneWeekdaySymbols
,
455 formatter
->_property
._VeryShortStandaloneWeekdaySymbols
,
456 formatter
->_property
._QuarterSymbols
,
457 formatter
->_property
._ShortQuarterSymbols
,
458 formatter
->_property
._StandaloneQuarterSymbols
,
459 formatter
->_property
._ShortStandaloneQuarterSymbols
461 CFArrayRef customSymbols
[18] = {formatter
->_property
._CustomEraSymbols
,
462 formatter
->_property
._CustomLongEraSymbols
,
463 formatter
->_property
._CustomMonthSymbols
,
464 formatter
->_property
._CustomShortMonthSymbols
,
465 formatter
->_property
._CustomVeryShortMonthSymbols
,
466 formatter
->_property
._CustomStandaloneMonthSymbols
,
467 formatter
->_property
._CustomShortStandaloneMonthSymbols
,
468 formatter
->_property
._CustomVeryShortStandaloneMonthSymbols
,
469 formatter
->_property
._CustomWeekdaySymbols
,
470 formatter
->_property
._CustomShortWeekdaySymbols
,
471 formatter
->_property
._CustomVeryShortWeekdaySymbols
,
472 formatter
->_property
._CustomStandaloneWeekdaySymbols
,
473 formatter
->_property
._CustomShortStandaloneWeekdaySymbols
,
474 formatter
->_property
._CustomVeryShortStandaloneWeekdaySymbols
,
475 formatter
->_property
._CustomQuarterSymbols
,
476 formatter
->_property
._CustomShortQuarterSymbols
,
477 formatter
->_property
._CustomStandaloneQuarterSymbols
,
478 formatter
->_property
._CustomShortStandaloneQuarterSymbols
481 for (CFIndex i
= 0; i
< 18; i
++) {
482 if (symbols
[i
] != NULL
) {
483 __CFDateFormatterSetSymbolsArray(formatter
->_df
, types
[i
], offsets
[i
], symbols
[i
]);
484 } else if (customSymbols
[i
] != NULL
) {
485 __CFDateFormatterSetSymbolsArray(formatter
->_df
, types
[i
], offsets
[i
], customSymbols
[i
]);
493 if (formatter
->_property
._AMSymbol
!= NULL
) {
494 ampm
[0] = formatter
->_property
._AMSymbol
;
495 } else if (formatter
->_property
._CustomAMSymbol
!= NULL
) {
496 ampm
[0] = formatter
->_property
._CustomAMSymbol
;
498 if (formatter
->_property
._PMSymbol
!= NULL
) {
499 ampm
[1] = formatter
->_property
._PMSymbol
;
500 } else if (formatter
->_property
._CustomPMSymbol
!= NULL
) {
501 ampm
[1] = formatter
->_property
._CustomPMSymbol
;
503 for (CFIndex i
= 0; i
< 2; i
++) {
504 CFStringRef sym
= ampm
[i
];
506 CFIndex item_cnt
= CFStringGetLength(sym
);
507 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
508 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr(sym
);
509 if (NULL
== item_ustr
) {
510 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
511 CFStringGetCharacters(sym
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
512 item_ustr
= item_buffer
;
514 UErrorCode status
= U_ZERO_ERROR
;
515 __cficu_udat_setSymbols(formatter
->_df
, UDAT_AM_PMS
, i
, item_ustr
, item_cnt
, &status
);
520 static void __SetCalendarProperties(CFDateFormatterRef df
) {
521 CFStringRef calName
= df
->_property
._CalendarName
? (df
->_property
._CalendarName
) : NULL
;
523 calName
= (CFStringRef
)CFLocaleGetValue(df
->_locale
, kCFLocaleCalendarIdentifierKey
);
525 UErrorCode status
= U_ZERO_ERROR
;
526 const UCalendar
*cal
= __cficu_udat_getCalendar(df
->_df
);
527 UCalendar
*new_cal
= NULL
;
529 if (df
->_property
._Calendar
!= NULL
|| df
->_property
._CalendarName
!= NULL
) {
530 UCalendar
*caltmp
= __CFCalendarCreateUCalendar(NULL
, CFLocaleGetIdentifier(df
->_locale
), df
->_property
._TimeZone
);
535 if (new_cal
== NULL
) {
536 new_cal
= __cficu_ucal_clone(cal
, &status
);
539 if (df
->_property
._IsLenient
!= NULL
) {
540 status
= U_ZERO_ERROR
;
541 CFBooleanRef value
= df
->_property
._IsLenient
;
542 __cficu_ucal_setAttribute(new_cal
, UCAL_LENIENT
, (kCFBooleanTrue
== value
));
544 if (df
->_property
._TimeZone
!= NULL
) {
545 status
= U_ZERO_ERROR
;
546 UChar ubuffer
[BUFFER_SIZE
];
547 CFStringRef tznam
= CFTimeZoneGetName(df
->_property
._TimeZone
);
548 CFIndex ucnt
= CFStringGetLength(tznam
);
549 if (BUFFER_SIZE
< ucnt
) ucnt
= BUFFER_SIZE
;
550 CFStringGetCharacters(tznam
, CFRangeMake(0, ucnt
), (UniChar
*)ubuffer
);
551 __cficu_ucal_setTimeZone(new_cal
, ubuffer
, ucnt
, &status
);
553 if (df
->_property
._GregorianStartDate
!= NULL
) {
554 status
= U_ZERO_ERROR
;
555 CFAbsoluteTime at
= CFDateGetAbsoluteTime((CFDateRef
)df
->_property
._GregorianStartDate
);
556 UDate udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
557 __cficu_ucal_setGregorianChange(new_cal
, udate
, &status
);
558 } else if (calName
&& CFEqual(calName
, kCFCalendarIdentifierGregorian
)) {
559 status
= U_ZERO_ERROR
;
560 UDate udate
= __cficu_ucal_getGregorianChange(cal
, &status
);
561 CFAbsoluteTime at
= U_SUCCESS(status
) ? (udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
) : -13197600000.0; // Oct 15, 1582
562 udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
563 status
= U_ZERO_ERROR
;
564 __cficu_ucal_setGregorianChange(new_cal
, udate
, &status
);
566 if (df
->_property
._Calendar
!= NULL
) {
567 __cficu_ucal_setAttribute(new_cal
, UCAL_FIRST_DAY_OF_WEEK
, CFCalendarGetFirstWeekday((CFCalendarRef
)df
->_property
._Calendar
));
568 } else if (df
->_property
._CustomFirstWeekday
!= NULL
) {
569 CFNumberRef firstWeekday
= (CFNumberRef
)CFDictionaryGetValue(df
->_property
._CustomFirstWeekday
, calName
);
570 if (NULL
!= firstWeekday
&& CFGetTypeID(firstWeekday
) == CFNumberGetTypeID()) {
572 if (CFNumberGetValue((CFNumberRef
)firstWeekday
, kCFNumberCFIndexType
, &wkdy
)) {
573 status
= U_ZERO_ERROR
;
574 __cficu_ucal_setAttribute(new_cal
, UCAL_FIRST_DAY_OF_WEEK
, wkdy
);
579 if (df
->_property
._Calendar
!= NULL
) {
580 __cficu_ucal_setAttribute(new_cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
, CFCalendarGetMinimumDaysInFirstWeek((CFCalendarRef
)df
->_property
._Calendar
));
581 } else if (df
->_property
._CustomMinDaysInFirstWeek
!= NULL
) {
582 CFNumberRef minDays
= (CFNumberRef
)CFDictionaryGetValue(df
->_property
._CustomMinDaysInFirstWeek
, calName
);
583 if (NULL
!= minDays
&& CFGetTypeID(minDays
) == CFNumberGetTypeID()) {
585 if (CFNumberGetValue((CFNumberRef
)minDays
, kCFNumberCFIndexType
, &mwd
)) {
586 status
= U_ZERO_ERROR
;
587 __cficu_ucal_setAttribute(new_cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
, mwd
);
591 __cficu_udat_setCalendar(df
->_df
, new_cal
);
592 __cficu_ucal_close(new_cal
);
595 #define RESET_PROPERTY(C, K) \
596 if (df->_property. C) __CFDateFormatterSetProperty(df, K, df->_property. C, true);
598 static void __ResetUDateFormat(CFDateFormatterRef df
, Boolean goingToHaveCustomFormat
) {
599 if (df
->_df
) __cficu_udat_close(df
->_df
);
602 // uses _timeStyle, _dateStyle, _locale, _property._TimeZone; sets _df, _format, _defformat
603 char loc_buffer
[BUFFER_SIZE
];
605 CFStringRef tmpLocName
= df
->_locale
? CFLocaleGetIdentifier(df
->_locale
) : CFSTR("");
606 CFStringGetCString(tmpLocName
, loc_buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
);
608 UChar tz_buffer
[BUFFER_SIZE
];
610 CFStringRef tmpTZName
= df
->_property
._TimeZone
? CFTimeZoneGetName(df
->_property
._TimeZone
) : CFSTR("GMT");
611 CFStringGetCharacters(tmpTZName
, CFRangeMake(0, CFStringGetLength(tmpTZName
)), (UniChar
*)tz_buffer
);
613 int32_t udstyle
= 0, utstyle
= 0; // effectively this makes UDAT_FULL the default for unknown dateStyle/timeStyle values
614 switch (df
->_dateStyle
) {
615 case kCFDateFormatterNoStyle
: udstyle
= UDAT_NONE
; break;
616 case kCFDateFormatterShortStyle
: udstyle
= UDAT_SHORT
; break;
617 case kCFDateFormatterMediumStyle
: udstyle
= UDAT_MEDIUM
; break;
618 case kCFDateFormatterLongStyle
: udstyle
= UDAT_LONG
; break;
619 case kCFDateFormatterFullStyle
: udstyle
= UDAT_FULL
; break;
621 switch (df
->_timeStyle
) {
622 case kCFDateFormatterNoStyle
: utstyle
= UDAT_NONE
; break;
623 case kCFDateFormatterShortStyle
: utstyle
= UDAT_SHORT
; break;
624 case kCFDateFormatterMediumStyle
: utstyle
= UDAT_MEDIUM
; break;
625 case kCFDateFormatterLongStyle
: utstyle
= UDAT_LONG
; break;
626 case kCFDateFormatterFullStyle
: utstyle
= UDAT_FULL
; break;
628 Boolean wantRelative
= (NULL
!= df
->_property
._DoesRelativeDateFormatting
&& df
->_property
._DoesRelativeDateFormatting
== kCFBooleanTrue
);
629 Boolean hasFormat
= (NULL
!= df
->_property
._HasCustomFormat
&& df
->_property
._HasCustomFormat
== kCFBooleanTrue
) || goingToHaveCustomFormat
;
630 if (wantRelative
&& !hasFormat
&& kCFDateFormatterNoStyle
!= df
->_dateStyle
) {
631 udstyle
|= UDAT_RELATIVE
;
634 UErrorCode status
= U_ZERO_ERROR
;
635 UDateFormat
*icudf
= __cficu_udat_open((UDateFormatStyle
)utstyle
, (UDateFormatStyle
)udstyle
, loc_buffer
, tz_buffer
, CFStringGetLength(tmpTZName
), NULL
, 0, &status
);
637 if (NULL
== icudf
|| U_FAILURE(status
)) {
641 // <rdar://problem/15420462> "Yesterday" and "Today" now appear in lower case
642 // ICU uses middle of sentence context for relative days by default. We need to have relative dates to be captalized by default for backward compatibility
644 __cficu_udat_setContext(icudf
, UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
, &status
);
647 if (df
->_property
._IsLenient
!= NULL
) {
648 __cficu_udat_setLenient(icudf
, (kCFBooleanTrue
== df
->_property
._IsLenient
));
650 __cficu_udat_setLenient(icudf
, 0);
652 if (kCFDateFormatterNoStyle
== df
->_dateStyle
&& kCFDateFormatterNoStyle
== df
->_timeStyle
) {
653 if (wantRelative
&& !hasFormat
&& kCFDateFormatterNoStyle
!= df
->_dateStyle
) {
654 UErrorCode s
= U_ZERO_ERROR
;
655 __cficu_udat_applyPatternRelative(icudf
, NULL
, 0, NULL
, 0, &s
);
657 __cficu_udat_applyPattern(icudf
, false, NULL
, 0);
660 if (!wantRelative
&& df
->_property
._HasCustomFormat
== kCFBooleanTrue
) {
661 CFIndex cnt
= CFStringGetLength(df
->_format
);
662 STACK_BUFFER_DECL(UChar
, ubuffer
, cnt
);
663 const UChar
*ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)df
->_format
);
665 CFStringGetCharacters(df
->_format
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
668 __cficu_udat_applyPattern(icudf
, false, ustr
, cnt
);
671 CFStringRef calident
= (CFStringRef
)CFLocaleGetValue(df
->_locale
, kCFLocaleCalendarIdentifierKey
);
672 if (calident
&& CFEqual(calident
, kCFCalendarIdentifierGregorian
)) {
673 status
= U_ZERO_ERROR
;
674 __cficu_udat_set2DigitYearStart(icudf
, -631152000000.0, &status
); // 1950-01-01 00:00:00 GMT
678 __ReadCustomUDateFormatProperty(df
);
680 __SetCalendarProperties(df
);
682 if (wantRelative
&& !hasFormat
&& kCFDateFormatterNoStyle
!= df
->_dateStyle
) {
683 __substituteFormatStringFromPrefsDFRelative(df
);
685 __substituteFormatStringFromPrefsDF(df
, false);
686 __substituteFormatStringFromPrefsDF(df
, true);
689 __ApplyUDateFormatSymbol(df
);
692 if (wantRelative
&& !hasFormat
&& kCFDateFormatterNoStyle
!= df
->_dateStyle
) {
693 UChar dateBuffer
[BUFFER_SIZE
];
694 UChar timeBuffer
[BUFFER_SIZE
];
695 status
= U_ZERO_ERROR
;
696 CFIndex dateLen
= __cficu_udat_toPatternRelativeDate(icudf
, dateBuffer
, BUFFER_SIZE
, &status
);
697 CFIndex timeLen
= (utstyle
!= UDAT_NONE
) ? __cficu_udat_toPatternRelativeTime(icudf
, timeBuffer
, BUFFER_SIZE
, &status
) : 0;
698 if (U_SUCCESS(status
) && dateLen
<= BUFFER_SIZE
&& timeLen
<= BUFFER_SIZE
) {
699 // We assume that the 12/24-hour forcing preferences only affect the Time component
700 CFStringRef newFormat
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)timeBuffer
, timeLen
);
701 CFStringRef formatString
= __CFDateFormatterCreateForcedString(df
, newFormat
);
702 CFIndex cnt
= CFStringGetLength(formatString
);
703 CFAssert1(cnt
<= BUFFER_SIZE
, __kCFLogAssertion
, "%s(): time format string too long", __PRETTY_FUNCTION__
);
704 if (cnt
<= BUFFER_SIZE
) {
705 CFStringGetCharacters(formatString
, CFRangeMake(0, cnt
), (UniChar
*)timeBuffer
);
707 status
= U_ZERO_ERROR
;
708 __cficu_udat_applyPatternRelative(icudf
, dateBuffer
, dateLen
, timeBuffer
, timeLen
, &status
);
709 // ignore error and proceed anyway, what else can be done?
711 UChar ubuffer
[BUFFER_SIZE
];
712 status
= U_ZERO_ERROR
;
713 int32_t ret
= __cficu_udat_toPattern(icudf
, false, ubuffer
, BUFFER_SIZE
, &status
); // read out current pattern
714 if (U_SUCCESS(status
) && ret
<= BUFFER_SIZE
) {
715 if (df
->_format
) CFRelease(df
->_format
);
716 df
->_format
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)ubuffer
, ret
);
719 CFRelease(formatString
);
720 CFRelease(newFormat
);
723 UChar ubuffer
[BUFFER_SIZE
];
724 status
= U_ZERO_ERROR
;
725 int32_t ret
= __cficu_udat_toPattern(icudf
, false, ubuffer
, BUFFER_SIZE
, &status
);
726 if (U_SUCCESS(status
) && ret
<= BUFFER_SIZE
) {
727 CFStringRef newFormat
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)ubuffer
, ret
);
728 CFStringRef formatString
= __CFDateFormatterCreateForcedString(df
, newFormat
);
729 CFIndex cnt
= CFStringGetLength(formatString
);
730 CFAssert1(cnt
<= 1024, __kCFLogAssertion
, "%s(): format string too long", __PRETTY_FUNCTION__
);
731 if (df
->_format
!= formatString
&& cnt
<= 1024) {
732 STACK_BUFFER_DECL(UChar
, ubuffer
, cnt
);
733 const UChar
*ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)formatString
);
735 CFStringGetCharacters(formatString
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
738 UErrorCode status
= U_ZERO_ERROR
;
739 // __cficu_udat_applyPattern(df->_df, false, ustr, cnt, &status);
740 __cficu_udat_applyPattern(df
->_df
, false, ustr
, cnt
);
741 if (U_SUCCESS(status
)) {
742 if (df
->_format
) CFRelease(df
->_format
);
743 df
->_format
= (CFStringRef
)CFStringCreateCopy(CFGetAllocator(df
), formatString
);
746 CFRelease(formatString
);
747 CFRelease(newFormat
);
750 if (df
->_defformat
) CFRelease(df
->_defformat
);
751 df
->_defformat
= df
->_format
? (CFStringRef
)CFRetain(df
->_format
) : NULL
;
753 RESET_PROPERTY(_IsLenient
, kCFDateFormatterIsLenientKey
);
754 RESET_PROPERTY(_DoesRelativeDateFormatting
, kCFDateFormatterDoesRelativeDateFormattingKey
);
755 RESET_PROPERTY(_Calendar
, kCFDateFormatterCalendarKey
);
756 RESET_PROPERTY(_CalendarName
, kCFDateFormatterCalendarIdentifierKey
);
757 RESET_PROPERTY(_TimeZone
, kCFDateFormatterTimeZoneKey
);
758 RESET_PROPERTY(_TwoDigitStartDate
, kCFDateFormatterTwoDigitStartDateKey
);
759 RESET_PROPERTY(_DefaultDate
, kCFDateFormatterDefaultDateKey
);
760 RESET_PROPERTY(_GregorianStartDate
, kCFDateFormatterGregorianStartDateKey
);
761 RESET_PROPERTY(_AmbiguousYearStrategy
, kCFDateFormatterAmbiguousYearStrategyKey
);
762 RESET_PROPERTY(_UsesCharacterDirection
, kCFDateFormatterUsesCharacterDirectionKey
);
763 RESET_PROPERTY(_FormattingContext
, kCFDateFormatterFormattingContextKey
);
766 static CFTypeID __kCFDateFormatterTypeID
= _kCFRuntimeNotATypeID
;
768 static const CFRuntimeClass __CFDateFormatterClass
= {
773 __CFDateFormatterDeallocate
,
777 __CFDateFormatterCopyDescription
780 CFTypeID
CFDateFormatterGetTypeID(void) {
781 static dispatch_once_t initOnce
;
782 dispatch_once(&initOnce
, ^{ __kCFDateFormatterTypeID
= _CFRuntimeRegisterClass(&__CFDateFormatterClass
); });
783 return __kCFDateFormatterTypeID
;
786 CFDateFormatterRef
CFDateFormatterCreate(CFAllocatorRef allocator
, CFLocaleRef locale
, CFDateFormatterStyle dateStyle
, CFDateFormatterStyle timeStyle
) {
787 struct __CFDateFormatter
*memory
;
788 uint32_t size
= sizeof(struct __CFDateFormatter
) - sizeof(CFRuntimeBase
);
789 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
790 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
791 if (locale
) __CFGenericValidateType(locale
, CFLocaleGetTypeID());
792 memory
= (struct __CFDateFormatter
*)_CFRuntimeCreateInstance(allocator
, CFDateFormatterGetTypeID(), size
, NULL
);
793 if (NULL
== memory
) {
797 memory
->_locale
= NULL
;
798 memory
->_format
= NULL
;
799 memory
->_defformat
= NULL
;
800 memory
->_dateStyle
= dateStyle
;
801 memory
->_timeStyle
= timeStyle
;
802 memory
->_property
._IsLenient
= NULL
;
803 memory
->_property
._DoesRelativeDateFormatting
= NULL
;
804 memory
->_property
._HasCustomFormat
= NULL
;
805 memory
->_property
._TimeZone
= NULL
;
806 memory
->_property
._Calendar
= NULL
;
807 memory
->_property
._CalendarName
= NULL
;
808 memory
->_property
._TwoDigitStartDate
= NULL
;
809 memory
->_property
._DefaultDate
= NULL
;
810 memory
->_property
._GregorianStartDate
= NULL
;
811 memory
->_property
._EraSymbols
= NULL
;
812 memory
->_property
._LongEraSymbols
= NULL
;
813 memory
->_property
._MonthSymbols
= NULL
;
814 memory
->_property
._ShortMonthSymbols
= NULL
;
815 memory
->_property
._VeryShortMonthSymbols
= NULL
;
816 memory
->_property
._StandaloneMonthSymbols
= NULL
;
817 memory
->_property
._ShortStandaloneMonthSymbols
= NULL
;
818 memory
->_property
._VeryShortStandaloneMonthSymbols
= NULL
;
819 memory
->_property
._WeekdaySymbols
= NULL
;
820 memory
->_property
._ShortWeekdaySymbols
= NULL
;
821 memory
->_property
._VeryShortWeekdaySymbols
= NULL
;
822 memory
->_property
._StandaloneWeekdaySymbols
= NULL
;
823 memory
->_property
._ShortStandaloneWeekdaySymbols
= NULL
;
824 memory
->_property
._VeryShortStandaloneWeekdaySymbols
= NULL
;
825 memory
->_property
._QuarterSymbols
= NULL
;
826 memory
->_property
._ShortQuarterSymbols
= NULL
;
827 memory
->_property
._StandaloneQuarterSymbols
= NULL
;
828 memory
->_property
._ShortStandaloneQuarterSymbols
= NULL
;
829 memory
->_property
._AMSymbol
= NULL
;
830 memory
->_property
._PMSymbol
= NULL
;
831 memory
->_property
._AmbiguousYearStrategy
= NULL
;
832 memory
->_property
._UsesCharacterDirection
= NULL
;
833 memory
->_property
._FormattingContext
= NULL
;
834 memory
->_property
._CustomEraSymbols
= NULL
;
835 memory
->_property
._CustomMonthSymbols
= NULL
;
836 memory
->_property
._CustomShortMonthSymbols
= NULL
;
837 memory
->_property
._CustomWeekdaySymbols
= NULL
;
838 memory
->_property
._CustomShortWeekdaySymbols
= NULL
;
839 memory
->_property
._CustomLongEraSymbols
= NULL
;
840 memory
->_property
._CustomVeryShortMonthSymbols
= NULL
;
841 memory
->_property
._CustomVeryShortWeekdaySymbols
= NULL
;
842 memory
->_property
._CustomStandaloneMonthSymbols
= NULL
;
843 memory
->_property
._CustomShortStandaloneMonthSymbols
= NULL
;
844 memory
->_property
._CustomVeryShortStandaloneMonthSymbols
= NULL
;
845 memory
->_property
._CustomStandaloneWeekdaySymbols
= NULL
;
846 memory
->_property
._CustomShortStandaloneWeekdaySymbols
= NULL
;
847 memory
->_property
._CustomVeryShortStandaloneWeekdaySymbols
= NULL
;
848 memory
->_property
._CustomQuarterSymbols
= NULL
;
849 memory
->_property
._CustomShortQuarterSymbols
= NULL
;
850 memory
->_property
._CustomStandaloneQuarterSymbols
= NULL
;
851 memory
->_property
._CustomShortStandaloneQuarterSymbols
= NULL
;
852 memory
->_property
._CustomDateFormat
= NULL
;
853 memory
->_property
._CustomTimeFormat
= NULL
;
854 memory
->_property
._Custom24Hour
= NULL
;
855 memory
->_property
._Custom12Hour
= NULL
;
856 memory
->_property
._CustomAMSymbol
= NULL
;
857 memory
->_property
._CustomPMSymbol
= NULL
;
858 memory
->_property
._CustomFirstWeekday
= NULL
;
859 memory
->_property
._CustomMinDaysInFirstWeek
= NULL
;
862 case kCFDateFormatterNoStyle
:
863 case kCFDateFormatterShortStyle
:
864 case kCFDateFormatterMediumStyle
:
865 case kCFDateFormatterLongStyle
:
866 case kCFDateFormatterFullStyle
: break;
868 CFAssert2(0, __kCFLogAssertion
, "%s(): unknown date style %d", __PRETTY_FUNCTION__
, dateStyle
);
869 memory
->_dateStyle
= kCFDateFormatterMediumStyle
;
873 case kCFDateFormatterNoStyle
:
874 case kCFDateFormatterShortStyle
:
875 case kCFDateFormatterMediumStyle
:
876 case kCFDateFormatterLongStyle
:
877 case kCFDateFormatterFullStyle
: break;
879 CFAssert2(0, __kCFLogAssertion
, "%s(): unknown time style %d", __PRETTY_FUNCTION__
, timeStyle
);
880 memory
->_timeStyle
= kCFDateFormatterMediumStyle
;
884 memory
->_locale
= locale
? CFLocaleCreateCopy(allocator
, locale
) : (CFLocaleRef
)CFRetain(CFLocaleGetSystem());
885 memory
->_property
._TimeZone
= CFTimeZoneCopyDefault();
887 CFStringRef calident
= (CFStringRef
)CFLocaleGetValue(memory
->_locale
, kCFLocaleCalendarIdentifierKey
);
888 if (calident
&& CFEqual(calident
, kCFCalendarIdentifierGregorian
)) {
889 memory
->_property
._TwoDigitStartDate
= CFDateCreate(kCFAllocatorSystemDefault
, -1609459200.0); // 1950-01-01 00:00:00 +0000
892 __ResetUDateFormat(memory
, false);
897 return (CFDateFormatterRef
)memory
;
900 static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter
) {
902 CFIndex dateLen
= -1;
903 UChar dateBuffer
[BUFFER_SIZE
];
904 if (kCFDateFormatterNoStyle
!= formatter
->_dateStyle
) {
905 if (formatter
->_property
._CustomDateFormat
!= NULL
) {
906 dateLen
= __CFMin(CFStringGetLength(formatter
->_property
._CustomDateFormat
), BUFFER_SIZE
);
907 CFStringGetCharacters(formatter
->_property
._CustomDateFormat
, CFRangeMake(0, dateLen
), (UniChar
*)dateBuffer
);
911 UErrorCode status
= U_ZERO_ERROR
;
912 int32_t ret
= __cficu_udat_toPatternRelativeDate(formatter
->_df
, dateBuffer
, BUFFER_SIZE
, &status
);
913 if (!U_FAILURE(status
)) {
918 CFIndex timeLen
= -1;
919 UChar timeBuffer
[BUFFER_SIZE
];
920 if (kCFDateFormatterNoStyle
!= formatter
->_timeStyle
) {
921 if (formatter
->_property
._CustomTimeFormat
!= NULL
) {
922 timeLen
= __CFMin(CFStringGetLength(formatter
->_property
._CustomTimeFormat
), BUFFER_SIZE
);
923 CFStringGetCharacters(formatter
->_property
._CustomTimeFormat
, CFRangeMake(0, timeLen
), (UniChar
*)timeBuffer
);
927 UErrorCode status
= U_ZERO_ERROR
;
928 int32_t ret
= __cficu_udat_toPatternRelativeTime(formatter
->_df
, timeBuffer
, BUFFER_SIZE
, &status
);
929 if (!U_FAILURE(status
)) {
934 UErrorCode status
= U_ZERO_ERROR
;
935 __cficu_udat_applyPatternRelative(formatter
->_df
, (0 <= dateLen
) ? dateBuffer
: NULL
, (0 <= dateLen
) ? dateLen
: 0, (0 <= timeLen
) ? timeBuffer
: NULL
, (0 <= timeLen
) ? timeLen
: 0, &status
);
938 static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter
, bool doTime
) {
939 CFIndex formatStyle
= doTime
? formatter
->_timeStyle
: formatter
->_dateStyle
;
940 CFStringRef pref
= doTime
? formatter
->_property
._CustomTimeFormat
: formatter
->_property
._CustomDateFormat
;
941 if (kCFDateFormatterNoStyle
!= formatStyle
) {
943 int32_t icustyle
= UDAT_NONE
;
944 switch (formatStyle
) {
945 case kCFDateFormatterShortStyle
: icustyle
= UDAT_SHORT
; break;
946 case kCFDateFormatterMediumStyle
: icustyle
= UDAT_MEDIUM
; break;
947 case kCFDateFormatterLongStyle
: icustyle
= UDAT_LONG
; break;
948 case kCFDateFormatterFullStyle
: icustyle
= UDAT_FULL
; break;
950 CFStringRef localeName
= CFLocaleGetIdentifier(formatter
->_locale
);
951 char buffer
[BUFFER_SIZE
];
952 const char *cstr
= CFStringGetCStringPtr(localeName
, kCFStringEncodingASCII
);
954 if (CFStringGetCString(localeName
, buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
)) cstr
= buffer
;
956 UErrorCode status
= U_ZERO_ERROR
;
957 UDateFormat
*df
= __cficu_udat_open((UDateFormatStyle
)(doTime
? icustyle
: UDAT_NONE
), (UDateFormatStyle
)(doTime
? UDAT_NONE
: icustyle
), cstr
, NULL
, 0, NULL
, 0, &status
);
959 UChar ubuffer
[BUFFER_SIZE
];
960 status
= U_ZERO_ERROR
;
961 int32_t date_len
= __cficu_udat_toPattern(df
, false, ubuffer
, BUFFER_SIZE
, &status
);
962 if (U_SUCCESS(status
) && date_len
<= BUFFER_SIZE
) {
963 CFStringRef dateString
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (UniChar
*)ubuffer
, date_len
);
964 status
= U_ZERO_ERROR
;
965 int32_t formatter_len
= __cficu_udat_toPattern(formatter
->_df
, false, ubuffer
, BUFFER_SIZE
, &status
);
966 if (U_SUCCESS(status
) && formatter_len
<= BUFFER_SIZE
) {
967 CFMutableStringRef formatString
= CFStringCreateMutable(kCFAllocatorSystemDefault
, 0);
968 CFStringAppendCharacters(formatString
, (UniChar
*)ubuffer
, formatter_len
);
969 // find dateString inside formatString, substitute the pref in that range
971 if (CFStringFindWithOptions(formatString
, dateString
, CFRangeMake(0, formatter_len
), 0, &result
)) {
972 CFStringReplace(formatString
, result
, pref
);
973 int32_t new_len
= CFStringGetLength(formatString
);
974 STACK_BUFFER_DECL(UChar
, new_buffer
, new_len
);
975 const UChar
*new_ustr
= (UChar
*)CFStringGetCharactersPtr(formatString
);
976 if (NULL
== new_ustr
) {
977 CFStringGetCharacters(formatString
, CFRangeMake(0, new_len
), (UniChar
*)new_buffer
);
978 new_ustr
= new_buffer
;
980 status
= U_ZERO_ERROR
;
981 // __cficu_udat_applyPattern(formatter->_df, false, new_ustr, new_len, &status);
982 __cficu_udat_applyPattern(formatter
->_df
, false, new_ustr
, new_len
);
984 CFRelease(formatString
);
986 CFRelease(dateString
);
988 __cficu_udat_close(df
);
994 static void __CFDateFormatterStoreSymbolPrefs(const void *key
, const void *value
, void *context
) {
995 if (CFGetTypeID(key
) == CFStringGetTypeID() && CFGetTypeID(value
) == CFArrayGetTypeID()) {
996 CFDateFormatterRef formatter
= (CFDateFormatterRef
)context
;
997 UDateFormatSymbolType sym
= (UDateFormatSymbolType
)CFStringGetIntValue((CFStringRef
)key
);
998 CFArrayRef array
= (CFArrayRef
)value
;
999 CFIndex idx
, cnt
= CFArrayGetCount(array
);
1002 formatter
->_property
._CustomEraSymbols
= (CFArrayRef
)CFRetain(array
);
1005 formatter
->_property
._CustomMonthSymbols
= (CFArrayRef
)CFRetain(array
);
1007 case UDAT_SHORT_MONTHS
:
1008 formatter
->_property
._CustomShortMonthSymbols
= (CFArrayRef
)CFRetain(array
);
1011 formatter
->_property
._CustomWeekdaySymbols
= (CFArrayRef
)CFRetain(array
);
1013 case UDAT_SHORT_WEEKDAYS
:
1014 formatter
->_property
._CustomShortWeekdaySymbols
= (CFArrayRef
)CFRetain(array
);
1018 for (idx
= 0; idx
< cnt
; idx
++) {
1019 CFStringRef item
= (CFStringRef
)CFArrayGetValueAtIndex(array
, idx
);
1020 if (CFGetTypeID(item
) != CFStringGetTypeID()) continue;
1022 formatter
->_property
._CustomAMSymbol
= (CFStringRef
)CFRetain(item
);
1023 } else if (idx
== 1) {
1024 formatter
->_property
._CustomPMSymbol
= (CFStringRef
)CFRetain(item
);
1029 case UDAT_ERA_NAMES
:
1030 formatter
->_property
._CustomLongEraSymbols
= (CFArrayRef
)CFRetain(array
);
1032 case UDAT_NARROW_MONTHS
:
1033 formatter
->_property
._CustomVeryShortMonthSymbols
= (CFArrayRef
)CFRetain(array
);
1035 case UDAT_NARROW_WEEKDAYS
:
1036 formatter
->_property
._CustomVeryShortWeekdaySymbols
= (CFArrayRef
)CFRetain(array
);
1038 case UDAT_STANDALONE_MONTHS
:
1039 formatter
->_property
._CustomStandaloneMonthSymbols
= (CFArrayRef
)CFRetain(array
);
1041 case UDAT_STANDALONE_SHORT_MONTHS
:
1042 formatter
->_property
._CustomShortStandaloneMonthSymbols
= (CFArrayRef
)CFRetain(array
);
1044 case UDAT_STANDALONE_NARROW_MONTHS
:
1045 formatter
->_property
._CustomVeryShortStandaloneMonthSymbols
= (CFArrayRef
)CFRetain(array
);
1047 case UDAT_STANDALONE_WEEKDAYS
:
1048 formatter
->_property
._CustomStandaloneWeekdaySymbols
= (CFArrayRef
)CFRetain(array
);
1050 case UDAT_STANDALONE_SHORT_WEEKDAYS
:
1051 formatter
->_property
._CustomShortStandaloneWeekdaySymbols
= (CFArrayRef
)CFRetain(array
);
1053 case UDAT_STANDALONE_NARROW_WEEKDAYS
:
1054 formatter
->_property
._CustomVeryShortStandaloneWeekdaySymbols
= (CFArrayRef
)CFRetain(array
);
1057 formatter
->_property
._CustomQuarterSymbols
= (CFArrayRef
)CFRetain(array
);
1059 case UDAT_SHORT_QUARTERS
:
1060 formatter
->_property
._CustomShortQuarterSymbols
= (CFArrayRef
)CFRetain(array
);
1062 case UDAT_STANDALONE_QUARTERS
:
1063 formatter
->_property
._CustomStandaloneQuarterSymbols
= (CFArrayRef
)CFRetain(array
);
1065 case UDAT_STANDALONE_SHORT_QUARTERS
:
1066 formatter
->_property
._CustomShortStandaloneQuarterSymbols
= (CFArrayRef
)CFRetain(array
);
1074 static CFStringRef
__CFDateFormatterCreateForcedTemplate(CFLocaleRef locale
, CFStringRef inString
, Boolean stripAMPM
) {
1075 if (!inString
) return NULL
;
1076 Boolean doForce24
= false, doForce12
= false;
1077 CFDictionaryRef prefs
= __CFLocaleGetPrefs(locale
);
1078 CFPropertyListRef pref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUForce24HourTime")) : NULL
;
1079 if (NULL
!= pref
&& CFGetTypeID(pref
) == CFBooleanGetTypeID()) {
1080 doForce24
= CFBooleanGetValue((CFBooleanRef
)pref
);
1082 pref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUForce12HourTime")) : NULL
;
1083 if (NULL
!= pref
&& CFGetTypeID(pref
) == CFBooleanGetTypeID()) {
1084 doForce12
= CFBooleanGetValue((CFBooleanRef
)pref
);
1086 if (doForce24
) doForce12
= false; // if both are set, Force24 wins, period
1087 if (!doForce24
&& !doForce12
) return (CFStringRef
)CFRetain(inString
);
1089 CFMutableStringRef outString
= CFStringCreateMutable(kCFAllocatorSystemDefault
, 0);
1090 CFIndex cnt
= CFStringGetLength(inString
);
1091 CFIndex lastSecond
= -1, lastMinute
= -1, firstHour
= -1;
1092 Boolean isInQuote
= false, hasA
= false, had12Hour
= false, had24Hour
= false;
1093 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1094 Boolean emit
= true;
1095 UniChar ch
= CFStringGetCharacterAtIndex(inString
, idx
);
1097 case '\'': isInQuote
= !isInQuote
; break;
1098 case 'J': //fall through
1099 case 'j': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); if (doForce24
) ch
= 'H'; else ch
= 'h';} break;
1100 case 'h': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had12Hour
= true; if (doForce24
) ch
= 'H';} break; // switch 12-hour to 24-hour
1101 case 'K': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had12Hour
= true; if (doForce24
) ch
= 'k';} break; // switch 12-hour to 24-hour
1102 case 'H': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had24Hour
= true; if (doForce12
) ch
= 'h';} break; // switch 24-hour to 12-hour
1103 case 'k': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had24Hour
= true; if (doForce12
) ch
= 'K';} break; // switch 24-hour to 12-hour
1104 case 'm': if (!isInQuote
) lastMinute
= CFStringGetLength(outString
); break;
1105 case 's': if (!isInQuote
) lastSecond
= CFStringGetLength(outString
); break;
1106 case 'a': if (!isInQuote
) {hasA
= true; if (doForce24
|| stripAMPM
) emit
= false;} break;
1109 if (emit
) CFStringAppendCharacters(outString
, &ch
, 1);
1115 static CFStringRef
__CFDateFormatterCreateForcedString(CFDateFormatterRef formatter
, CFStringRef inString
) {
1116 if (!inString
) return NULL
;
1118 UDateTimePatternMatchOptions options
= UDATPG_MATCH_NO_OPTIONS
;
1120 if (formatter
->_property
._Custom12Hour
!= NULL
&& CFBooleanGetValue((CFBooleanRef
)formatter
->_property
._Custom12Hour
)) {
1121 options
= UADATPG_FORCE_12_HOUR_CYCLE
;
1123 if (formatter
->_property
._Custom24Hour
!= NULL
&& CFBooleanGetValue((CFBooleanRef
)formatter
->_property
._Custom24Hour
)) {
1124 options
= UADATPG_FORCE_24_HOUR_CYCLE
; //force 24 hour always wins if both are specified
1126 if (options
== UDATPG_MATCH_NO_OPTIONS
) return (CFStringRef
)CFRetain(inString
);
1128 static CFCharacterSetRef hourCharacters
;
1129 static dispatch_once_t onceToken
;
1130 dispatch_once(&onceToken
, ^{
1131 hourCharacters
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorSystemDefault
, CFSTR("hHkK"));
1134 CFRange hourRange
= CFRangeMake(kCFNotFound
, 0);
1135 if (!CFStringFindCharacterFromSet(inString
, hourCharacters
, CFRangeMake(0, CFStringGetLength(inString
)), 0, &hourRange
) || hourRange
.location
== kCFNotFound
) {
1136 return (CFStringRef
)CFRetain(inString
);
1138 __block CFStringRef result
= NULL
;
1139 __block
int32_t newPatternLen
= 0;
1140 Boolean success
= useTemplatePatternGenerator(formatter
->_locale
, ^(UDateTimePatternGenerator
*ptg
) {
1141 CFIndex cnt
= CFStringGetLength(inString
);
1142 STACK_BUFFER_DECL(UChar
, ubuffer
, cnt
);
1143 const UChar
*ustr
= (UChar
*)CFStringGetCharactersPtr(inString
);
1145 CFStringGetCharacters(inString
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
1148 STACK_BUFFER_DECL(UChar
, outBuffer
, 256);
1150 UErrorCode err
= U_ZERO_ERROR
;
1151 newPatternLen
= uadatpg_remapPatternWithOptions(ptg
, ustr
, cnt
, options
, outBuffer
, 256, &err
);
1152 if (U_SUCCESS(err
)) {
1153 result
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, outBuffer
, newPatternLen
);
1154 } else if (err
== U_BUFFER_OVERFLOW_ERROR
) {
1156 UChar
*largerBuffer
= calloc(newPatternLen
+ 1, sizeof(UChar
));
1157 newPatternLen
= uadatpg_remapPatternWithOptions(ptg
, ustr
, cnt
, options
, outBuffer
, newPatternLen
+ 1, &err
);
1158 if (U_SUCCESS(err
)) {
1159 result
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, largerBuffer
, newPatternLen
);
1164 return success
&& result
&& newPatternLen
> 0 ? result
: CFRetain(inString
);
1167 CFLocaleRef
CFDateFormatterGetLocale(CFDateFormatterRef formatter
) {
1168 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1169 return formatter
->_locale
;
1172 CFDateFormatterStyle
CFDateFormatterGetDateStyle(CFDateFormatterRef formatter
) {
1173 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1174 return formatter
->_dateStyle
;
1177 CFDateFormatterStyle
CFDateFormatterGetTimeStyle(CFDateFormatterRef formatter
) {
1178 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1179 return formatter
->_timeStyle
;
1182 CFStringRef
CFDateFormatterGetFormat(CFDateFormatterRef formatter
) {
1183 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1184 return formatter
->_format
;
1187 void CFDateFormatterSetFormat(CFDateFormatterRef formatter
, CFStringRef formatString
) {
1188 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1189 __CFGenericValidateType(formatString
, CFStringGetTypeID());
1190 formatString
= __CFDateFormatterCreateForcedString(formatter
, formatString
);
1191 CFIndex cnt
= CFStringGetLength(formatString
);
1192 CFAssert1(cnt
<= 1024, __kCFLogAssertion
, "%s(): format string too long", __PRETTY_FUNCTION__
);
1193 if (formatter
->_format
!= formatString
&& cnt
<= 1024) {
1194 // When going from a situation where there is no custom format already,
1195 // and the "relative date formatting" property is set, we need to reset
1196 // the whole UDateFormat.
1197 if (formatter
->_property
._HasCustomFormat
!= kCFBooleanTrue
&& formatter
->_property
._DoesRelativeDateFormatting
== kCFBooleanTrue
) {
1198 __ResetUDateFormat(formatter
, true);
1199 // the "true" results in: if you set a custom format string, you don't get relative date formatting
1201 STACK_BUFFER_DECL(UChar
, ubuffer
, cnt
);
1202 const UChar
*ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)formatString
);
1204 CFStringGetCharacters(formatString
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
1207 UErrorCode status
= U_ZERO_ERROR
;
1208 // __cficu_udat_applyPattern(formatter->_df, false, ustr, cnt, &status);
1209 __cficu_udat_applyPattern(formatter
->_df
, false, ustr
, cnt
);
1210 if (U_SUCCESS(status
)) {
1211 if (formatter
->_format
) CFRelease(formatter
->_format
);
1212 formatter
->_format
= (CFStringRef
)CFStringCreateCopy(CFGetAllocator(formatter
), formatString
);
1213 formatter
->_property
._HasCustomFormat
= kCFBooleanTrue
;
1216 if (formatString
) CFRelease(formatString
);
1219 CFStringRef
CFDateFormatterCreateStringWithDate(CFAllocatorRef allocator
, CFDateFormatterRef formatter
, CFDateRef date
) {
1220 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
1221 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
1222 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1223 __CFGenericValidateType(date
, CFDateGetTypeID());
1224 return CFDateFormatterCreateStringWithAbsoluteTime(allocator
, formatter
, CFDateGetAbsoluteTime(date
));
1227 CFStringRef
CFDateFormatterCreateStringWithAbsoluteTime(CFAllocatorRef allocator
, CFDateFormatterRef formatter
, CFAbsoluteTime at
) {
1228 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
1229 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
1230 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1231 UChar
*ustr
= NULL
, ubuffer
[BUFFER_SIZE
+ 1];
1232 UErrorCode status
= U_ZERO_ERROR
;
1233 CFIndex used
, cnt
= BUFFER_SIZE
;
1234 UDate ud
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0 + 0.5;
1235 used
= __cficu_udat_format(formatter
->_df
, ud
, ubuffer
+ 1, cnt
, NULL
, &status
);
1236 if (status
== U_BUFFER_OVERFLOW_ERROR
|| cnt
< used
) {
1237 cnt
= used
+ 1 + 1; // leave room for RTL marker if needed
1238 ustr
= (UChar
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(UChar
) * cnt
, 0);
1239 status
= U_ZERO_ERROR
;
1240 used
= __cficu_udat_format(formatter
->_df
, ud
, ustr
+ 1, cnt
, NULL
, &status
);
1242 CFStringRef string
= NULL
;
1243 if (U_SUCCESS(status
)) {
1244 UniChar
*bufferToUse
= ustr
? (UniChar
*)ustr
: (UniChar
*)ubuffer
;
1245 if (formatter
->_property
._UsesCharacterDirection
== kCFBooleanTrue
&& CFLocaleGetLanguageCharacterDirection(CFLocaleGetIdentifier(formatter
->_locale
)) == kCFLocaleLanguageDirectionRightToLeft
) {
1246 // Insert Unicode RTL marker
1247 bufferToUse
[0] = 0x200F;
1250 // Move past direction marker
1253 string
= CFStringCreateWithCharacters(allocator
, bufferToUse
, used
);
1255 if (ustr
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, ustr
);
1259 static UDate
__CFDateFormatterCorrectTimeWithTarget(UCalendar
*calendar
, UDate at
, int32_t target
, Boolean isEra
, UErrorCode
*status
) {
1260 __cficu_ucal_setMillis(calendar
, at
, status
);
1261 UCalendarDateFields field
= isEra
? UCAL_ERA
: UCAL_YEAR
;
1262 __cficu_ucal_set(calendar
, field
, target
);
1263 return __cficu_ucal_getMillis(calendar
, status
);
1266 static UDate
__CFDateFormatterCorrectTimeToARangeAroundCurrentDate(UCalendar
*calendar
, UDate at
, CFIndex period
, CFIndex pastYears
, CFIndex futureYears
, Boolean isEra
, UErrorCode
*status
) {
1267 __cficu_ucal_setMillis(calendar
, __cficu_ucal_getNow(), status
);
1268 int32_t currYear
= __cficu_ucal_get(calendar
, UCAL_YEAR
, status
);
1269 UCalendarDateFields field
= isEra
? UCAL_ERA
: UCAL_YEAR
;
1270 int32_t currEraOrCentury
= __cficu_ucal_get(calendar
, field
, status
);
1273 currEraOrCentury
= currEraOrCentury
/ 100 * 100; // get century
1276 CFIndex futureMax
= currYear
+ futureYears
;
1277 CFIndex pastMin
= currYear
- pastYears
;
1279 CFRange currRange
, futureRange
, pastRange
;
1280 currRange
.location
= futureRange
.location
= pastRange
.location
= kCFNotFound
;
1281 currRange
.length
= futureRange
.length
= pastRange
.length
= 0;
1283 if (period
< INT_MAX
&& futureMax
>= period
) {
1284 futureRange
.location
= 0;
1285 futureRange
.length
= futureMax
- period
+ 1;
1288 pastRange
.location
= period
+ pastMin
;
1289 pastRange
.length
= period
- pastRange
.location
;
1291 if (pastRange
.location
!= kCFNotFound
) {
1292 currRange
.location
= 0;
1294 currRange
.location
= pastMin
;
1297 if (period
< INT_MAX
&& futureMax
> period
) {
1298 futureRange
.location
= 1,
1299 futureRange
.length
= futureMax
- period
;
1302 pastRange
.location
= period
+ pastMin
;
1303 pastRange
.length
= period
- pastRange
.location
+ 1;
1305 if (pastRange
.location
!= kCFNotFound
) {
1306 currRange
.location
= 1;
1308 currRange
.location
= pastMin
;
1312 currRange
.length
= period
- pastRange
.length
- futureRange
.length
;
1314 __cficu_ucal_setMillis(calendar
, at
, status
);
1315 int32_t atYear
= __cficu_ucal_get(calendar
, UCAL_YEAR
, status
);
1318 currEraOrCentury
+= atYear
;
1321 int32_t offset
= 0; // current era or century
1322 if (pastRange
.location
!= kCFNotFound
&& atYear
>= pastRange
.location
&& atYear
- pastRange
.location
+ 1 <= pastRange
.length
) {
1323 offset
= -1; // past era or century
1324 } else if (futureRange
.location
!= kCFNotFound
&& atYear
>= futureRange
.location
&& atYear
- futureRange
.location
+ 1 <= futureRange
.length
) {
1325 offset
= 1; // next era or century
1327 if (!isEra
) offset
*= 100;
1328 return __CFDateFormatterCorrectTimeWithTarget(calendar
, at
, currEraOrCentury
+offset
, isEra
, status
);
1331 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
1332 static int32_t __CFDateFormatterGetMaxYearGivenJapaneseEra(UCalendar
*calendar
, int32_t era
, UErrorCode
*status
) {
1334 __cficu_ucal_clear(calendar
);
1335 __cficu_ucal_set(calendar
, UCAL_ERA
, era
+1);
1336 UDate target
= __cficu_ucal_getMillis(calendar
, status
);
1337 __cficu_ucal_set(calendar
, UCAL_ERA
, era
);
1338 years
= __cficu_ucal_getFieldDifference(calendar
, target
, UCAL_YEAR
, status
);
1343 static Boolean
__CFDateFormatterHandleAmbiguousYear(CFDateFormatterRef formatter
, CFStringRef calendar_id
, UDateFormat
*df
, UCalendar
*cal
, UDate
*at
, const UChar
*ustr
, CFIndex length
, UErrorCode
*status
) {
1344 Boolean success
= true;
1345 int64_t ambigStrat
= kCFDateFormatterAmbiguousYearAssumeToNone
;
1346 if (formatter
->_property
._AmbiguousYearStrategy
) {
1347 CFNumberGetValue(formatter
->_property
._AmbiguousYearStrategy
, kCFNumberSInt64Type
, &ambigStrat
);
1349 if (calendar_id
== kCFCalendarIdentifierChinese
) {
1350 // we default to era 1 if era is missing, however, we cannot just test if the era is 1 becuase we may get era 2 or larger if the year in the string is greater than 60
1351 // now I just assume that the year will not be greater than 600 in the string
1352 if (__cficu_ucal_get(cal
, UCAL_ERA
, status
) < 10) {
1353 switch (ambigStrat
) {
1354 case kCFDateFormatterAmbiguousYearFailToParse
:
1357 case kCFDateFormatterAmbiguousYearAssumeToCurrent
: {
1358 __cficu_ucal_setMillis(cal
, __cficu_ucal_getNow(), status
);
1359 int32_t currEra
= __cficu_ucal_get(cal
, UCAL_ERA
, status
);
1360 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1363 case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate
:
1364 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 60, 29, 30, true, status
);
1366 case kCFDateFormatterAmbiguousYearAssumeToFuture
:
1367 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 60, 0, 59, true, status
);
1369 case kCFDateFormatterAmbiguousYearAssumeToPast
:
1370 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 60, 59, 0, true, status
);
1372 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture
:
1373 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 60, 10, 49, true, status
);
1375 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast
:
1376 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 60, 49, 10, true, status
);
1378 case kCFDateFormatterAmbiguousYearAssumeToNone
:
1380 break; // do nothing
1383 } else if (calendar_id
== kCFCalendarIdentifierJapanese
) { // ??? need more work
1384 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
1385 __cficu_ucal_clear(cal
);
1386 __cficu_ucal_set(cal
, UCAL_ERA
, 1);
1387 __cficu_udat_parseCalendar(df
, cal
, ustr
, length
, NULL
, status
);
1388 UDate test
= __cficu_ucal_getMillis(cal
, status
);
1389 if (test
!= *at
) { // missing era
1390 __cficu_ucal_setMillis(cal
, *at
, status
);
1391 int32_t givenYear
= __cficu_ucal_get(cal
, UCAL_YEAR
, status
);
1392 __cficu_ucal_setMillis(cal
, __cficu_ucal_getNow(), status
);
1393 int32_t currYear
= __cficu_ucal_get(cal
, UCAL_YEAR
, status
);
1394 int32_t currEra
= __cficu_ucal_get(cal
, UCAL_ERA
, status
);
1395 switch (ambigStrat
) {
1396 case kCFDateFormatterAmbiguousYearFailToParse
:
1399 case kCFDateFormatterAmbiguousYearAssumeToCurrent
:
1400 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1402 case kCFDateFormatterAmbiguousYearAssumeToFuture
:
1403 if (givenYear
< currYear
) { // we only consider current or the future
1405 } else { // current era
1406 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1409 case kCFDateFormatterAmbiguousYearAssumeToPast
:
1410 if (givenYear
> currYear
) { // past era
1412 // we find the closest era that has the given year
1413 // if no era has such given year, we fail the parse
1414 for (CFIndex era
= currEra
-1; era
>= 234; era
--) { // Showa era (234) is the longest era
1415 int32_t years
= __CFDateFormatterGetMaxYearGivenJapaneseEra(cal
, era
, status
);
1416 if (givenYear
> years
) {
1420 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, era
, true, status
);
1423 } else { // current era
1424 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1427 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture
:
1428 if (givenYear
< currYear
- 10) { // we allow 10 years to the past
1431 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1434 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast
:
1435 if (givenYear
> currYear
+ 10) {
1437 // we find the closest era that has the given year
1438 // if no era has such given year, we fail the parse
1439 for (CFIndex era
= currEra
-1; era
>= 234; era
--) { // Showa era (234) is the longest era
1440 int32_t years
= __CFDateFormatterGetMaxYearGivenJapaneseEra(cal
, era
, status
);
1441 if (givenYear
> years
) {
1445 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, era
, true, status
);
1448 } else { // current era
1449 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1452 case kCFDateFormatterAmbiguousYearAssumeToNone
:
1454 break; // do nothing
1460 } else { // calenders other than chinese and japanese
1461 int32_t parsedYear
= __cficu_ucal_get(cal
, UCAL_YEAR
, status
);
1462 if (parsedYear
>= 12000 && parsedYear
<= 12099) { // most likely that the parsed string had a 2-digits year
1463 if (formatter
->_property
._TwoDigitStartDate
!= NULL
) {
1464 UCalendar
*tempCal
= __cficu_ucal_clone(cal
, status
);
1465 __cficu_ucal_clear(tempCal
);
1466 CFAbsoluteTime twoDigitAt
= CFDateGetAbsoluteTime(formatter
->_property
._TwoDigitStartDate
);
1467 UDate targetUdate
= (twoDigitAt
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
1468 __cficu_ucal_setMillis(tempCal
, targetUdate
, status
);
1469 int targetYear
= __cficu_ucal_get(tempCal
, UCAL_YEAR
, status
);
1470 parsedYear
-= 12000;
1471 int targetYearM100
= targetYear
% 100;
1472 if (targetYearM100
< parsedYear
) {
1473 parsedYear
= ((targetYear
/ 100) * 100) + parsedYear
;
1474 } else if (parsedYear
< targetYearM100
) {
1475 parsedYear
= ((targetYear
/ 100) * 100) + 100 + parsedYear
;
1477 __cficu_ucal_set(cal
, UCAL_YEAR
, targetYear
);
1478 UDate parseUdate
= __cficu_ucal_getMillis(cal
, status
);
1479 if (parseUdate
>= targetUdate
) {
1480 parsedYear
= targetYear
;
1482 parsedYear
= targetYear
+ 100;
1485 __cficu_ucal_close(tempCal
);
1486 __cficu_ucal_set(cal
, UCAL_YEAR
, parsedYear
);
1487 *at
= __cficu_ucal_getMillis(cal
, status
);
1489 switch (ambigStrat
) {
1490 case kCFDateFormatterAmbiguousYearFailToParse
:
1493 case kCFDateFormatterAmbiguousYearAssumeToCurrent
:
1495 // we can modify cal here because cal is just a temp cal from the caller
1496 __cficu_ucal_setMillis(cal
, __cficu_ucal_getNow(), status
);
1497 int32_t currYear
= __cficu_ucal_get(cal
, UCAL_YEAR
, status
);
1498 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, (currYear
/ 100 * 100) + parsedYear
% 100, false, status
);
1501 case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate
:
1502 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 100, 50, 49, false, status
);
1504 case kCFDateFormatterAmbiguousYearAssumeToFuture
:
1505 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 100, 0, 99, false, status
);
1507 case kCFDateFormatterAmbiguousYearAssumeToPast
:
1508 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 100, 99, 0, false, status
);
1510 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture
:
1511 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 100, 9, 90, false, status
);
1513 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast
:
1514 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 100, 90, 9, false, status
);
1516 case kCFDateFormatterAmbiguousYearAssumeToNone
:
1518 if (calendar_id
== kCFCalendarIdentifierGregorian
) { // historical default behavior of 1950 - 2049
1519 int32_t twoDigits
= parsedYear
% 100;
1520 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, ((twoDigits
< 50) ? 2000 : 1900) + twoDigits
, false, status
);
1522 break; // do nothing
1531 CFDateRef
CFDateFormatterCreateDateFromString(CFAllocatorRef allocator
, CFDateFormatterRef formatter
, CFStringRef string
, CFRange
*rangep
) {
1532 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
1533 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
1534 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1535 __CFGenericValidateType(string
, CFStringGetTypeID());
1537 if (CFDateFormatterGetAbsoluteTimeFromString(formatter
, string
, rangep
, &at
)) {
1538 return CFDateCreate(allocator
, at
);
1543 Boolean
CFDateFormatterGetAbsoluteTimeFromString(CFDateFormatterRef formatter
, CFStringRef string
, CFRange
*rangep
, CFAbsoluteTime
*atp
) {
1544 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1545 __CFGenericValidateType(string
, CFStringGetTypeID());
1546 CFRange range
= {0, 0};
1550 range
.length
= CFStringGetLength(string
);
1552 if (1024 < range
.length
) range
.length
= 1024;
1553 const UChar
*ustr
= (UChar
*)CFStringGetCharactersPtr(string
);
1554 STACK_BUFFER_DECL(UChar
, ubuffer
, (NULL
== ustr
) ? range
.length
: 1);
1556 CFStringGetCharacters(string
, range
, (UniChar
*)ubuffer
);
1559 ustr
+= range
.location
;
1563 UErrorCode status
= U_ZERO_ERROR
;
1564 UDateFormat
*df2
= __cficu_udat_clone(formatter
->_df
, &status
);
1565 const UCalendar
*ucal2
= __cficu_udat_getCalendar(df2
);
1566 UCalendar
*cal2
= __cficu_ucal_clone(ucal2
, &status
);
1567 CFStringRef calendar_id
= (CFStringRef
) CFDateFormatterCopyProperty(formatter
, kCFDateFormatterCalendarIdentifierKey
);
1568 if (calendar_id
!= kCFCalendarIdentifierChinese
&& calendar_id
!= kCFCalendarIdentifierJapanese
) {
1569 __cficu_ucal_clear(cal2
);
1570 // set both year, and 2DigitYearStart to year 12000
1571 __cficu_ucal_set(cal2
, UCAL_YEAR
, 12000);
1572 __cficu_udat_set2DigitYearStart(df2
, 316516204800.0 * 1000.0, &status
);
1573 } else if (calendar_id
== kCFCalendarIdentifierChinese
) {
1574 __cficu_ucal_clear(cal2
);
1575 __cficu_ucal_set(cal2
, UCAL_ERA
, 1); // default to era 1 if no era info in the string for chinese
1576 } else if (calendar_id
== kCFCalendarIdentifierJapanese
) { // default to the current era
1577 __cficu_ucal_setMillis(cal2
, __cficu_ucal_getNow(), &status
);
1578 int32_t currEra
= __cficu_ucal_get(cal2
, UCAL_ERA
, &status
);
1579 __cficu_ucal_clear(cal2
);
1580 __cficu_ucal_set(cal2
, UCAL_ERA
, currEra
);
1582 if (formatter
->_property
._DefaultDate
) {
1583 CFAbsoluteTime at
= CFDateGetAbsoluteTime(formatter
->_property
._DefaultDate
);
1584 udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
1585 __cficu_ucal_setMillis(cal2
, udate
, &status
);
1587 __cficu_udat_parseCalendar(df2
, cal2
, ustr
, range
.length
, &dpos
, &status
);
1588 udate
= __cficu_ucal_getMillis(cal2
, &status
);
1589 if (rangep
) rangep
->length
= dpos
;
1590 Boolean success
= false;
1591 // first status check is for parsing and the second status check is for the work done inside __CFDateFormatterHandleAmbiguousYear()
1592 if (!U_FAILURE(status
) && (__CFDateFormatterHandleAmbiguousYear(formatter
, calendar_id
, df2
, cal2
, &udate
, ustr
, range
.length
, &status
)) && !U_FAILURE(status
)) {
1594 *atp
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
1598 CFRelease(calendar_id
);
1599 __cficu_udat_close(df2
);
1600 __cficu_ucal_close(cal2
);
1604 static void __CFDateFormatterSetSymbolsArray(UDateFormat
*icudf
, int32_t icucode
, int index_base
, CFTypeRef value
) {
1605 UErrorCode status
= U_ZERO_ERROR
;
1606 __CFGenericValidateType(value
, CFArrayGetTypeID());
1607 CFArrayRef array
= (CFArrayRef
)value
;
1608 CFIndex idx
, cnt
= CFArrayGetCount(array
);
1609 for (idx
= 0; idx
< cnt
; idx
++) {
1610 CFStringRef item
= (CFStringRef
)CFArrayGetValueAtIndex(array
, idx
);
1611 __CFGenericValidateType(item
, CFStringGetTypeID());
1612 CFIndex item_cnt
= CFStringGetLength(item
);
1613 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
1614 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr(item
);
1615 if (NULL
== item_ustr
) {
1616 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
1617 CFStringGetCharacters(item
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
1618 item_ustr
= item_buffer
;
1620 status
= U_ZERO_ERROR
;
1621 __cficu_udat_setSymbols(icudf
, (UDateFormatSymbolType
)icucode
, idx
+ index_base
, item_ustr
, item_cnt
, &status
);
1625 static CFArrayRef
__CFDateFormatterGetSymbolsArray(UDateFormat
*icudf
, int32_t icucode
, int index_base
) {
1626 UErrorCode status
= U_ZERO_ERROR
;
1627 CFIndex idx
, cnt
= __cficu_udat_countSymbols(icudf
, (UDateFormatSymbolType
)icucode
);
1628 if (cnt
<= index_base
) return CFArrayCreate(kCFAllocatorSystemDefault
, NULL
, 0, &kCFTypeArrayCallBacks
);
1629 cnt
= cnt
- index_base
;
1630 STACK_BUFFER_DECL(CFStringRef
, strings
, cnt
);
1631 for (idx
= 0; idx
< cnt
; idx
++) {
1632 UChar ubuffer
[BUFFER_SIZE
];
1633 CFStringRef str
= NULL
;
1634 status
= U_ZERO_ERROR
;
1635 CFIndex ucnt
= __cficu_udat_getSymbols(icudf
, (UDateFormatSymbolType
)icucode
, idx
+ index_base
, ubuffer
, BUFFER_SIZE
, &status
);
1636 if (U_SUCCESS(status
) && cnt
<= BUFFER_SIZE
) {
1637 str
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)ubuffer
, ucnt
);
1639 strings
[idx
] = !str
? (CFStringRef
)CFRetain(CFSTR("<error>")) : str
;
1641 CFArrayRef array
= CFArrayCreate(kCFAllocatorSystemDefault
, (const void **)strings
, cnt
, &kCFTypeArrayCallBacks
);
1643 CFRelease(strings
[cnt
]);
1648 #define SET_SYMBOLS_ARRAY(A, B, C) \
1649 if (!directToICU) { \
1650 oldProperty = formatter->_property. C; \
1651 formatter->_property. C = NULL; \
1653 __CFDateFormatterSetSymbolsArray(formatter->_df, A, B, value); \
1654 if (!directToICU) { \
1655 formatter->_property. C = __CFDateFormatterGetSymbolsArray(formatter->_df, A, B); \
1658 static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter
, CFStringRef key
, CFTypeRef value
, Boolean directToICU
) {
1659 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1660 __CFGenericValidateType(key
, CFStringGetTypeID());
1661 CFTypeRef oldProperty
= NULL
;
1662 UErrorCode status
= U_ZERO_ERROR
;
1664 if (kCFDateFormatterIsLenientKey
== key
) {
1666 oldProperty
= formatter
->_property
. _IsLenient
;
1667 formatter
->_property
. _IsLenient
= NULL
;
1669 __CFGenericValidateType(value
, CFBooleanGetTypeID());
1671 formatter
->_property
. _IsLenient
= value
? (CFBooleanRef
)CFRetain(value
) : NULL
;
1672 __ResetUDateFormat(formatter
, false);
1674 } else if (kCFDateFormatterDoesRelativeDateFormattingKey
== key
) {
1676 oldProperty
= formatter
->_property
. _DoesRelativeDateFormatting
;
1677 formatter
->_property
. _DoesRelativeDateFormatting
= NULL
;
1679 __CFGenericValidateType(value
, CFBooleanGetTypeID());
1681 if (kCFBooleanTrue
!= value
) value
= kCFBooleanFalse
;
1682 formatter
->_property
. _DoesRelativeDateFormatting
= value
? (CFBooleanRef
)CFRetain(value
) : NULL
;
1683 __ResetUDateFormat(formatter
, false);
1685 } else if (kCFDateFormatterCalendarKey
== key
) {
1687 oldProperty
= formatter
->_property
. _Calendar
;
1688 formatter
->_property
. _Calendar
= NULL
;
1690 __CFGenericValidateType(value
, CFCalendarGetTypeID());
1691 CFStringRef localeName
= CFLocaleGetIdentifier(formatter
->_locale
);
1692 CFDictionaryRef components
= CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault
, localeName
);
1693 CFMutableDictionaryRef mcomponents
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, 0, components
);
1694 CFDictionarySetValue(mcomponents
, kCFLocaleCalendarIdentifierKey
, CFCalendarGetIdentifier((CFCalendarRef
)value
));
1695 localeName
= CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault
, mcomponents
);
1696 CFRelease(mcomponents
);
1697 CFRelease(components
);
1698 CFLocaleRef newLocale
= CFLocaleCreate(CFGetAllocator(formatter
->_locale
), localeName
);
1699 // at this point, we should be setting the preferences if any into this new locale
1700 CFRelease(localeName
);
1701 CFRelease(formatter
->_locale
);
1702 formatter
->_locale
= newLocale
;
1704 formatter
->_property
. _Calendar
= (CFCalendarRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterCalendarKey
);
1705 __ResetUDateFormat(formatter
, false);
1707 } else if (kCFDateFormatterCalendarIdentifierKey
== key
) {
1709 oldProperty
= formatter
->_property
. _CalendarName
;
1710 formatter
->_property
. _CalendarName
= NULL
;
1712 __CFGenericValidateType(value
, CFStringGetTypeID());
1713 CFStringRef localeName
= CFLocaleGetIdentifier(formatter
->_locale
);
1714 CFDictionaryRef components
= CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault
, localeName
);
1715 CFMutableDictionaryRef mcomponents
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, 0, components
);
1716 CFDictionarySetValue(mcomponents
, kCFLocaleCalendarIdentifierKey
, value
);
1717 localeName
= CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault
, mcomponents
);
1718 CFRelease(mcomponents
);
1719 CFRelease(components
);
1720 CFLocaleRef newLocale
= CFLocaleCreate(CFGetAllocator(formatter
->_locale
), localeName
);
1721 // at this point, we should be setting the preferences if any into this new locale
1722 CFRelease(localeName
);
1723 CFRelease(formatter
->_locale
);
1724 formatter
->_locale
= newLocale
;
1726 formatter
->_property
. _CalendarName
= (CFStringRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterCalendarIdentifierKey
);
1727 __ResetUDateFormat(formatter
, false);
1729 } else if (kCFDateFormatterTimeZoneKey
== key
) {
1730 if (formatter
->_property
. _TimeZone
!= value
) {
1732 oldProperty
= formatter
->_property
. _TimeZone
;
1733 formatter
->_property
. _TimeZone
= NULL
;
1735 __CFGenericValidateType(value
, CFTimeZoneGetTypeID());
1736 CFTimeZoneRef old
= formatter
->_property
._TimeZone
;
1737 formatter
->_property
._TimeZone
= value
? (CFTimeZoneRef
)CFRetain(value
) : CFTimeZoneCopyDefault();
1738 if (old
) CFRelease(old
);
1740 old
= formatter
->_property
._TimeZone
;
1741 formatter
->_property
. _TimeZone
= (CFTimeZoneRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterTimeZoneKey
);
1742 __ResetUDateFormat(formatter
, false);
1743 if (old
) CFRelease(old
);
1746 } else if (kCFDateFormatterDefaultFormatKey
== key
) {
1747 // read-only attribute
1748 } else if (kCFDateFormatterTwoDigitStartDateKey
== key
) {
1750 oldProperty
= formatter
->_property
. _TwoDigitStartDate
;
1751 formatter
->_property
. _TwoDigitStartDate
= NULL
;
1753 __CFGenericValidateType(value
, CFDateGetTypeID());
1755 formatter
->_property
. _TwoDigitStartDate
= value
? (CFDateRef
)CFRetain(value
) : NULL
;
1757 } else if (kCFDateFormatterDefaultDateKey
== key
) {
1759 oldProperty
= formatter
->_property
. _DefaultDate
;
1760 formatter
->_property
. _DefaultDate
= NULL
;
1762 __CFGenericValidateType(value
, CFDateGetTypeID());
1764 formatter
->_property
._DefaultDate
= value
? (CFDateRef
)CFRetain(value
) : NULL
;
1766 } else if (kCFDateFormatterGregorianStartDateKey
== key
) {
1768 oldProperty
= formatter
->_property
. _GregorianStartDate
;
1769 formatter
->_property
. _GregorianStartDate
= NULL
;
1771 __CFGenericValidateType(value
, CFDateGetTypeID());
1773 formatter
->_property
. _GregorianStartDate
= value
? (CFDateRef
)CFRetain(value
) : NULL
;
1774 __ResetUDateFormat(formatter
, false);
1776 } else if (kCFDateFormatterEraSymbolsKey
== key
) {
1777 SET_SYMBOLS_ARRAY(UDAT_ERAS
, 0, _EraSymbols
)
1778 } else if (kCFDateFormatterLongEraSymbolsKey
== key
) {
1779 SET_SYMBOLS_ARRAY(UDAT_ERA_NAMES
, 0, _LongEraSymbols
)
1780 } else if (kCFDateFormatterMonthSymbolsKey
== key
) {
1781 SET_SYMBOLS_ARRAY(UDAT_MONTHS
, 0, _MonthSymbols
)
1782 } else if (kCFDateFormatterShortMonthSymbolsKey
== key
) {
1783 SET_SYMBOLS_ARRAY(UDAT_SHORT_MONTHS
, 0, _ShortMonthSymbols
)
1784 } else if (kCFDateFormatterVeryShortMonthSymbolsKey
== key
) {
1785 SET_SYMBOLS_ARRAY(UDAT_NARROW_MONTHS
, 0, _VeryShortMonthSymbols
)
1786 } else if (kCFDateFormatterStandaloneMonthSymbolsKey
== key
) {
1787 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_MONTHS
, 0, _StandaloneMonthSymbols
)
1788 } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey
== key
) {
1789 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_MONTHS
, 0, _ShortStandaloneMonthSymbols
)
1790 } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey
== key
) {
1791 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_MONTHS
, 0, _VeryShortStandaloneMonthSymbols
)
1792 } else if (kCFDateFormatterWeekdaySymbolsKey
== key
) {
1793 SET_SYMBOLS_ARRAY(UDAT_WEEKDAYS
, 1, _WeekdaySymbols
)
1794 } else if (kCFDateFormatterShortWeekdaySymbolsKey
== key
) {
1795 SET_SYMBOLS_ARRAY(UDAT_SHORT_WEEKDAYS
, 1, _ShortWeekdaySymbols
)
1796 } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey
== key
) {
1797 SET_SYMBOLS_ARRAY(UDAT_NARROW_WEEKDAYS
, 1, _VeryShortWeekdaySymbols
)
1798 } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey
== key
) {
1799 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_WEEKDAYS
, 1, _StandaloneWeekdaySymbols
)
1800 } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey
== key
) {
1801 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_WEEKDAYS
, 1, _ShortStandaloneWeekdaySymbols
)
1802 } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey
== key
) {
1803 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_WEEKDAYS
, 1, _VeryShortStandaloneWeekdaySymbols
)
1804 } else if (kCFDateFormatterQuarterSymbolsKey
== key
) {
1805 SET_SYMBOLS_ARRAY(UDAT_QUARTERS
, 0, _QuarterSymbols
)
1806 } else if (kCFDateFormatterShortQuarterSymbolsKey
== key
) {
1807 SET_SYMBOLS_ARRAY(UDAT_SHORT_QUARTERS
, 0, _ShortQuarterSymbols
)
1808 } else if (kCFDateFormatterStandaloneQuarterSymbolsKey
== key
) {
1809 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_QUARTERS
, 0, _StandaloneQuarterSymbols
)
1810 } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey
== key
) {
1811 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_QUARTERS
, 0, _ShortStandaloneQuarterSymbols
)
1812 } else if (kCFDateFormatterAMSymbolKey
== key
) {
1814 oldProperty
= formatter
->_property
. _AMSymbol
;
1815 formatter
->_property
. _AMSymbol
= NULL
;
1817 __CFGenericValidateType(value
, CFStringGetTypeID());
1818 CFIndex item_cnt
= CFStringGetLength((CFStringRef
)value
);
1819 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
1820 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)value
);
1821 if (NULL
== item_ustr
) {
1822 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
1823 CFStringGetCharacters((CFStringRef
)value
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
1824 item_ustr
= item_buffer
;
1826 __cficu_udat_setSymbols(formatter
->_df
, UDAT_AM_PMS
, 0, item_ustr
, item_cnt
, &status
);
1828 formatter
->_property
. _AMSymbol
= value
? (CFStringRef
)CFStringCreateCopy(NULL
, value
) : NULL
;
1830 } else if (kCFDateFormatterPMSymbolKey
== key
) {
1832 oldProperty
= formatter
->_property
. _PMSymbol
;
1833 formatter
->_property
. _PMSymbol
= NULL
;
1835 __CFGenericValidateType(value
, CFStringGetTypeID());
1836 CFIndex item_cnt
= CFStringGetLength((CFStringRef
)value
);
1837 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
1838 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)value
);
1839 if (NULL
== item_ustr
) {
1840 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
1841 CFStringGetCharacters((CFStringRef
)value
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
1842 item_ustr
= item_buffer
;
1844 __cficu_udat_setSymbols(formatter
->_df
, UDAT_AM_PMS
, 1, item_ustr
, item_cnt
, &status
);
1846 formatter
->_property
. _PMSymbol
= value
? (CFStringRef
)CFStringCreateCopy(NULL
, value
) : NULL
;
1848 } else if (kCFDateFormatterAmbiguousYearStrategyKey
== key
) {
1849 oldProperty
= formatter
->_property
._AmbiguousYearStrategy
;
1850 formatter
->_property
._AmbiguousYearStrategy
= NULL
;
1851 __CFGenericValidateType(value
, CFNumberGetTypeID());
1852 formatter
->_property
._AmbiguousYearStrategy
= (CFNumberRef
)CFRetain(value
);
1853 } else if (kCFDateFormatterUsesCharacterDirectionKey
== key
) {
1854 __CFGenericValidateType(value
, CFBooleanGetTypeID());
1855 oldProperty
= formatter
->_property
._UsesCharacterDirection
;
1856 formatter
->_property
._UsesCharacterDirection
= (CFBooleanRef
)CFRetain(value
);
1857 } else if (CFEqual(key
, kCFDateFormatterFormattingContextKey
)) {
1859 oldProperty
= formatter
->_property
. _FormattingContext
;
1860 formatter
->_property
._FormattingContext
= NULL
;
1862 __CFGenericValidateType(value
, CFNumberGetTypeID());
1864 CFNumberGetValue(value
, kCFNumberIntType
, &context
);
1865 __cficu_udat_setContext(formatter
->_df
, context
, &status
);
1867 formatter
->_property
._FormattingContext
= (CFNumberRef
)CFRetain(value
);
1870 CFAssert3(0, __kCFLogAssertion
, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__
, key
, key
);
1872 if (oldProperty
) CFRelease(oldProperty
);
1875 void CFDateFormatterSetProperty(CFDateFormatterRef formatter
, CFStringRef key
, CFTypeRef value
) {
1876 __CFDateFormatterSetProperty(formatter
, key
, value
, false);
1879 CFTypeRef
CFDateFormatterCopyProperty(CFDateFormatterRef formatter
, CFStringRef key
) {
1880 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1881 __CFGenericValidateType(key
, CFStringGetTypeID());
1882 UErrorCode status
= U_ZERO_ERROR
;
1883 UChar ubuffer
[BUFFER_SIZE
];
1885 if (kCFDateFormatterIsLenientKey
== key
) {
1886 if (formatter
->_property
._IsLenient
) return CFRetain(formatter
->_property
._IsLenient
);
1887 return CFRetain(__cficu_udat_isLenient(formatter
->_df
) ? kCFBooleanTrue
: kCFBooleanFalse
);
1888 } else if (kCFDateFormatterDoesRelativeDateFormattingKey
== key
) {
1889 if (formatter
->_property
._DoesRelativeDateFormatting
) return CFRetain(formatter
->_property
._DoesRelativeDateFormatting
);
1890 return CFRetain(kCFBooleanFalse
);
1891 } else if (kCFDateFormatterCalendarKey
== key
) {
1892 if (formatter
->_property
._Calendar
) return CFRetain(formatter
->_property
._Calendar
);
1893 CFCalendarRef calendar
= (CFCalendarRef
)CFLocaleGetValue(formatter
->_locale
, kCFLocaleCalendarKey
);
1894 return calendar
? CFRetain(calendar
) : NULL
;
1895 } else if (kCFDateFormatterCalendarIdentifierKey
== key
) {
1896 if (formatter
->_property
._CalendarName
) return CFRetain(formatter
->_property
._CalendarName
);
1897 CFStringRef ident
= (CFStringRef
)CFLocaleGetValue(formatter
->_locale
, kCFLocaleCalendarIdentifierKey
);
1898 return ident
? CFRetain(ident
) : NULL
;
1899 } else if (kCFDateFormatterTimeZoneKey
== key
) {
1900 return formatter
->_property
._TimeZone
? CFRetain(formatter
->_property
._TimeZone
) : NULL
;
1901 } else if (kCFDateFormatterDefaultFormatKey
== key
) {
1902 return formatter
->_defformat
? CFRetain(formatter
->_defformat
) : NULL
;
1903 } else if (kCFDateFormatterTwoDigitStartDateKey
== key
) {
1904 return formatter
->_property
._TwoDigitStartDate
? CFRetain(formatter
->_property
._TwoDigitStartDate
) : NULL
;
1905 } else if (kCFDateFormatterDefaultDateKey
== key
) {
1906 return formatter
->_property
._DefaultDate
? CFRetain(formatter
->_property
._DefaultDate
) : NULL
;
1907 } else if (kCFDateFormatterGregorianStartDateKey
== key
) {
1908 if (formatter
->_property
._GregorianStartDate
) return CFRetain(formatter
->_property
._GregorianStartDate
);
1909 const UCalendar
*cal
= __cficu_udat_getCalendar(formatter
->_df
);
1910 UDate udate
= __cficu_ucal_getGregorianChange(cal
, &status
);
1911 if (U_SUCCESS(status
)) {
1912 CFAbsoluteTime at
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
1913 return CFDateCreate(CFGetAllocator(formatter
), at
);
1915 } else if (kCFDateFormatterEraSymbolsKey
== key
) {
1916 if (formatter
->_property
._EraSymbols
) return CFRetain(formatter
->_property
._EraSymbols
);
1917 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_ERAS
, 0);
1918 } else if (kCFDateFormatterLongEraSymbolsKey
== key
) {
1919 if (formatter
->_property
._LongEraSymbols
) return CFRetain(formatter
->_property
._LongEraSymbols
);
1920 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_ERA_NAMES
, 0);
1921 } else if (kCFDateFormatterMonthSymbolsKey
== key
) {
1922 if (formatter
->_property
._MonthSymbols
) return CFRetain(formatter
->_property
._MonthSymbols
);
1923 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_MONTHS
, 0);
1924 } else if (kCFDateFormatterShortMonthSymbolsKey
== key
) {
1925 if (formatter
->_property
._ShortMonthSymbols
) return CFRetain(formatter
->_property
._ShortMonthSymbols
);
1926 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_SHORT_MONTHS
, 0);
1927 } else if (kCFDateFormatterVeryShortMonthSymbolsKey
== key
) {
1928 if (formatter
->_property
._VeryShortMonthSymbols
) return CFRetain(formatter
->_property
._VeryShortMonthSymbols
);
1929 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_NARROW_MONTHS
, 0);
1930 } else if (kCFDateFormatterStandaloneMonthSymbolsKey
== key
) {
1931 if (formatter
->_property
._StandaloneMonthSymbols
) return CFRetain(formatter
->_property
._StandaloneMonthSymbols
);
1932 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_MONTHS
, 0);
1933 } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey
== key
) {
1934 if (formatter
->_property
._ShortStandaloneMonthSymbols
) return CFRetain(formatter
->_property
._ShortStandaloneMonthSymbols
);
1935 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_SHORT_MONTHS
, 0);
1936 } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey
== key
) {
1937 if (formatter
->_property
._VeryShortStandaloneMonthSymbols
) return CFRetain(formatter
->_property
._VeryShortStandaloneMonthSymbols
);
1938 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_NARROW_MONTHS
, 0);
1939 } else if (kCFDateFormatterWeekdaySymbolsKey
== key
) {
1940 if (formatter
->_property
._WeekdaySymbols
) return CFRetain(formatter
->_property
._WeekdaySymbols
);
1941 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_WEEKDAYS
, 1);
1942 } else if (kCFDateFormatterShortWeekdaySymbolsKey
== key
) {
1943 if (formatter
->_property
._ShortWeekdaySymbols
) return CFRetain(formatter
->_property
._ShortWeekdaySymbols
);
1944 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_SHORT_WEEKDAYS
, 1);
1945 } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey
== key
) {
1946 if (formatter
->_property
._VeryShortWeekdaySymbols
) return CFRetain(formatter
->_property
._VeryShortWeekdaySymbols
);
1947 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_NARROW_WEEKDAYS
, 1);
1948 } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey
== key
) {
1949 if (formatter
->_property
._StandaloneWeekdaySymbols
) return CFRetain(formatter
->_property
._StandaloneWeekdaySymbols
);
1950 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_WEEKDAYS
, 1);
1951 } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey
== key
) {
1952 if (formatter
->_property
._ShortStandaloneWeekdaySymbols
) return CFRetain(formatter
->_property
._ShortStandaloneWeekdaySymbols
);
1953 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_SHORT_WEEKDAYS
, 1);
1954 } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey
== key
) {
1955 if (formatter
->_property
._VeryShortStandaloneWeekdaySymbols
) return CFRetain(formatter
->_property
._VeryShortStandaloneWeekdaySymbols
);
1956 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_NARROW_WEEKDAYS
, 1);
1957 } else if (kCFDateFormatterQuarterSymbolsKey
== key
) {
1958 if (formatter
->_property
._QuarterSymbols
) return CFRetain(formatter
->_property
._QuarterSymbols
);
1959 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_QUARTERS
, 0);
1960 } else if (kCFDateFormatterShortQuarterSymbolsKey
== key
) {
1961 if (formatter
->_property
._ShortQuarterSymbols
) return CFRetain(formatter
->_property
._ShortQuarterSymbols
);
1962 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_SHORT_QUARTERS
, 0);
1963 } else if (kCFDateFormatterStandaloneQuarterSymbolsKey
== key
) {
1964 if (formatter
->_property
._StandaloneQuarterSymbols
) return CFRetain(formatter
->_property
._StandaloneQuarterSymbols
);
1965 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_QUARTERS
, 0);
1966 } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey
== key
) {
1967 if (formatter
->_property
._ShortStandaloneQuarterSymbols
) return CFRetain(formatter
->_property
._ShortStandaloneQuarterSymbols
);
1968 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_SHORT_QUARTERS
, 0);
1969 } else if (kCFDateFormatterAMSymbolKey
== key
) {
1970 if (formatter
->_property
._AMSymbol
) return CFRetain(formatter
->_property
._AMSymbol
);
1971 CFIndex cnt
= __cficu_udat_countSymbols(formatter
->_df
, UDAT_AM_PMS
);
1973 CFIndex ucnt
= __cficu_udat_getSymbols(formatter
->_df
, UDAT_AM_PMS
, 0, ubuffer
, BUFFER_SIZE
, &status
);
1974 if (U_SUCCESS(status
) && cnt
<= BUFFER_SIZE
) {
1975 return CFStringCreateWithCharacters(CFGetAllocator(formatter
), (UniChar
*)ubuffer
, ucnt
);
1978 } else if (kCFDateFormatterPMSymbolKey
== key
) {
1979 if (formatter
->_property
._PMSymbol
) return CFRetain(formatter
->_property
._PMSymbol
);
1980 CFIndex cnt
= __cficu_udat_countSymbols(formatter
->_df
, UDAT_AM_PMS
);
1982 CFIndex ucnt
= __cficu_udat_getSymbols(formatter
->_df
, UDAT_AM_PMS
, 1, ubuffer
, BUFFER_SIZE
, &status
);
1983 if (U_SUCCESS(status
) && cnt
<= BUFFER_SIZE
) {
1984 return CFStringCreateWithCharacters(CFGetAllocator(formatter
), (UniChar
*)ubuffer
, ucnt
);
1987 } else if (kCFDateFormatterAmbiguousYearStrategyKey
== key
) {
1988 if (formatter
->_property
._AmbiguousYearStrategy
) return CFRetain(formatter
->_property
._AmbiguousYearStrategy
);
1989 } else if (kCFDateFormatterUsesCharacterDirectionKey
== key
) {
1990 return formatter
->_property
._UsesCharacterDirection
? CFRetain(formatter
->_property
._UsesCharacterDirection
) : CFRetain(kCFBooleanFalse
);
1991 } else if (CFEqual(key
, kCFDateFormatterFormattingContextKey
)) {
1992 if (formatter
->_property
._FormattingContext
) return CFRetain(formatter
->_property
._FormattingContext
);
1993 int value
= __cficu_udat_getContext(formatter
->_df
, UDISPCTX_TYPE_CAPITALIZATION
, &status
);
1994 return CFNumberCreate(CFGetAllocator(formatter
), kCFNumberIntType
, (const void *)&value
);
1996 CFAssert3(0, __kCFLogAssertion
, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__
, key
, key
);