2 * Copyright (c) 2008 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@
24 Copyright 2002-2003, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
28 #include <CoreFoundation/CFDateFormatter.h>
29 #include <CoreFoundation/CFDate.h>
30 #include <CoreFoundation/CFTimeZone.h>
31 #include <CoreFoundation/CFCalendar.h>
32 #include <CoreFoundation/CFNumber.h>
33 #include "CFInternal.h"
34 #include <unicode/udat.h>
38 extern UCalendar
*__CFCalendarCreateUCalendar(CFStringRef calendarID
, CFStringRef localeID
, CFTimeZoneRef tz
);
39 static void __CFDateFormatterCustomize(CFDateFormatterRef formatter
);
41 extern const CFStringRef kCFDateFormatterCalendarIdentifier
;
43 #define BUFFER_SIZE 768
45 struct __CFDateFormatter
{
49 CFDateFormatterStyle _timeStyle
;
50 CFDateFormatterStyle _dateStyle
;
52 CFStringRef _defformat
;
53 CFStringRef _calendarName
;
55 CFDateRef _defaultDate
;
58 static CFStringRef
__CFDateFormatterCopyDescription(CFTypeRef cf
) {
59 CFDateFormatterRef formatter
= (CFDateFormatterRef
)cf
;
60 return CFStringCreateWithFormat(CFGetAllocator(formatter
), NULL
, CFSTR("<CFDateFormatter %p [%p]>"), cf
, CFGetAllocator(formatter
));
63 static void __CFDateFormatterDeallocate(CFTypeRef cf
) {
64 CFDateFormatterRef formatter
= (CFDateFormatterRef
)cf
;
65 if (formatter
->_df
) udat_close(formatter
->_df
);
66 if (formatter
->_locale
) CFRelease(formatter
->_locale
);
67 if (formatter
->_format
) CFRelease(formatter
->_format
);
68 if (formatter
->_defformat
) CFRelease(formatter
->_defformat
);
69 if (formatter
->_calendarName
) CFRelease(formatter
->_calendarName
);
70 if (formatter
->_tz
) CFRelease(formatter
->_tz
);
71 if (formatter
->_defaultDate
) CFRelease(formatter
->_defaultDate
);
74 static CFTypeID __kCFDateFormatterTypeID
= _kCFRuntimeNotATypeID
;
76 static const CFRuntimeClass __CFDateFormatterClass
= {
81 __CFDateFormatterDeallocate
,
85 __CFDateFormatterCopyDescription
88 static void __CFDateFormatterInitialize(void) {
89 __kCFDateFormatterTypeID
= _CFRuntimeRegisterClass(&__CFDateFormatterClass
);
92 CFTypeID
CFDateFormatterGetTypeID(void) {
93 if (_kCFRuntimeNotATypeID
== __kCFDateFormatterTypeID
) __CFDateFormatterInitialize();
94 return __kCFDateFormatterTypeID
;
97 CFDateFormatterRef
CFDateFormatterCreate(CFAllocatorRef allocator
, CFLocaleRef locale
, CFDateFormatterStyle dateStyle
, CFDateFormatterStyle timeStyle
) {
98 struct __CFDateFormatter
*memory
;
99 uint32_t size
= sizeof(struct __CFDateFormatter
) - sizeof(CFRuntimeBase
);
100 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
101 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
102 if (locale
) __CFGenericValidateType(locale
, CFLocaleGetTypeID());
103 memory
= (struct __CFDateFormatter
*)_CFRuntimeCreateInstance(allocator
, CFDateFormatterGetTypeID(), size
, NULL
);
104 if (NULL
== memory
) {
108 memory
->_locale
= NULL
;
109 memory
->_format
= NULL
;
110 memory
->_defformat
= NULL
;
111 memory
->_calendarName
= NULL
;
113 memory
->_defaultDate
= NULL
;
114 if (NULL
== locale
) locale
= CFLocaleGetSystem();
115 memory
->_dateStyle
= dateStyle
;
116 memory
->_timeStyle
= timeStyle
;
117 int32_t udstyle
, utstyle
;
119 case kCFDateFormatterNoStyle
: udstyle
= UDAT_NONE
; break;
120 case kCFDateFormatterShortStyle
: udstyle
= UDAT_SHORT
; break;
121 case kCFDateFormatterMediumStyle
: udstyle
= UDAT_MEDIUM
; break;
122 case kCFDateFormatterLongStyle
: udstyle
= UDAT_LONG
; break;
123 case kCFDateFormatterFullStyle
: udstyle
= UDAT_FULL
; break;
125 CFAssert2(0, __kCFLogAssertion
, "%s(): unknown date style %d", __PRETTY_FUNCTION__
, dateStyle
);
126 udstyle
= UDAT_MEDIUM
;
127 memory
->_dateStyle
= kCFDateFormatterMediumStyle
;
131 case kCFDateFormatterNoStyle
: utstyle
= UDAT_NONE
; break;
132 case kCFDateFormatterShortStyle
: utstyle
= UDAT_SHORT
; break;
133 case kCFDateFormatterMediumStyle
: utstyle
= UDAT_MEDIUM
; break;
134 case kCFDateFormatterLongStyle
: utstyle
= UDAT_LONG
; break;
135 case kCFDateFormatterFullStyle
: utstyle
= UDAT_FULL
; break;
137 CFAssert2(0, __kCFLogAssertion
, "%s(): unknown time style %d", __PRETTY_FUNCTION__
, timeStyle
);
138 utstyle
= UDAT_MEDIUM
;
139 memory
->_timeStyle
= kCFDateFormatterMediumStyle
;
142 CFStringRef localeName
= locale
? CFLocaleGetIdentifier(locale
) : CFSTR("");
143 char buffer
[BUFFER_SIZE
];
144 const char *cstr
= CFStringGetCStringPtr(localeName
, kCFStringEncodingASCII
);
146 if (CFStringGetCString(localeName
, buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
)) cstr
= buffer
;
152 UChar ubuffer
[BUFFER_SIZE
];
153 memory
->_tz
= CFTimeZoneCopyDefault();
154 CFStringRef tznam
= CFTimeZoneGetName(memory
->_tz
);
155 CFIndex cnt
= CFStringGetLength(tznam
);
156 if (BUFFER_SIZE
< cnt
) cnt
= BUFFER_SIZE
;
157 CFStringGetCharacters(tznam
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
158 UErrorCode status
= U_ZERO_ERROR
;
159 memory
->_df
= udat_open((UDateFormatStyle
)utstyle
, (UDateFormatStyle
)udstyle
, cstr
, ubuffer
, cnt
, NULL
, 0, &status
);
160 CFAssert2(memory
->_df
, __kCFLogAssertion
, "%s(): error (%d) creating date formatter", __PRETTY_FUNCTION__
, status
);
161 if (NULL
== memory
->_df
) {
162 CFRelease(memory
->_tz
);
166 udat_setLenient(memory
->_df
, 0);
167 if (kCFDateFormatterNoStyle
== dateStyle
&& kCFDateFormatterNoStyle
== timeStyle
) {
168 udat_applyPattern(memory
->_df
, false, NULL
, 0);
170 CFTypeRef calident
= CFLocaleGetValue(locale
, kCFLocaleCalendarIdentifier
);
171 if (calident
&& CFEqual(calident
, kCFGregorianCalendar
)) {
172 status
= U_ZERO_ERROR
;
173 udat_set2DigitYearStart(memory
->_df
, -631152000000.0, &status
); // 1950-01-01 00:00:00 GMT
175 memory
->_locale
= locale
? CFLocaleCreateCopy(allocator
, locale
) : CFLocaleGetSystem();
176 __CFDateFormatterCustomize(memory
);
177 status
= U_ZERO_ERROR
;
178 int32_t ret
= udat_toPattern(memory
->_df
, false, ubuffer
, BUFFER_SIZE
, &status
);
179 if (U_SUCCESS(status
) && ret
<= BUFFER_SIZE
) {
180 memory
->_format
= CFStringCreateWithCharacters(allocator
, (const UniChar
*)ubuffer
, ret
);
182 memory
->_defformat
= memory
->_format
? (CFStringRef
)CFRetain(memory
->_format
) : NULL
;
183 return (CFDateFormatterRef
)memory
;
186 extern CFDictionaryRef
__CFLocaleGetPrefs(CFLocaleRef locale
);
188 static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter
, bool doTime
) {
189 CFIndex formatStyle
= doTime
? formatter
->_timeStyle
: formatter
->_dateStyle
;
190 CFStringRef prefName
= doTime
? CFSTR("AppleICUTimeFormatStrings") : CFSTR("AppleICUDateFormatStrings");
191 if (kCFDateFormatterNoStyle
!= formatStyle
) {
192 CFStringRef pref
= NULL
;
193 CFDictionaryRef prefs
= __CFLocaleGetPrefs(formatter
->_locale
);
194 CFPropertyListRef metapref
= prefs
? CFDictionaryGetValue(prefs
, prefName
) : NULL
;
195 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
197 switch (formatStyle
) {
198 case kCFDateFormatterShortStyle
: key
= CFSTR("1"); break;
199 case kCFDateFormatterMediumStyle
: key
= CFSTR("2"); break;
200 case kCFDateFormatterLongStyle
: key
= CFSTR("3"); break;
201 case kCFDateFormatterFullStyle
: key
= CFSTR("4"); break;
202 default: key
= CFSTR("0"); break;
204 pref
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)metapref
, key
);
206 if (NULL
!= pref
&& CFGetTypeID(pref
) == CFStringGetTypeID()) {
207 int32_t icustyle
= UDAT_NONE
;
208 switch (formatStyle
) {
209 case kCFDateFormatterShortStyle
: icustyle
= UDAT_SHORT
; break;
210 case kCFDateFormatterMediumStyle
: icustyle
= UDAT_MEDIUM
; break;
211 case kCFDateFormatterLongStyle
: icustyle
= UDAT_LONG
; break;
212 case kCFDateFormatterFullStyle
: icustyle
= UDAT_FULL
; break;
214 CFStringRef localeName
= CFLocaleGetIdentifier(formatter
->_locale
);
215 char buffer
[BUFFER_SIZE
];
216 const char *cstr
= CFStringGetCStringPtr(localeName
, kCFStringEncodingASCII
);
218 if (CFStringGetCString(localeName
, buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
)) cstr
= buffer
;
220 UErrorCode status
= U_ZERO_ERROR
;
221 UDateFormat
*df
= udat_open((UDateFormatStyle
)(doTime
? icustyle
: UDAT_NONE
), (UDateFormatStyle
)(doTime
? UDAT_NONE
: icustyle
), cstr
, NULL
, 0, NULL
, 0, &status
);
223 UChar ubuffer
[BUFFER_SIZE
];
224 status
= U_ZERO_ERROR
;
225 int32_t date_len
= udat_toPattern(df
, false, ubuffer
, BUFFER_SIZE
, &status
);
226 if (U_SUCCESS(status
) && date_len
<= BUFFER_SIZE
) {
227 CFStringRef dateString
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (UniChar
*)ubuffer
, date_len
);
228 status
= U_ZERO_ERROR
;
229 int32_t formatter_len
= udat_toPattern(formatter
->_df
, false, ubuffer
, BUFFER_SIZE
, &status
);
230 if (U_SUCCESS(status
) && formatter_len
<= BUFFER_SIZE
) {
231 CFMutableStringRef formatString
= CFStringCreateMutable(kCFAllocatorSystemDefault
, 0);
232 CFStringAppendCharacters(formatString
, (UniChar
*)ubuffer
, formatter_len
);
233 // find dateString inside formatString, substitute the pref in that range
235 if (CFStringFindWithOptions(formatString
, dateString
, CFRangeMake(0, formatter_len
), 0, &result
)) {
236 CFStringReplace(formatString
, result
, pref
);
237 int32_t new_len
= CFStringGetLength(formatString
);
238 STACK_BUFFER_DECL(UChar
, new_buffer
, new_len
);
239 const UChar
*new_ustr
= (UChar
*)CFStringGetCharactersPtr(formatString
);
240 if (NULL
== new_ustr
) {
241 CFStringGetCharacters(formatString
, CFRangeMake(0, new_len
), (UniChar
*)new_buffer
);
242 new_ustr
= new_buffer
;
244 status
= U_ZERO_ERROR
;
245 // udat_applyPattern(formatter->_df, false, new_ustr, new_len, &status);
246 udat_applyPattern(formatter
->_df
, false, new_ustr
, new_len
);
248 CFRelease(formatString
);
250 CFRelease(dateString
);
258 static void __CFDateFormatterApplySymbolPrefs(const void *key
, const void *value
, void *context
) {
259 if (CFGetTypeID(key
) == CFStringGetTypeID() && CFGetTypeID(value
) == CFArrayGetTypeID()) {
260 CFDateFormatterRef formatter
= (CFDateFormatterRef
)context
;
261 UDateFormatSymbolType sym
= (UDateFormatSymbolType
)CFStringGetIntValue((CFStringRef
)key
);
262 CFArrayRef array
= (CFArrayRef
)value
;
263 CFIndex idx
, cnt
= CFArrayGetCount(array
);
264 for (idx
= 0; idx
< cnt
; idx
++) {
265 CFStringRef item
= (CFStringRef
)CFArrayGetValueAtIndex(array
, idx
);
266 if (CFGetTypeID(item
) != CFStringGetTypeID()) continue;
267 CFIndex item_cnt
= CFStringGetLength(item
);
268 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
269 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr(item
);
270 if (NULL
== item_ustr
) {
271 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
272 CFStringGetCharacters(item
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
273 item_ustr
= item_buffer
;
275 UErrorCode status
= U_ZERO_ERROR
;
276 udat_setSymbols(formatter
->_df
, sym
, idx
, item_ustr
, item_cnt
, &status
);
281 static void __CFDateFormatterCustomize(CFDateFormatterRef formatter
) {
282 __substituteFormatStringFromPrefsDF(formatter
, false);
283 __substituteFormatStringFromPrefsDF(formatter
, true);
284 CFDictionaryRef prefs
= __CFLocaleGetPrefs(formatter
->_locale
);
285 CFPropertyListRef metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUDateTimeSymbols")) : NULL
;
286 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
287 CFDictionaryApplyFunction((CFDictionaryRef
)metapref
, __CFDateFormatterApplySymbolPrefs
, formatter
);
291 CFLocaleRef
CFDateFormatterGetLocale(CFDateFormatterRef formatter
) {
292 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
293 return formatter
->_locale
;
296 CFDateFormatterStyle
CFDateFormatterGetDateStyle(CFDateFormatterRef formatter
) {
297 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
298 return formatter
->_dateStyle
;
301 CFDateFormatterStyle
CFDateFormatterGetTimeStyle(CFDateFormatterRef formatter
) {
302 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
303 return formatter
->_timeStyle
;
306 CFStringRef
CFDateFormatterGetFormat(CFDateFormatterRef formatter
) {
307 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
308 return formatter
->_format
;
311 void CFDateFormatterSetFormat(CFDateFormatterRef formatter
, CFStringRef formatString
) {
312 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
313 __CFGenericValidateType(formatString
, CFStringGetTypeID());
314 CFIndex cnt
= CFStringGetLength(formatString
);
315 CFAssert1(cnt
<= 1024, __kCFLogAssertion
, "%s(): format string too long", __PRETTY_FUNCTION__
);
316 if (formatter
->_format
!= formatString
&& cnt
<= 1024) {
317 STACK_BUFFER_DECL(UChar
, ubuffer
, cnt
);
318 const UChar
*ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)formatString
);
320 CFStringGetCharacters(formatString
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
323 UErrorCode status
= U_ZERO_ERROR
;
324 // udat_applyPattern(formatter->_df, false, ustr, cnt, &status);
325 udat_applyPattern(formatter
->_df
, false, ustr
, cnt
);
326 if (U_SUCCESS(status
)) {
327 if (formatter
->_format
) CFRelease(formatter
->_format
);
328 formatter
->_format
= (CFStringRef
)CFStringCreateCopy(CFGetAllocator(formatter
), formatString
);
333 CFStringRef
CFDateFormatterCreateStringWithDate(CFAllocatorRef allocator
, CFDateFormatterRef formatter
, CFDateRef date
) {
334 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
335 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
336 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
337 __CFGenericValidateType(date
, CFDateGetTypeID());
338 return CFDateFormatterCreateStringWithAbsoluteTime(allocator
, formatter
, CFDateGetAbsoluteTime(date
));
341 CFStringRef
CFDateFormatterCreateStringWithAbsoluteTime(CFAllocatorRef allocator
, CFDateFormatterRef formatter
, CFAbsoluteTime at
) {
342 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
343 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
344 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
345 UChar
*ustr
= NULL
, ubuffer
[BUFFER_SIZE
];
346 UErrorCode status
= U_ZERO_ERROR
;
347 CFIndex used
, cnt
= BUFFER_SIZE
;
348 UDate ud
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0 + 0.5;
349 used
= udat_format(formatter
->_df
, ud
, ubuffer
, cnt
, NULL
, &status
);
350 if (status
== U_BUFFER_OVERFLOW_ERROR
|| cnt
< used
) {
352 ustr
= (UChar
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(UChar
) * cnt
, 0);
353 status
= U_ZERO_ERROR
;
354 used
= udat_format(formatter
->_df
, ud
, ustr
, cnt
, NULL
, &status
);
356 CFStringRef string
= NULL
;
357 if (U_SUCCESS(status
)) {
358 string
= CFStringCreateWithCharacters(allocator
, (const UniChar
*)(ustr
? ustr
: ubuffer
), used
);
360 if (ustr
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, ustr
);
364 CFDateRef
CFDateFormatterCreateDateFromString(CFAllocatorRef allocator
, CFDateFormatterRef formatter
, CFStringRef string
, CFRange
*rangep
) {
365 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
366 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
367 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
368 __CFGenericValidateType(string
, CFStringGetTypeID());
370 if (CFDateFormatterGetAbsoluteTimeFromString(formatter
, string
, rangep
, &at
)) {
371 return CFDateCreate(allocator
, at
);
376 Boolean
CFDateFormatterGetAbsoluteTimeFromString(CFDateFormatterRef formatter
, CFStringRef string
, CFRange
*rangep
, CFAbsoluteTime
*atp
) {
377 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
378 __CFGenericValidateType(string
, CFStringGetTypeID());
379 CFRange range
= {0, 0};
383 range
.length
= CFStringGetLength(string
);
385 if (1024 < range
.length
) range
.length
= 1024;
386 const UChar
*ustr
= (UChar
*)CFStringGetCharactersPtr(string
);
387 STACK_BUFFER_DECL(UChar
, ubuffer
, (NULL
== ustr
) ? range
.length
: 1);
389 CFStringGetCharacters(string
, range
, (UniChar
*)ubuffer
);
392 ustr
+= range
.location
;
396 UErrorCode status
= U_ZERO_ERROR
;
397 if (formatter
->_defaultDate
) {
398 CFAbsoluteTime at
= CFDateGetAbsoluteTime(formatter
->_defaultDate
);
399 udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
400 UDateFormat
*df2
= udat_clone(formatter
->_df
, &status
);
401 UCalendar
*cal2
= (UCalendar
*)udat_getCalendar(df2
);
402 ucal_setMillis(cal2
, udate
, &status
);
403 udat_parseCalendar(formatter
->_df
, cal2
, ustr
, range
.length
, &dpos
, &status
);
404 udate
= ucal_getMillis(cal2
, &status
);
407 udate
= udat_parse(formatter
->_df
, ustr
, range
.length
, &dpos
, &status
);
409 if (rangep
) rangep
->length
= dpos
;
410 if (U_FAILURE(status
)) {
414 *atp
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
419 #define SET_SYMBOLS_ARRAY(ICU_CODE, INDEX_BASE) \
420 __CFGenericValidateType(value, CFArrayGetTypeID()); \
421 CFArrayRef array = (CFArrayRef)value; \
422 CFIndex idx, cnt = CFArrayGetCount(array); \
423 for (idx = 0; idx < cnt; idx++) { \
424 CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx); \
425 __CFGenericValidateType(item, CFStringGetTypeID()); \
426 CFIndex item_cnt = CFStringGetLength(item); \
427 STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); \
428 UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item); \
429 if (NULL == item_ustr) { \
430 item_cnt = __CFMin(BUFFER_SIZE, item_cnt); \
431 CFStringGetCharacters(item, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); \
432 item_ustr = item_buffer; \
434 status = U_ZERO_ERROR; \
435 udat_setSymbols(formatter->_df, ICU_CODE, idx + INDEX_BASE, item_ustr, item_cnt, &status); \
438 void CFDateFormatterSetProperty(CFDateFormatterRef formatter
, CFStringRef key
, CFTypeRef value
) {
439 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
440 __CFGenericValidateType(key
, CFStringGetTypeID());
441 UErrorCode status
= U_ZERO_ERROR
;
442 UChar ubuffer
[BUFFER_SIZE
];
444 if (kCFDateFormatterIsLenient
== key
) {
445 __CFGenericValidateType(value
, CFBooleanGetTypeID());
446 udat_setLenient(formatter
->_df
, (kCFBooleanTrue
== value
));
447 } else if (kCFDateFormatterCalendar
== key
) {
448 __CFGenericValidateType(value
, CFCalendarGetTypeID());
449 CFStringRef localeName
= CFLocaleGetIdentifier(formatter
->_locale
);
450 CFDictionaryRef components
= CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault
, localeName
);
451 CFMutableDictionaryRef mcomponents
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, 0, components
);
452 CFDictionarySetValue(mcomponents
, kCFLocaleCalendarIdentifier
, CFCalendarGetIdentifier((CFCalendarRef
)value
));
453 localeName
= CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault
, mcomponents
);
454 CFRelease(mcomponents
);
455 CFRelease(components
);
456 CFLocaleRef newLocale
= CFLocaleCreate(CFGetAllocator(formatter
->_locale
), localeName
);
457 CFRelease(localeName
);
458 CFRelease(formatter
->_locale
);
459 formatter
->_locale
= newLocale
;
460 UCalendar
*cal
= __CFCalendarCreateUCalendar(NULL
, CFLocaleGetIdentifier(formatter
->_locale
), formatter
->_tz
);
461 if (cal
) udat_setCalendar(formatter
->_df
, cal
);
462 if (cal
) ucal_close(cal
);
463 } else if (kCFDateFormatterCalendarIdentifier
== key
|| kCFDateFormatterCalendarName
== key
) {
464 __CFGenericValidateType(value
, CFStringGetTypeID());
465 CFStringRef localeName
= CFLocaleGetIdentifier(formatter
->_locale
);
466 CFDictionaryRef components
= CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault
, localeName
);
467 CFMutableDictionaryRef mcomponents
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, 0, components
);
468 CFDictionarySetValue(mcomponents
, kCFLocaleCalendarIdentifier
, value
);
469 localeName
= CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault
, mcomponents
);
470 CFRelease(mcomponents
);
471 CFRelease(components
);
472 CFLocaleRef newLocale
= CFLocaleCreate(CFGetAllocator(formatter
->_locale
), localeName
);
473 CFRelease(localeName
);
474 CFRelease(formatter
->_locale
);
475 formatter
->_locale
= newLocale
;
476 UCalendar
*cal
= __CFCalendarCreateUCalendar(NULL
, CFLocaleGetIdentifier(formatter
->_locale
), formatter
->_tz
);
477 if (cal
) udat_setCalendar(formatter
->_df
, cal
);
478 if (cal
) ucal_close(cal
);
479 } else if (kCFDateFormatterTimeZone
== key
) {
480 __CFGenericValidateType(value
, CFTimeZoneGetTypeID());
481 CFTimeZoneRef old
= formatter
->_tz
;
482 formatter
->_tz
= value
? (CFTimeZoneRef
)CFRetain(value
) : CFTimeZoneCopyDefault();
483 if (old
) CFRelease(old
);
484 CFStringRef tznam
= CFTimeZoneGetName(formatter
->_tz
);
485 UCalendar
*cal
= (UCalendar
*)udat_getCalendar(formatter
->_df
);
486 CFIndex ucnt
= CFStringGetLength(tznam
);
487 if (BUFFER_SIZE
< ucnt
) ucnt
= BUFFER_SIZE
;
488 CFStringGetCharacters(tznam
, CFRangeMake(0, ucnt
), (UniChar
*)ubuffer
);
489 ucal_setTimeZone(cal
, ubuffer
, ucnt
, &status
);
490 } else if (kCFDateFormatterDefaultFormat
== key
) {
491 // read-only attribute
492 } else if (kCFDateFormatterTwoDigitStartDate
== key
) {
493 __CFGenericValidateType(value
, CFDateGetTypeID());
494 CFAbsoluteTime at
= CFDateGetAbsoluteTime((CFDateRef
)value
);
495 UDate udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
496 udat_set2DigitYearStart(formatter
->_df
, udate
, &status
);
497 } else if (kCFDateFormatterDefaultDate
== key
) {
498 __CFGenericValidateType(value
, CFDateGetTypeID());
499 CFDateRef old
= formatter
->_defaultDate
;
500 formatter
->_defaultDate
= value
? (CFDateRef
)CFRetain(value
) : NULL
;
501 if (old
) CFRelease(old
);
502 } else if (kCFDateFormatterEraSymbols
== key
) {
503 SET_SYMBOLS_ARRAY(UDAT_ERAS
, 0)
504 } else if (kCFDateFormatterMonthSymbols
== key
) {
505 SET_SYMBOLS_ARRAY(UDAT_MONTHS
, 0)
506 } else if (kCFDateFormatterShortMonthSymbols
== key
) {
507 SET_SYMBOLS_ARRAY(UDAT_SHORT_MONTHS
, 0)
508 } else if (kCFDateFormatterWeekdaySymbols
== key
) {
509 SET_SYMBOLS_ARRAY(UDAT_WEEKDAYS
, 1)
510 } else if (kCFDateFormatterShortWeekdaySymbols
== key
) {
511 SET_SYMBOLS_ARRAY(UDAT_SHORT_WEEKDAYS
, 1)
512 } else if (kCFDateFormatterAMSymbol
== key
) {
513 __CFGenericValidateType(value
, CFStringGetTypeID());
514 CFIndex item_cnt
= CFStringGetLength((CFStringRef
)value
);
515 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
516 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)value
);
517 if (NULL
== item_ustr
) {
518 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
519 CFStringGetCharacters((CFStringRef
)value
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
520 item_ustr
= item_buffer
;
522 udat_setSymbols(formatter
->_df
, UDAT_AM_PMS
, 0, item_ustr
, item_cnt
, &status
);
523 } else if (kCFDateFormatterPMSymbol
== key
) {
524 __CFGenericValidateType(value
, CFStringGetTypeID());
525 CFIndex item_cnt
= CFStringGetLength((CFStringRef
)value
);
526 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
527 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)value
);
528 if (NULL
== item_ustr
) {
529 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
530 CFStringGetCharacters((CFStringRef
)value
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
531 item_ustr
= item_buffer
;
533 udat_setSymbols(formatter
->_df
, UDAT_AM_PMS
, 1, item_ustr
, item_cnt
, &status
);
534 } else if (kCFDateFormatterGregorianStartDate
== key
) {
535 __CFGenericValidateType(value
, CFDateGetTypeID());
536 CFAbsoluteTime at
= CFDateGetAbsoluteTime((CFDateRef
)value
);
537 UDate udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
538 UCalendar
*cal
= (UCalendar
*)udat_getCalendar(formatter
->_df
);
539 ucal_setGregorianChange(cal
, udate
, &status
);
540 } else if (kCFDateFormatterLongEraSymbols
== key
) {
541 SET_SYMBOLS_ARRAY(UDAT_ERA_NAMES
, 0)
542 } else if (kCFDateFormatterVeryShortMonthSymbols
== key
) {
543 SET_SYMBOLS_ARRAY(UDAT_NARROW_MONTHS
, 0)
544 } else if (kCFDateFormatterStandaloneMonthSymbols
== key
) {
545 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_MONTHS
, 0)
546 } else if (kCFDateFormatterShortStandaloneMonthSymbols
== key
) {
547 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_MONTHS
, 0)
548 } else if (kCFDateFormatterVeryShortStandaloneMonthSymbols
== key
) {
549 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_MONTHS
, 0)
550 } else if (kCFDateFormatterVeryShortWeekdaySymbols
== key
) {
551 SET_SYMBOLS_ARRAY(UDAT_NARROW_WEEKDAYS
, 1)
552 } else if (kCFDateFormatterStandaloneWeekdaySymbols
== key
) {
553 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_WEEKDAYS
, 1)
554 } else if (kCFDateFormatterShortStandaloneWeekdaySymbols
== key
) {
555 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_WEEKDAYS
, 1)
556 } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbols
== key
) {
557 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_WEEKDAYS
, 1)
558 } else if (kCFDateFormatterQuarterSymbols
== key
) {
559 SET_SYMBOLS_ARRAY(UDAT_QUARTERS
, 1)
560 } else if (kCFDateFormatterShortQuarterSymbols
== key
) {
561 SET_SYMBOLS_ARRAY(UDAT_SHORT_QUARTERS
, 1)
562 } else if (kCFDateFormatterStandaloneQuarterSymbols
== key
) {
563 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_QUARTERS
, 1)
564 } else if (kCFDateFormatterShortStandaloneQuarterSymbols
== key
) {
565 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_QUARTERS
, 1)
567 CFAssert3(0, __kCFLogAssertion
, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__
, key
, key
);
571 #define GET_SYMBOLS_ARRAY(ICU_CODE, INDEX_BASE) \
572 CFIndex idx, cnt = udat_countSymbols(formatter->_df, ICU_CODE) - INDEX_BASE; \
573 STACK_BUFFER_DECL(CFStringRef, strings, cnt); \
574 for (idx = 0; idx < cnt; idx++) { \
575 CFStringRef str = NULL; \
576 status = U_ZERO_ERROR; \
577 CFIndex ucnt = udat_getSymbols(formatter->_df, ICU_CODE, idx + INDEX_BASE, ubuffer, BUFFER_SIZE, &status); \
578 if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { \
579 str = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, ucnt); \
581 strings[idx] = !str ? (CFStringRef)CFRetain(CFSTR("<error>")) : str; \
583 CFArrayRef array = CFArrayCreate(CFGetAllocator(formatter), (const void **)strings, cnt, &kCFTypeArrayCallBacks); \
585 CFRelease(strings[cnt]); \
589 CFTypeRef
CFDateFormatterCopyProperty(CFDateFormatterRef formatter
, CFStringRef key
) {
590 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
591 __CFGenericValidateType(key
, CFStringGetTypeID());
592 UErrorCode status
= U_ZERO_ERROR
;
593 UChar ubuffer
[BUFFER_SIZE
];
595 if (kCFDateFormatterIsLenient
== key
) {
596 return CFRetain(udat_isLenient(formatter
->_df
) ? kCFBooleanTrue
: kCFBooleanFalse
);
597 } else if (kCFDateFormatterCalendar
== key
) {
598 CFCalendarRef calendar
= (CFCalendarRef
)CFLocaleGetValue(formatter
->_locale
, kCFLocaleCalendar
);
599 return calendar
? CFRetain(calendar
) : NULL
;
600 } else if (kCFDateFormatterCalendarIdentifier
== key
|| kCFDateFormatterCalendarName
== key
) {
601 CFStringRef ident
= (CFStringRef
)CFLocaleGetValue(formatter
->_locale
, kCFLocaleCalendarIdentifier
);
602 return ident
? CFRetain(ident
) : NULL
;
603 } else if (kCFDateFormatterTimeZone
== key
) {
604 return CFRetain(formatter
->_tz
);
605 } else if (kCFDateFormatterDefaultFormat
== key
) {
606 return formatter
->_defformat
? CFRetain(formatter
->_defformat
) : NULL
;
607 } else if (kCFDateFormatterTwoDigitStartDate
== key
) {
608 UDate udate
= udat_get2DigitYearStart(formatter
->_df
, &status
);
609 if (U_SUCCESS(status
)) {
610 CFAbsoluteTime at
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
611 return CFDateCreate(CFGetAllocator(formatter
), at
);
613 } else if (kCFDateFormatterDefaultDate
== key
) {
614 return formatter
->_defaultDate
? CFRetain(formatter
->_defaultDate
) : NULL
;
615 } else if (kCFDateFormatterEraSymbols
== key
) {
616 GET_SYMBOLS_ARRAY(UDAT_ERAS
, 0)
617 } else if (kCFDateFormatterMonthSymbols
== key
) {
618 GET_SYMBOLS_ARRAY(UDAT_MONTHS
, 0)
619 } else if (kCFDateFormatterShortMonthSymbols
== key
) {
620 GET_SYMBOLS_ARRAY(UDAT_SHORT_MONTHS
, 0)
621 } else if (kCFDateFormatterWeekdaySymbols
== key
) {
622 GET_SYMBOLS_ARRAY(UDAT_WEEKDAYS
, 1)
623 } else if (kCFDateFormatterShortWeekdaySymbols
== key
) {
624 GET_SYMBOLS_ARRAY(UDAT_SHORT_WEEKDAYS
, 1)
625 } else if (kCFDateFormatterAMSymbol
== key
) {
626 CFIndex cnt
= udat_countSymbols(formatter
->_df
, UDAT_AM_PMS
);
628 CFIndex ucnt
= udat_getSymbols(formatter
->_df
, UDAT_AM_PMS
, 0, ubuffer
, BUFFER_SIZE
, &status
);
629 if (U_SUCCESS(status
) && cnt
<= BUFFER_SIZE
) {
630 return CFStringCreateWithCharacters(CFGetAllocator(formatter
), (UniChar
*)ubuffer
, ucnt
);
633 } else if (kCFDateFormatterPMSymbol
== key
) {
634 CFIndex cnt
= udat_countSymbols(formatter
->_df
, UDAT_AM_PMS
);
636 CFIndex ucnt
= udat_getSymbols(formatter
->_df
, UDAT_AM_PMS
, 1, ubuffer
, BUFFER_SIZE
, &status
);
637 if (U_SUCCESS(status
) && cnt
<= BUFFER_SIZE
) {
638 return CFStringCreateWithCharacters(CFGetAllocator(formatter
), (UniChar
*)ubuffer
, ucnt
);
641 } else if (kCFDateFormatterGregorianStartDate
== key
) {
642 UCalendar
*cal
= (UCalendar
*)udat_getCalendar(formatter
->_df
);
643 UDate udate
= ucal_getGregorianChange(cal
, &status
);
644 if (U_SUCCESS(status
)) {
645 CFAbsoluteTime at
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
646 return CFDateCreate(CFGetAllocator(formatter
), at
);
648 } else if (kCFDateFormatterLongEraSymbols
== key
) {
649 GET_SYMBOLS_ARRAY(UDAT_ERA_NAMES
, 0)
650 } else if (kCFDateFormatterVeryShortMonthSymbols
== key
) {
651 GET_SYMBOLS_ARRAY(UDAT_NARROW_MONTHS
, 0)
652 } else if (kCFDateFormatterStandaloneMonthSymbols
== key
) {
653 GET_SYMBOLS_ARRAY(UDAT_STANDALONE_MONTHS
, 0)
654 } else if (kCFDateFormatterShortStandaloneMonthSymbols
== key
) {
655 GET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_MONTHS
, 0)
656 } else if (kCFDateFormatterVeryShortStandaloneMonthSymbols
== key
) {
657 GET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_MONTHS
, 0)
658 } else if (kCFDateFormatterVeryShortWeekdaySymbols
== key
) {
659 GET_SYMBOLS_ARRAY(UDAT_NARROW_WEEKDAYS
, 1)
660 } else if (kCFDateFormatterStandaloneWeekdaySymbols
== key
) {
661 GET_SYMBOLS_ARRAY(UDAT_STANDALONE_WEEKDAYS
, 1)
662 } else if (kCFDateFormatterShortStandaloneWeekdaySymbols
== key
) {
663 GET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_WEEKDAYS
, 1)
664 } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbols
== key
) {
665 GET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_WEEKDAYS
, 1)
666 } else if (kCFDateFormatterQuarterSymbols
== key
) {
667 GET_SYMBOLS_ARRAY(UDAT_QUARTERS
, 1)
668 } else if (kCFDateFormatterShortQuarterSymbols
== key
) {
669 GET_SYMBOLS_ARRAY(UDAT_SHORT_QUARTERS
, 1)
670 } else if (kCFDateFormatterStandaloneQuarterSymbols
== key
) {
671 GET_SYMBOLS_ARRAY(UDAT_STANDALONE_QUARTERS
, 1)
672 } else if (kCFDateFormatterShortStandaloneQuarterSymbols
== key
) {
673 GET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_QUARTERS
, 1)
675 CFAssert3(0, __kCFLogAssertion
, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__
, key
, key
);
680 CONST_STRING_DECL(kCFDateFormatterIsLenient
, "kCFDateFormatterIsLenient")
681 CONST_STRING_DECL(kCFDateFormatterTimeZone
, "kCFDateFormatterTimeZone")
682 CONST_STRING_DECL(kCFDateFormatterCalendarName
, "kCFDateFormatterCalendarName")
683 CONST_STRING_DECL(kCFDateFormatterCalendarIdentifier
, "kCFDateFormatterCalendarIdentifier")
684 CONST_STRING_DECL(kCFDateFormatterCalendar
, "kCFDateFormatterCalendar")
685 CONST_STRING_DECL(kCFDateFormatterDefaultFormat
, "kCFDateFormatterDefaultFormat")
687 CONST_STRING_DECL(kCFDateFormatterTwoDigitStartDate
, "kCFDateFormatterTwoDigitStartDate")
688 CONST_STRING_DECL(kCFDateFormatterDefaultDate
, "kCFDateFormatterDefaultDate")
689 CONST_STRING_DECL(kCFDateFormatterEraSymbols
, "kCFDateFormatterEraSymbols")
690 CONST_STRING_DECL(kCFDateFormatterMonthSymbols
, "kCFDateFormatterMonthSymbols")
691 CONST_STRING_DECL(kCFDateFormatterShortMonthSymbols
, "kCFDateFormatterShortMonthSymbols")
692 CONST_STRING_DECL(kCFDateFormatterWeekdaySymbols
, "kCFDateFormatterWeekdaySymbols")
693 CONST_STRING_DECL(kCFDateFormatterShortWeekdaySymbols
, "kCFDateFormatterShortWeekdaySymbols")
694 CONST_STRING_DECL(kCFDateFormatterAMSymbol
, "kCFDateFormatterAMSymbol")
695 CONST_STRING_DECL(kCFDateFormatterPMSymbol
, "kCFDateFormatterPMSymbol")
697 CONST_STRING_DECL(kCFDateFormatterLongEraSymbols
, "kCFDateFormatterLongEraSymbols")
698 CONST_STRING_DECL(kCFDateFormatterVeryShortMonthSymbols
, "kCFDateFormatterVeryShortMonthSymbols")
699 CONST_STRING_DECL(kCFDateFormatterStandaloneMonthSymbols
, "kCFDateFormatterStandaloneMonthSymbols")
700 CONST_STRING_DECL(kCFDateFormatterShortStandaloneMonthSymbols
, "kCFDateFormatterShortStandaloneMonthSymbols")
701 CONST_STRING_DECL(kCFDateFormatterVeryShortStandaloneMonthSymbols
, "kCFDateFormatterVeryShortStandaloneMonthSymbols")
702 CONST_STRING_DECL(kCFDateFormatterVeryShortWeekdaySymbols
, "kCFDateFormatterVeryShortWeekdaySymbols")
703 CONST_STRING_DECL(kCFDateFormatterStandaloneWeekdaySymbols
, "kCFDateFormatterStandaloneWeekdaySymbols")
704 CONST_STRING_DECL(kCFDateFormatterShortStandaloneWeekdaySymbols
, "kCFDateFormatterShortStandaloneWeekdaySymbols")
705 CONST_STRING_DECL(kCFDateFormatterVeryShortStandaloneWeekdaySymbols
, "kCFDateFormatterVeryShortStandaloneWeekdaySymbols")
706 CONST_STRING_DECL(kCFDateFormatterQuarterSymbols
, "kCFDateFormatterQuarterSymbols")
707 CONST_STRING_DECL(kCFDateFormatterShortQuarterSymbols
, "kCFDateFormatterShortQuarterSymbols")
708 CONST_STRING_DECL(kCFDateFormatterStandaloneQuarterSymbols
, "kCFDateFormatterStandaloneQuarterSymbols")
709 CONST_STRING_DECL(kCFDateFormatterShortStandaloneQuarterSymbols
, "kCFDateFormatterShortStandaloneQuarterSymbols")
710 CONST_STRING_DECL(kCFDateFormatterGregorianStartDate
, "kCFDateFormatterGregorianStartDate")