2 * Copyright (c) 2012 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-2012, 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 <unicode/udat.h>
40 #include <unicode/udatpg.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
);
56 static void __CFDateFormatterCustomize(CFDateFormatterRef formatter
);
58 CF_EXPORT
const CFStringRef kCFDateFormatterCalendarIdentifierKey
;
60 #undef CFReleaseIfNotNull
61 #define CFReleaseIfNotNull(X) if (X) CFRelease(X)
63 #define BUFFER_SIZE 768
65 static CFStringRef
__CFDateFormatterCreateForcedTemplate(CFLocaleRef locale
, CFStringRef inString
);
67 // If you pass in a string in tmplate, you get back NULL (failure) or a CFStringRef.
68 // If you pass in an array in tmplate, you get back NULL (global failure) or a CFArrayRef with CFStringRefs or kCFNulls (per-template failure) at each corresponding index.
70 CFArrayRef
CFDateFormatterCreateDateFormatsFromTemplates(CFAllocatorRef allocator
, CFArrayRef tmplates
, CFOptionFlags options
, CFLocaleRef locale
) {
71 return (CFArrayRef
)CFDateFormatterCreateDateFormatFromTemplate(allocator
, (CFStringRef
)tmplates
, options
, locale
);
74 CFStringRef
CFDateFormatterCreateDateFormatFromTemplate(CFAllocatorRef allocator
, CFStringRef tmplate
, CFOptionFlags options
, CFLocaleRef locale
) {
75 if (allocator
) __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
76 if (locale
) __CFGenericValidateType(locale
, CFLocaleGetTypeID());
77 Boolean tmplateIsString
= (CFStringGetTypeID() == CFGetTypeID(tmplate
));
78 if (!tmplateIsString
) {
79 __CFGenericValidateType(tmplate
, CFArrayGetTypeID());
82 CFStringRef localeName
= locale
? CFLocaleGetIdentifier(locale
) : CFSTR("");
83 char buffer
[BUFFER_SIZE
];
84 const char *cstr
= CFStringGetCStringPtr(localeName
, kCFStringEncodingASCII
);
86 if (CFStringGetCString(localeName
, buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
)) cstr
= buffer
;
92 UErrorCode status
= U_ZERO_ERROR
;
93 UDateTimePatternGenerator
*ptg
= udatpg_open(cstr
, &status
);
94 if (NULL
== ptg
|| U_FAILURE(status
)) {
98 CFTypeRef result
= tmplateIsString
? NULL
: (CFTypeRef
)CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
100 for (CFIndex idx
= 0, cnt
= tmplateIsString
? 1 : CFArrayGetCount((CFArrayRef
)tmplate
); idx
< cnt
; idx
++) {
101 CFStringRef tmplateString
= tmplateIsString
? (CFStringRef
)tmplate
: (CFStringRef
)CFArrayGetValueAtIndex((CFArrayRef
)tmplate
, idx
);
102 CFStringRef resultString
= NULL
;
104 tmplateString
= __CFDateFormatterCreateForcedTemplate(locale
? locale
: CFLocaleGetSystem(), tmplateString
);
106 CFIndex jCount
= 0; // the only interesting cases are 0, 1, and 2 (adjacent)
107 CFRange r
= CFStringFind(tmplateString
, CFSTR("j"), 0);
108 if (kCFNotFound
!= r
.location
) {
110 if ((r
.location
+ 1 < CFStringGetLength(tmplateString
)) && ('j' == CFStringGetCharacterAtIndex(tmplateString
, r
.location
+ 1))) {
115 UChar pattern
[BUFFER_SIZE
], skel
[BUFFER_SIZE
], bpat
[BUFFER_SIZE
];
116 CFIndex tmpltLen
= CFStringGetLength(tmplateString
);
117 if (BUFFER_SIZE
< tmpltLen
) tmpltLen
= BUFFER_SIZE
;
118 CFStringGetCharacters(tmplateString
, CFRangeMake(0, tmpltLen
), (UniChar
*)pattern
);
119 CFRelease(tmplateString
);
121 int32_t patlen
= tmpltLen
;
122 status
= U_ZERO_ERROR
;
123 int32_t skellen
= udatpg_getSkeleton(ptg
, pattern
, patlen
, skel
, sizeof(skel
) / sizeof(skel
[0]), &status
);
124 if (!U_FAILURE(status
)) {
125 if ((0 < jCount
) && (skellen
+ jCount
< (sizeof(skel
) / sizeof(skel
[0])))) {
126 skel
[skellen
++] = 'j';
127 if (1 < jCount
) skel
[skellen
++] = 'j';
130 status
= U_ZERO_ERROR
;
131 int32_t bpatlen
= udatpg_getBestPattern(ptg
, skel
, skellen
, bpat
, sizeof(bpat
) / sizeof(bpat
[0]), &status
);
132 if (!U_FAILURE(status
)) {
133 resultString
= CFStringCreateWithCharacters(allocator
, (const UniChar
*)bpat
, bpatlen
);
137 if (tmplateIsString
) {
138 result
= (CFTypeRef
)resultString
;
140 CFArrayAppendValue((CFMutableArrayRef
)result
, resultString
? (CFTypeRef
)resultString
: (CFTypeRef
)kCFNull
);
141 if (resultString
) CFRelease(resultString
);
147 return (CFStringRef
)result
;
150 struct __CFDateFormatter
{
154 CFDateFormatterStyle _timeStyle
;
155 CFDateFormatterStyle _dateStyle
;
157 CFStringRef _defformat
;
159 CFBooleanRef _IsLenient
;
160 CFBooleanRef _DoesRelativeDateFormatting
;
161 CFBooleanRef _HasCustomFormat
;
162 CFTimeZoneRef _TimeZone
;
163 CFCalendarRef _Calendar
;
164 CFStringRef _CalendarName
;
165 CFDateRef _TwoDigitStartDate
;
166 CFDateRef _DefaultDate
;
167 CFDateRef _GregorianStartDate
;
168 CFArrayRef _EraSymbols
;
169 CFArrayRef _LongEraSymbols
;
170 CFArrayRef _MonthSymbols
;
171 CFArrayRef _ShortMonthSymbols
;
172 CFArrayRef _VeryShortMonthSymbols
;
173 CFArrayRef _StandaloneMonthSymbols
;
174 CFArrayRef _ShortStandaloneMonthSymbols
;
175 CFArrayRef _VeryShortStandaloneMonthSymbols
;
176 CFArrayRef _WeekdaySymbols
;
177 CFArrayRef _ShortWeekdaySymbols
;
178 CFArrayRef _VeryShortWeekdaySymbols
;
179 CFArrayRef _StandaloneWeekdaySymbols
;
180 CFArrayRef _ShortStandaloneWeekdaySymbols
;
181 CFArrayRef _VeryShortStandaloneWeekdaySymbols
;
182 CFArrayRef _QuarterSymbols
;
183 CFArrayRef _ShortQuarterSymbols
;
184 CFArrayRef _StandaloneQuarterSymbols
;
185 CFArrayRef _ShortStandaloneQuarterSymbols
;
186 CFStringRef _AMSymbol
;
187 CFStringRef _PMSymbol
;
188 CFNumberRef _AmbiguousYearStrategy
;
192 static CFStringRef
__CFDateFormatterCopyDescription(CFTypeRef cf
) {
193 CFDateFormatterRef formatter
= (CFDateFormatterRef
)cf
;
194 return CFStringCreateWithFormat(CFGetAllocator(formatter
), NULL
, CFSTR("<CFDateFormatter %p [%p]>"), cf
, CFGetAllocator(formatter
));
197 static void __CFDateFormatterDeallocate(CFTypeRef cf
) {
198 CFDateFormatterRef formatter
= (CFDateFormatterRef
)cf
;
199 if (formatter
->_df
) udat_close(formatter
->_df
);
200 if (formatter
->_locale
) CFRelease(formatter
->_locale
);
201 if (formatter
->_format
) CFRelease(formatter
->_format
);
202 if (formatter
->_defformat
) CFRelease(formatter
->_defformat
);
203 CFReleaseIfNotNull(formatter
->_property
._IsLenient
);
204 CFReleaseIfNotNull(formatter
->_property
._DoesRelativeDateFormatting
);
205 CFReleaseIfNotNull(formatter
->_property
._TimeZone
);
206 CFReleaseIfNotNull(formatter
->_property
._Calendar
);
207 CFReleaseIfNotNull(formatter
->_property
._CalendarName
);
208 CFReleaseIfNotNull(formatter
->_property
._TwoDigitStartDate
);
209 CFReleaseIfNotNull(formatter
->_property
._DefaultDate
);
210 CFReleaseIfNotNull(formatter
->_property
._GregorianStartDate
);
211 CFReleaseIfNotNull(formatter
->_property
._EraSymbols
);
212 CFReleaseIfNotNull(formatter
->_property
._LongEraSymbols
);
213 CFReleaseIfNotNull(formatter
->_property
._MonthSymbols
);
214 CFReleaseIfNotNull(formatter
->_property
._ShortMonthSymbols
);
215 CFReleaseIfNotNull(formatter
->_property
._VeryShortMonthSymbols
);
216 CFReleaseIfNotNull(formatter
->_property
._StandaloneMonthSymbols
);
217 CFReleaseIfNotNull(formatter
->_property
._ShortStandaloneMonthSymbols
);
218 CFReleaseIfNotNull(formatter
->_property
._VeryShortStandaloneMonthSymbols
);
219 CFReleaseIfNotNull(formatter
->_property
._WeekdaySymbols
);
220 CFReleaseIfNotNull(formatter
->_property
._ShortWeekdaySymbols
);
221 CFReleaseIfNotNull(formatter
->_property
._VeryShortWeekdaySymbols
);
222 CFReleaseIfNotNull(formatter
->_property
._StandaloneWeekdaySymbols
);
223 CFReleaseIfNotNull(formatter
->_property
._ShortStandaloneWeekdaySymbols
);
224 CFReleaseIfNotNull(formatter
->_property
._VeryShortStandaloneWeekdaySymbols
);
225 CFReleaseIfNotNull(formatter
->_property
._QuarterSymbols
);
226 CFReleaseIfNotNull(formatter
->_property
._ShortQuarterSymbols
);
227 CFReleaseIfNotNull(formatter
->_property
._StandaloneQuarterSymbols
);
228 CFReleaseIfNotNull(formatter
->_property
._ShortStandaloneQuarterSymbols
);
229 CFReleaseIfNotNull(formatter
->_property
._AMSymbol
);
230 CFReleaseIfNotNull(formatter
->_property
._PMSymbol
);
231 CFReleaseIfNotNull(formatter
->_property
._AmbiguousYearStrategy
);
234 static CFStringRef
__CFDateFormatterCreateForcedString(CFDateFormatterRef formatter
, CFStringRef inString
);
236 static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter
, CFStringRef key
, CFTypeRef value
, Boolean directToICU
);
238 #define RESET_PROPERTY(C, K) \
239 if (df->_property. C) __CFDateFormatterSetProperty(df, K, df->_property. C, true);
241 // This blows away any custom format string the client may have set
242 // on the date formatter with CFDateFormatterSetFormat().
243 static void __ResetUDateFormat(CFDateFormatterRef df
, Boolean goingToHaveCustomFormat
) {
244 if (df
->_df
) udat_close(df
->_df
);
247 // uses _timeStyle, _dateStyle, _locale, _property._TimeZone; sets _df, _format, _defformat
248 char loc_buffer
[BUFFER_SIZE
];
250 CFStringRef tmpLocName
= df
->_locale
? CFLocaleGetIdentifier(df
->_locale
) : CFSTR("");
251 CFStringGetCString(tmpLocName
, loc_buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
);
253 UChar tz_buffer
[BUFFER_SIZE
];
255 CFStringRef tmpTZName
= df
->_property
._TimeZone
? CFTimeZoneGetName(df
->_property
._TimeZone
) : CFSTR("GMT");
256 CFStringGetCharacters(tmpTZName
, CFRangeMake(0, CFStringGetLength(tmpTZName
)), (UniChar
*)tz_buffer
);
258 df
->_property
._HasCustomFormat
= NULL
;
260 int32_t udstyle
= 0, utstyle
= 0;
261 switch (df
->_dateStyle
) {
262 case kCFDateFormatterNoStyle
: udstyle
= UDAT_NONE
; break;
263 case kCFDateFormatterShortStyle
: udstyle
= UDAT_SHORT
; break;
264 case kCFDateFormatterMediumStyle
: udstyle
= UDAT_MEDIUM
; break;
265 case kCFDateFormatterLongStyle
: udstyle
= UDAT_LONG
; break;
266 case kCFDateFormatterFullStyle
: udstyle
= UDAT_FULL
; break;
268 switch (df
->_timeStyle
) {
269 case kCFDateFormatterNoStyle
: utstyle
= UDAT_NONE
; break;
270 case kCFDateFormatterShortStyle
: utstyle
= UDAT_SHORT
; break;
271 case kCFDateFormatterMediumStyle
: utstyle
= UDAT_MEDIUM
; break;
272 case kCFDateFormatterLongStyle
: utstyle
= UDAT_LONG
; break;
273 case kCFDateFormatterFullStyle
: utstyle
= UDAT_FULL
; break;
275 Boolean wantRelative
= (NULL
!= df
->_property
._DoesRelativeDateFormatting
&& df
->_property
._DoesRelativeDateFormatting
== kCFBooleanTrue
);
276 Boolean hasFormat
= (NULL
!= df
->_property
._HasCustomFormat
&& df
->_property
._HasCustomFormat
== kCFBooleanTrue
) || goingToHaveCustomFormat
;
277 if (wantRelative
&& !hasFormat
&& kCFDateFormatterNoStyle
!= df
->_dateStyle
) {
278 udstyle
|= UDAT_RELATIVE
;
281 UErrorCode status
= U_ZERO_ERROR
;
282 UDateFormat
*icudf
= udat_open((UDateFormatStyle
)utstyle
, (UDateFormatStyle
)udstyle
, loc_buffer
, tz_buffer
, CFStringGetLength(tmpTZName
), NULL
, 0, &status
);
283 if (NULL
== icudf
|| U_FAILURE(status
)) {
286 udat_setLenient(icudf
, 0);
287 if (kCFDateFormatterNoStyle
== df
->_dateStyle
&& kCFDateFormatterNoStyle
== df
->_timeStyle
) {
288 if (wantRelative
&& !hasFormat
&& kCFDateFormatterNoStyle
!= df
->_dateStyle
) {
289 UErrorCode s
= U_ZERO_ERROR
;
290 udat_applyPatternRelative(icudf
, NULL
, 0, NULL
, 0, &s
);
292 udat_applyPattern(icudf
, false, NULL
, 0);
295 CFStringRef calident
= (CFStringRef
)CFLocaleGetValue(df
->_locale
, kCFLocaleCalendarIdentifierKey
);
296 if (calident
&& CFEqual(calident
, kCFCalendarIdentifierGregorian
)) {
297 status
= U_ZERO_ERROR
;
298 udat_set2DigitYearStart(icudf
, -631152000000.0, &status
); // 1950-01-01 00:00:00 GMT
302 __CFDateFormatterCustomize(df
);
304 if (wantRelative
&& !hasFormat
&& kCFDateFormatterNoStyle
!= df
->_dateStyle
) {
305 UChar dateBuffer
[BUFFER_SIZE
];
306 UChar timeBuffer
[BUFFER_SIZE
];
307 status
= U_ZERO_ERROR
;
308 CFIndex dateLen
= udat_toPatternRelativeDate(icudf
, dateBuffer
, BUFFER_SIZE
, &status
);
309 CFIndex timeLen
= (utstyle
!= UDAT_NONE
) ? udat_toPatternRelativeTime(icudf
, timeBuffer
, BUFFER_SIZE
, &status
) : 0;
310 if (U_SUCCESS(status
) && dateLen
<= BUFFER_SIZE
&& timeLen
<= BUFFER_SIZE
) {
311 // We assume that the 12/24-hour forcing preferences only affect the Time component
312 CFStringRef newFormat
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)timeBuffer
, timeLen
);
313 CFStringRef formatString
= __CFDateFormatterCreateForcedString(df
, newFormat
);
314 CFIndex cnt
= CFStringGetLength(formatString
);
315 CFAssert1(cnt
<= BUFFER_SIZE
, __kCFLogAssertion
, "%s(): time format string too long", __PRETTY_FUNCTION__
);
316 if (cnt
<= BUFFER_SIZE
) {
317 CFStringGetCharacters(formatString
, CFRangeMake(0, cnt
), (UniChar
*)timeBuffer
);
319 status
= U_ZERO_ERROR
;
320 udat_applyPatternRelative(icudf
, dateBuffer
, dateLen
, timeBuffer
, timeLen
, &status
);
321 // ignore error and proceed anyway, what else can be done?
323 UChar ubuffer
[BUFFER_SIZE
];
324 status
= U_ZERO_ERROR
;
325 int32_t ret
= udat_toPattern(icudf
, false, ubuffer
, BUFFER_SIZE
, &status
); // read out current pattern
326 if (U_SUCCESS(status
) && ret
<= BUFFER_SIZE
) {
327 if (df
->_format
) CFRelease(df
->_format
);
328 df
->_format
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)ubuffer
, ret
);
331 CFRelease(formatString
);
332 CFRelease(newFormat
);
335 UChar ubuffer
[BUFFER_SIZE
];
336 status
= U_ZERO_ERROR
;
337 int32_t ret
= udat_toPattern(icudf
, false, ubuffer
, BUFFER_SIZE
, &status
);
338 if (U_SUCCESS(status
) && ret
<= BUFFER_SIZE
) {
339 CFStringRef newFormat
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)ubuffer
, ret
);
340 CFStringRef formatString
= __CFDateFormatterCreateForcedString(df
, newFormat
);
341 CFIndex cnt
= CFStringGetLength(formatString
);
342 CFAssert1(cnt
<= 1024, __kCFLogAssertion
, "%s(): format string too long", __PRETTY_FUNCTION__
);
343 if (df
->_format
!= formatString
&& cnt
<= 1024) {
344 STACK_BUFFER_DECL(UChar
, ubuffer
, cnt
);
345 const UChar
*ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)formatString
);
347 CFStringGetCharacters(formatString
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
350 UErrorCode status
= U_ZERO_ERROR
;
351 // udat_applyPattern(df->_df, false, ustr, cnt, &status);
352 udat_applyPattern(df
->_df
, false, ustr
, cnt
);
353 if (U_SUCCESS(status
)) {
354 if (df
->_format
) CFRelease(df
->_format
);
355 df
->_format
= (CFStringRef
)CFStringCreateCopy(CFGetAllocator(df
), formatString
);
358 CFRelease(formatString
);
359 CFRelease(newFormat
);
362 if (df
->_defformat
) CFRelease(df
->_defformat
);
363 df
->_defformat
= df
->_format
? (CFStringRef
)CFRetain(df
->_format
) : NULL
;
365 CFStringRef calName
= df
->_property
._CalendarName
? (df
->_property
._CalendarName
) : NULL
;
367 calName
= (CFStringRef
)CFLocaleGetValue(df
->_locale
, kCFLocaleCalendarIdentifierKey
);
369 if (calName
&& CFEqual(calName
, kCFCalendarIdentifierGregorian
)) {
370 UCalendar
*cal
= (UCalendar
*)udat_getCalendar(df
->_df
);
371 status
= U_ZERO_ERROR
;
372 UDate udate
= ucal_getGregorianChange(cal
, &status
);
373 CFAbsoluteTime at
= U_SUCCESS(status
) ? (udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
) : -13197600000.0; // Oct 15, 1582
374 udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
375 status
= U_ZERO_ERROR
;
376 ucal_setGregorianChange(cal
, udate
, &status
);
379 RESET_PROPERTY(_IsLenient
, kCFDateFormatterIsLenientKey
);
380 RESET_PROPERTY(_DoesRelativeDateFormatting
, kCFDateFormatterDoesRelativeDateFormattingKey
);
381 RESET_PROPERTY(_Calendar
, kCFDateFormatterCalendarKey
);
382 RESET_PROPERTY(_CalendarName
, kCFDateFormatterCalendarIdentifierKey
);
383 RESET_PROPERTY(_TimeZone
, kCFDateFormatterTimeZoneKey
);
384 RESET_PROPERTY(_TwoDigitStartDate
, kCFDateFormatterTwoDigitStartDateKey
);
385 RESET_PROPERTY(_DefaultDate
, kCFDateFormatterDefaultDateKey
);
386 RESET_PROPERTY(_GregorianStartDate
, kCFDateFormatterGregorianStartDateKey
);
387 RESET_PROPERTY(_EraSymbols
, kCFDateFormatterEraSymbolsKey
);
388 RESET_PROPERTY(_LongEraSymbols
, kCFDateFormatterLongEraSymbolsKey
);
389 RESET_PROPERTY(_MonthSymbols
, kCFDateFormatterMonthSymbolsKey
);
390 RESET_PROPERTY(_ShortMonthSymbols
, kCFDateFormatterShortMonthSymbolsKey
);
391 RESET_PROPERTY(_VeryShortMonthSymbols
, kCFDateFormatterVeryShortMonthSymbolsKey
);
392 RESET_PROPERTY(_StandaloneMonthSymbols
, kCFDateFormatterStandaloneMonthSymbolsKey
);
393 RESET_PROPERTY(_ShortStandaloneMonthSymbols
, kCFDateFormatterShortStandaloneMonthSymbolsKey
);
394 RESET_PROPERTY(_VeryShortStandaloneMonthSymbols
, kCFDateFormatterVeryShortStandaloneMonthSymbolsKey
);
395 RESET_PROPERTY(_WeekdaySymbols
, kCFDateFormatterWeekdaySymbolsKey
);
396 RESET_PROPERTY(_ShortWeekdaySymbols
, kCFDateFormatterShortWeekdaySymbolsKey
);
397 RESET_PROPERTY(_VeryShortWeekdaySymbols
, kCFDateFormatterVeryShortWeekdaySymbolsKey
);
398 RESET_PROPERTY(_StandaloneWeekdaySymbols
, kCFDateFormatterStandaloneWeekdaySymbolsKey
);
399 RESET_PROPERTY(_ShortStandaloneWeekdaySymbols
, kCFDateFormatterShortStandaloneWeekdaySymbolsKey
);
400 RESET_PROPERTY(_VeryShortStandaloneWeekdaySymbols
, kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey
);
401 RESET_PROPERTY(_QuarterSymbols
, kCFDateFormatterQuarterSymbolsKey
);
402 RESET_PROPERTY(_ShortQuarterSymbols
, kCFDateFormatterShortQuarterSymbolsKey
);
403 RESET_PROPERTY(_StandaloneQuarterSymbols
, kCFDateFormatterStandaloneQuarterSymbolsKey
);
404 RESET_PROPERTY(_ShortStandaloneQuarterSymbols
, kCFDateFormatterShortStandaloneQuarterSymbolsKey
);
405 RESET_PROPERTY(_AMSymbol
, kCFDateFormatterAMSymbolKey
);
406 RESET_PROPERTY(_PMSymbol
, kCFDateFormatterPMSymbolKey
);
407 RESET_PROPERTY(_AmbiguousYearStrategy
, kCFDateFormatterAmbiguousYearStrategyKey
);
410 static CFTypeID __kCFDateFormatterTypeID
= _kCFRuntimeNotATypeID
;
412 static const CFRuntimeClass __CFDateFormatterClass
= {
417 __CFDateFormatterDeallocate
,
421 __CFDateFormatterCopyDescription
424 static void __CFDateFormatterInitialize(void) {
425 __kCFDateFormatterTypeID
= _CFRuntimeRegisterClass(&__CFDateFormatterClass
);
428 CFTypeID
CFDateFormatterGetTypeID(void) {
429 if (_kCFRuntimeNotATypeID
== __kCFDateFormatterTypeID
) __CFDateFormatterInitialize();
430 return __kCFDateFormatterTypeID
;
433 CFDateFormatterRef
CFDateFormatterCreate(CFAllocatorRef allocator
, CFLocaleRef locale
, CFDateFormatterStyle dateStyle
, CFDateFormatterStyle timeStyle
) {
434 struct __CFDateFormatter
*memory
;
435 uint32_t size
= sizeof(struct __CFDateFormatter
) - sizeof(CFRuntimeBase
);
436 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
437 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
438 if (locale
) __CFGenericValidateType(locale
, CFLocaleGetTypeID());
439 memory
= (struct __CFDateFormatter
*)_CFRuntimeCreateInstance(allocator
, CFDateFormatterGetTypeID(), size
, NULL
);
440 if (NULL
== memory
) {
444 memory
->_locale
= NULL
;
445 memory
->_format
= NULL
;
446 memory
->_defformat
= NULL
;
447 memory
->_dateStyle
= dateStyle
;
448 memory
->_timeStyle
= timeStyle
;
449 memory
->_property
._IsLenient
= NULL
;
450 memory
->_property
._DoesRelativeDateFormatting
= NULL
;
451 memory
->_property
._HasCustomFormat
= NULL
;
452 memory
->_property
._TimeZone
= NULL
;
453 memory
->_property
._Calendar
= NULL
;
454 memory
->_property
._CalendarName
= NULL
;
455 memory
->_property
._TwoDigitStartDate
= NULL
;
456 memory
->_property
._DefaultDate
= NULL
;
457 memory
->_property
._GregorianStartDate
= NULL
;
458 memory
->_property
._EraSymbols
= NULL
;
459 memory
->_property
._LongEraSymbols
= NULL
;
460 memory
->_property
._MonthSymbols
= NULL
;
461 memory
->_property
._ShortMonthSymbols
= NULL
;
462 memory
->_property
._VeryShortMonthSymbols
= NULL
;
463 memory
->_property
._StandaloneMonthSymbols
= NULL
;
464 memory
->_property
._ShortStandaloneMonthSymbols
= NULL
;
465 memory
->_property
._VeryShortStandaloneMonthSymbols
= NULL
;
466 memory
->_property
._WeekdaySymbols
= NULL
;
467 memory
->_property
._ShortWeekdaySymbols
= NULL
;
468 memory
->_property
._VeryShortWeekdaySymbols
= NULL
;
469 memory
->_property
._StandaloneWeekdaySymbols
= NULL
;
470 memory
->_property
._ShortStandaloneWeekdaySymbols
= NULL
;
471 memory
->_property
._VeryShortStandaloneWeekdaySymbols
= NULL
;
472 memory
->_property
._QuarterSymbols
= NULL
;
473 memory
->_property
._ShortQuarterSymbols
= NULL
;
474 memory
->_property
._StandaloneQuarterSymbols
= NULL
;
475 memory
->_property
._ShortStandaloneQuarterSymbols
= NULL
;
476 memory
->_property
._AMSymbol
= NULL
;
477 memory
->_property
._PMSymbol
= NULL
;
478 memory
->_property
._AmbiguousYearStrategy
= NULL
;
481 case kCFDateFormatterNoStyle
:
482 case kCFDateFormatterShortStyle
:
483 case kCFDateFormatterMediumStyle
:
484 case kCFDateFormatterLongStyle
:
485 case kCFDateFormatterFullStyle
: break;
487 CFAssert2(0, __kCFLogAssertion
, "%s(): unknown date style %d", __PRETTY_FUNCTION__
, dateStyle
);
488 memory
->_dateStyle
= kCFDateFormatterMediumStyle
;
492 case kCFDateFormatterNoStyle
:
493 case kCFDateFormatterShortStyle
:
494 case kCFDateFormatterMediumStyle
:
495 case kCFDateFormatterLongStyle
:
496 case kCFDateFormatterFullStyle
: break;
498 CFAssert2(0, __kCFLogAssertion
, "%s(): unknown time style %d", __PRETTY_FUNCTION__
, timeStyle
);
499 memory
->_timeStyle
= kCFDateFormatterMediumStyle
;
503 memory
->_locale
= locale
? CFLocaleCreateCopy(allocator
, locale
) : (CFLocaleRef
)CFRetain(CFLocaleGetSystem());
504 memory
->_property
._TimeZone
= CFTimeZoneCopyDefault();
505 __ResetUDateFormat(memory
, false);
510 return (CFDateFormatterRef
)memory
;
513 extern CFDictionaryRef
__CFLocaleGetPrefs(CFLocaleRef locale
);
515 static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter
) {
516 CFDictionaryRef prefs
= __CFLocaleGetPrefs(formatter
->_locale
);
518 CFIndex dateLen
= -1;
519 UChar dateBuffer
[BUFFER_SIZE
];
520 if (kCFDateFormatterNoStyle
!= formatter
->_dateStyle
) {
521 CFPropertyListRef metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUDateFormatStrings")) : NULL
;
522 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
524 switch (formatter
->_dateStyle
) {
525 case kCFDateFormatterShortStyle
: key
= CFSTR("1"); break;
526 case kCFDateFormatterMediumStyle
: key
= CFSTR("2"); break;
527 case kCFDateFormatterLongStyle
: key
= CFSTR("3"); break;
528 case kCFDateFormatterFullStyle
: key
= CFSTR("4"); break;
529 default: key
= CFSTR("0"); break;
531 CFStringRef pref
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)metapref
, key
);
532 if (NULL
!= pref
&& CFGetTypeID(pref
) == CFStringGetTypeID()) {
533 dateLen
= __CFMin(CFStringGetLength(pref
), BUFFER_SIZE
);
534 CFStringGetCharacters(pref
, CFRangeMake(0, dateLen
), (UniChar
*)dateBuffer
);
539 UErrorCode status
= U_ZERO_ERROR
;
540 int32_t ret
= udat_toPatternRelativeDate(formatter
->_df
, dateBuffer
, BUFFER_SIZE
, &status
);
541 if (!U_FAILURE(status
)) {
546 CFIndex timeLen
= -1;
547 UChar timeBuffer
[BUFFER_SIZE
];
548 if (kCFDateFormatterNoStyle
!= formatter
->_timeStyle
) {
549 CFPropertyListRef metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUTimeFormatStrings")) : NULL
;
550 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
552 switch (formatter
->_timeStyle
) {
553 case kCFDateFormatterShortStyle
: key
= CFSTR("1"); break;
554 case kCFDateFormatterMediumStyle
: key
= CFSTR("2"); break;
555 case kCFDateFormatterLongStyle
: key
= CFSTR("3"); break;
556 case kCFDateFormatterFullStyle
: key
= CFSTR("4"); break;
557 default: key
= CFSTR("0"); break;
559 CFStringRef pref
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)metapref
, key
);
560 if (NULL
!= pref
&& CFGetTypeID(pref
) == CFStringGetTypeID()) {
561 timeLen
= __CFMin(CFStringGetLength(pref
), BUFFER_SIZE
);
562 CFStringGetCharacters(pref
, CFRangeMake(0, timeLen
), (UniChar
*)timeBuffer
);
567 UErrorCode status
= U_ZERO_ERROR
;
568 int32_t ret
= udat_toPatternRelativeTime(formatter
->_df
, timeBuffer
, BUFFER_SIZE
, &status
);
569 if (!U_FAILURE(status
)) {
574 UErrorCode status
= U_ZERO_ERROR
;
575 udat_applyPatternRelative(formatter
->_df
, (0 <= dateLen
) ? dateBuffer
: NULL
, (0 <= dateLen
) ? dateLen
: 0, (0 <= timeLen
) ? timeBuffer
: NULL
, (0 <= timeLen
) ? timeLen
: 0, &status
);
578 static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter
, bool doTime
) {
579 CFIndex formatStyle
= doTime
? formatter
->_timeStyle
: formatter
->_dateStyle
;
580 CFStringRef prefName
= doTime
? CFSTR("AppleICUTimeFormatStrings") : CFSTR("AppleICUDateFormatStrings");
581 if (kCFDateFormatterNoStyle
!= formatStyle
) {
582 CFStringRef pref
= NULL
;
583 CFDictionaryRef prefs
= __CFLocaleGetPrefs(formatter
->_locale
);
584 CFPropertyListRef metapref
= prefs
? CFDictionaryGetValue(prefs
, prefName
) : NULL
;
585 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
587 switch (formatStyle
) {
588 case kCFDateFormatterShortStyle
: key
= CFSTR("1"); break;
589 case kCFDateFormatterMediumStyle
: key
= CFSTR("2"); break;
590 case kCFDateFormatterLongStyle
: key
= CFSTR("3"); break;
591 case kCFDateFormatterFullStyle
: key
= CFSTR("4"); break;
592 default: key
= CFSTR("0"); break;
594 pref
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)metapref
, key
);
596 if (NULL
!= pref
&& CFGetTypeID(pref
) == CFStringGetTypeID()) {
597 int32_t icustyle
= UDAT_NONE
;
598 switch (formatStyle
) {
599 case kCFDateFormatterShortStyle
: icustyle
= UDAT_SHORT
; break;
600 case kCFDateFormatterMediumStyle
: icustyle
= UDAT_MEDIUM
; break;
601 case kCFDateFormatterLongStyle
: icustyle
= UDAT_LONG
; break;
602 case kCFDateFormatterFullStyle
: icustyle
= UDAT_FULL
; break;
604 CFStringRef localeName
= CFLocaleGetIdentifier(formatter
->_locale
);
605 char buffer
[BUFFER_SIZE
];
606 const char *cstr
= CFStringGetCStringPtr(localeName
, kCFStringEncodingASCII
);
608 if (CFStringGetCString(localeName
, buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
)) cstr
= buffer
;
610 UErrorCode status
= U_ZERO_ERROR
;
611 UDateFormat
*df
= udat_open((UDateFormatStyle
)(doTime
? icustyle
: UDAT_NONE
), (UDateFormatStyle
)(doTime
? UDAT_NONE
: icustyle
), cstr
, NULL
, 0, NULL
, 0, &status
);
613 UChar ubuffer
[BUFFER_SIZE
];
614 status
= U_ZERO_ERROR
;
615 int32_t date_len
= udat_toPattern(df
, false, ubuffer
, BUFFER_SIZE
, &status
);
616 if (U_SUCCESS(status
) && date_len
<= BUFFER_SIZE
) {
617 CFStringRef dateString
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (UniChar
*)ubuffer
, date_len
);
618 status
= U_ZERO_ERROR
;
619 int32_t formatter_len
= udat_toPattern(formatter
->_df
, false, ubuffer
, BUFFER_SIZE
, &status
);
620 if (U_SUCCESS(status
) && formatter_len
<= BUFFER_SIZE
) {
621 CFMutableStringRef formatString
= CFStringCreateMutable(kCFAllocatorSystemDefault
, 0);
622 CFStringAppendCharacters(formatString
, (UniChar
*)ubuffer
, formatter_len
);
623 // find dateString inside formatString, substitute the pref in that range
625 if (CFStringFindWithOptions(formatString
, dateString
, CFRangeMake(0, formatter_len
), 0, &result
)) {
626 CFStringReplace(formatString
, result
, pref
);
627 int32_t new_len
= CFStringGetLength(formatString
);
628 STACK_BUFFER_DECL(UChar
, new_buffer
, new_len
);
629 const UChar
*new_ustr
= (UChar
*)CFStringGetCharactersPtr(formatString
);
630 if (NULL
== new_ustr
) {
631 CFStringGetCharacters(formatString
, CFRangeMake(0, new_len
), (UniChar
*)new_buffer
);
632 new_ustr
= new_buffer
;
634 status
= U_ZERO_ERROR
;
635 // udat_applyPattern(formatter->_df, false, new_ustr, new_len, &status);
636 udat_applyPattern(formatter
->_df
, false, new_ustr
, new_len
);
638 CFRelease(formatString
);
640 CFRelease(dateString
);
648 static void __CFDateFormatterApplySymbolPrefs(const void *key
, const void *value
, void *context
) {
649 if (CFGetTypeID(key
) == CFStringGetTypeID() && CFGetTypeID(value
) == CFArrayGetTypeID()) {
650 CFDateFormatterRef formatter
= (CFDateFormatterRef
)context
;
651 UDateFormatSymbolType sym
= (UDateFormatSymbolType
)CFStringGetIntValue((CFStringRef
)key
);
652 CFArrayRef array
= (CFArrayRef
)value
;
653 CFIndex idx
, cnt
= CFArrayGetCount(array
);
654 for (idx
= 0; idx
< cnt
; idx
++) {
655 CFStringRef item
= (CFStringRef
)CFArrayGetValueAtIndex(array
, idx
);
656 if (CFGetTypeID(item
) != CFStringGetTypeID()) continue;
657 CFIndex item_cnt
= CFStringGetLength(item
);
658 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
659 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr(item
);
660 if (NULL
== item_ustr
) {
661 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
662 CFStringGetCharacters(item
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
663 item_ustr
= item_buffer
;
665 UErrorCode status
= U_ZERO_ERROR
;
666 udat_setSymbols(formatter
->_df
, sym
, idx
, item_ustr
, item_cnt
, &status
);
671 static CFStringRef
__CFDateFormatterCreateForcedTemplate(CFLocaleRef locale
, CFStringRef inString
) {
672 if (!inString
) return NULL
;
674 Boolean doForce24
= false, doForce12
= false;
675 CFDictionaryRef prefs
= __CFLocaleGetPrefs(locale
);
676 CFPropertyListRef pref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUForce24HourTime")) : NULL
;
677 if (NULL
!= pref
&& CFGetTypeID(pref
) == CFBooleanGetTypeID()) {
678 doForce24
= CFBooleanGetValue((CFBooleanRef
)pref
);
680 pref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUForce12HourTime")) : NULL
;
681 if (NULL
!= pref
&& CFGetTypeID(pref
) == CFBooleanGetTypeID()) {
682 doForce12
= CFBooleanGetValue((CFBooleanRef
)pref
);
684 if (doForce24
) doForce12
= false; // if both are set, Force24 wins, period
685 if (!doForce24
&& !doForce12
) return (CFStringRef
)CFRetain(inString
);
687 CFMutableStringRef outString
= CFStringCreateMutable(kCFAllocatorSystemDefault
, 0);
688 CFIndex cnt
= CFStringGetLength(inString
);
689 CFIndex lastSecond
= -1, lastMinute
= -1, firstHour
= -1;
690 Boolean isInQuote
= false, hasA
= false, had12Hour
= false, had24Hour
= false;
691 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
693 UniChar ch
= CFStringGetCharacterAtIndex(inString
, idx
);
695 case '\'': isInQuote
= !isInQuote
; break;
696 case 'j': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); if (doForce24
) ch
= 'H'; else ch
= 'h';} break;
697 case 'h': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had12Hour
= true; if (doForce24
) ch
= 'H';} break; // switch 12-hour to 24-hour
698 case 'K': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had12Hour
= true; if (doForce24
) ch
= 'k';} break; // switch 12-hour to 24-hour
699 case 'H': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had24Hour
= true; if (doForce12
) ch
= 'h';} break; // switch 24-hour to 12-hour
700 case 'k': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had24Hour
= true; if (doForce12
) ch
= 'K';} break; // switch 24-hour to 12-hour
701 case 'm': if (!isInQuote
) lastMinute
= CFStringGetLength(outString
); break;
702 case 's': if (!isInQuote
) lastSecond
= CFStringGetLength(outString
); break;
703 case 'a': if (!isInQuote
) {hasA
= true; if (doForce24
) emit
= false;} break;
706 if (emit
) CFStringAppendCharacters(outString
, &ch
, 1);
712 static CFStringRef
__CFDateFormatterCreateForcedString(CFDateFormatterRef formatter
, CFStringRef inString
) {
713 if (!inString
) return NULL
;
715 Boolean doForce24
= false, doForce12
= false;
716 CFDictionaryRef prefs
= __CFLocaleGetPrefs(formatter
->_locale
);
717 CFPropertyListRef pref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUForce24HourTime")) : NULL
;
718 if (NULL
!= pref
&& CFGetTypeID(pref
) == CFBooleanGetTypeID()) {
719 doForce24
= CFBooleanGetValue((CFBooleanRef
)pref
);
721 pref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUForce12HourTime")) : NULL
;
722 if (NULL
!= pref
&& CFGetTypeID(pref
) == CFBooleanGetTypeID()) {
723 doForce12
= CFBooleanGetValue((CFBooleanRef
)pref
);
725 if (doForce24
) doForce12
= false; // if both are set, Force24 wins, period
726 if (!doForce24
&& !doForce12
) return (CFStringRef
)CFRetain(inString
);
728 CFMutableStringRef outString
= CFStringCreateMutable(kCFAllocatorSystemDefault
, 0);
729 CFIndex cnt
= CFStringGetLength(inString
);
730 CFIndex lastSecond
= -1, lastMinute
= -1, firstHour
= -1;
731 Boolean isInQuote
= false, hasA
= false, had12Hour
= false, had24Hour
= false;
732 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
734 UniChar ch
= CFStringGetCharacterAtIndex(inString
, idx
);
736 case '\'': isInQuote
= !isInQuote
; break;
737 case 'h': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had12Hour
= true; if (doForce24
) ch
= 'H';} break; // switch 12-hour to 24-hour
738 case 'K': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had12Hour
= true; if (doForce24
) ch
= 'k';} break; // switch 12-hour to 24-hour
739 case 'H': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had24Hour
= true; if (doForce12
) ch
= 'h';} break; // switch 24-hour to 12-hour
740 case 'k': if (!isInQuote
) {if (-1 == firstHour
) firstHour
= CFStringGetLength(outString
); had24Hour
= true; if (doForce12
) ch
= 'K';} break; // switch 24-hour to 12-hour
741 case 'm': if (!isInQuote
) lastMinute
= CFStringGetLength(outString
); break;
742 case 's': if (!isInQuote
) lastSecond
= CFStringGetLength(outString
); break;
743 case 'a': if (!isInQuote
) hasA
= true;
744 if (!isInQuote
&& doForce24
) {
745 // skip 'a' and one optional trailing space
747 if (idx
+ 1 < cnt
&& ' ' == CFStringGetCharacterAtIndex(inString
, idx
+ 1)) idx
++;
751 if (!isInQuote
&& doForce24
) {
752 // if next character is 'a' AND we have seen the hour designator, skip space and 'a'
753 if (idx
+ 1 < cnt
&& 'a' == CFStringGetCharacterAtIndex(inString
, idx
+ 1) && -1 != firstHour
) {
760 if (emit
) CFStringAppendCharacters(outString
, &ch
, 1);
762 if (doForce12
&& !hasA
&& had24Hour
) {
763 CFStringRef locName
= CFLocaleGetIdentifier(formatter
->_locale
);
764 if (-1 != firstHour
&& (CFStringHasPrefix(locName
, CFSTR("ko")) || CFEqual(locName
, CFSTR("zh_SG")))) {
765 CFStringInsert(outString
, firstHour
, CFSTR("a "));
766 } else if (-1 != firstHour
&& (CFStringHasPrefix(locName
, CFSTR("zh")) || CFStringHasPrefix(locName
, CFSTR("ja")))) {
767 CFStringInsert(outString
, firstHour
, CFSTR("a"));
769 CFIndex lastPos
= (-1 != lastSecond
) ? lastSecond
: ((-1 != lastMinute
) ? lastMinute
: -1);
771 cnt
= CFStringGetLength(outString
);
773 UniChar ch
= (lastPos
< cnt
) ? CFStringGetCharacterAtIndex(outString
, lastPos
) : 0;
775 case '\"': lastPos
++; break;
780 ch
= (lastPos
< cnt
) ? CFStringGetCharacterAtIndex(outString
, lastPos
) : 0;
781 } while ('\'' != ch
&& '\0' != ch
);
782 if ('\'' == ch
) lastPos
++;
783 ch
= (lastPos
< cnt
) ? CFStringGetCharacterAtIndex(outString
, lastPos
) : 0;
784 if ('\'' == ch
) goto again
;
787 CFStringInsert(outString
, lastPos
, CFSTR(" a"));
794 static void __CFDateFormatterCustomize(CFDateFormatterRef formatter
) {
795 Boolean wantRelative
= (NULL
!= formatter
->_property
._DoesRelativeDateFormatting
&& formatter
->_property
._DoesRelativeDateFormatting
== kCFBooleanTrue
);
796 Boolean hasFormat
= (NULL
!= formatter
->_property
._HasCustomFormat
&& formatter
->_property
._HasCustomFormat
== kCFBooleanTrue
);
797 if (wantRelative
&& !hasFormat
&& kCFDateFormatterNoStyle
!= formatter
->_dateStyle
) {
798 __substituteFormatStringFromPrefsDFRelative(formatter
);
800 __substituteFormatStringFromPrefsDF(formatter
, false);
801 __substituteFormatStringFromPrefsDF(formatter
, true);
803 CFDictionaryRef prefs
= __CFLocaleGetPrefs(formatter
->_locale
);
804 CFPropertyListRef metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleICUDateTimeSymbols")) : NULL
;
805 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
806 CFDictionaryApplyFunction((CFDictionaryRef
)metapref
, __CFDateFormatterApplySymbolPrefs
, formatter
);
808 metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleFirstWeekday")) : NULL
;
809 CFStringRef calID
= (CFStringRef
)CFLocaleGetValue(formatter
->_locale
, kCFLocaleCalendarIdentifierKey
);
810 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
811 metapref
= (CFNumberRef
)CFDictionaryGetValue((CFDictionaryRef
)metapref
, calID
);
813 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFNumberGetTypeID()) {
815 if (CFNumberGetValue((CFNumberRef
)metapref
, kCFNumberCFIndexType
, &wkdy
)) {
816 UCalendar
*cal
= (UCalendar
*)udat_getCalendar(formatter
->_df
);
817 if (cal
) ucal_setAttribute(cal
, UCAL_FIRST_DAY_OF_WEEK
, wkdy
);
820 metapref
= prefs
? CFDictionaryGetValue(prefs
, CFSTR("AppleMinDaysInFirstWeek")) : NULL
;
821 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFDictionaryGetTypeID()) {
822 metapref
= (CFNumberRef
)CFDictionaryGetValue((CFDictionaryRef
)metapref
, calID
);
824 if (NULL
!= metapref
&& CFGetTypeID(metapref
) == CFNumberGetTypeID()) {
826 if (CFNumberGetValue((CFNumberRef
)metapref
, kCFNumberCFIndexType
, &mwd
)) {
827 UCalendar
*cal
= (UCalendar
*)udat_getCalendar(formatter
->_df
);
828 if (cal
) ucal_setAttribute(cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
, mwd
);
833 CFLocaleRef
CFDateFormatterGetLocale(CFDateFormatterRef formatter
) {
834 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
835 return formatter
->_locale
;
838 CFDateFormatterStyle
CFDateFormatterGetDateStyle(CFDateFormatterRef formatter
) {
839 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
840 return formatter
->_dateStyle
;
843 CFDateFormatterStyle
CFDateFormatterGetTimeStyle(CFDateFormatterRef formatter
) {
844 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
845 return formatter
->_timeStyle
;
848 CFStringRef
CFDateFormatterGetFormat(CFDateFormatterRef formatter
) {
849 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
850 return formatter
->_format
;
853 void CFDateFormatterSetFormat(CFDateFormatterRef formatter
, CFStringRef formatString
) {
854 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
855 __CFGenericValidateType(formatString
, CFStringGetTypeID());
856 formatString
= __CFDateFormatterCreateForcedString(formatter
, formatString
);
857 CFIndex cnt
= CFStringGetLength(formatString
);
858 CFAssert1(cnt
<= 1024, __kCFLogAssertion
, "%s(): format string too long", __PRETTY_FUNCTION__
);
859 if (formatter
->_format
!= formatString
&& cnt
<= 1024) {
860 // When going from a situation where there is no custom format already,
861 // and the "relative date formatting" property is set, we need to reset
862 // the whole UDateFormat.
863 if (formatter
->_property
._HasCustomFormat
!= kCFBooleanTrue
&& formatter
->_property
._DoesRelativeDateFormatting
== kCFBooleanTrue
) {
864 __ResetUDateFormat(formatter
, true);
865 // the "true" results in: if you set a custom format string, you don't get relative date formatting
867 STACK_BUFFER_DECL(UChar
, ubuffer
, cnt
);
868 const UChar
*ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)formatString
);
870 CFStringGetCharacters(formatString
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
873 UErrorCode status
= U_ZERO_ERROR
;
874 // udat_applyPattern(formatter->_df, false, ustr, cnt, &status);
875 udat_applyPattern(formatter
->_df
, false, ustr
, cnt
);
876 if (U_SUCCESS(status
)) {
877 if (formatter
->_format
) CFRelease(formatter
->_format
);
878 formatter
->_format
= (CFStringRef
)CFStringCreateCopy(CFGetAllocator(formatter
), formatString
);
879 formatter
->_property
._HasCustomFormat
= kCFBooleanTrue
;
882 if (formatString
) CFRelease(formatString
);
885 CFStringRef
CFDateFormatterCreateStringWithDate(CFAllocatorRef allocator
, CFDateFormatterRef formatter
, CFDateRef date
) {
886 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
887 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
888 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
889 __CFGenericValidateType(date
, CFDateGetTypeID());
890 return CFDateFormatterCreateStringWithAbsoluteTime(allocator
, formatter
, CFDateGetAbsoluteTime(date
));
893 CFStringRef
CFDateFormatterCreateStringWithAbsoluteTime(CFAllocatorRef allocator
, CFDateFormatterRef formatter
, CFAbsoluteTime at
) {
894 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
895 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
896 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
897 UChar
*ustr
= NULL
, ubuffer
[BUFFER_SIZE
];
898 UErrorCode status
= U_ZERO_ERROR
;
899 CFIndex used
, cnt
= BUFFER_SIZE
;
900 UDate ud
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0 + 0.5;
901 used
= udat_format(formatter
->_df
, ud
, ubuffer
, cnt
, NULL
, &status
);
902 if (status
== U_BUFFER_OVERFLOW_ERROR
|| cnt
< used
) {
904 ustr
= (UChar
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(UChar
) * cnt
, 0);
905 status
= U_ZERO_ERROR
;
906 used
= udat_format(formatter
->_df
, ud
, ustr
, cnt
, NULL
, &status
);
908 CFStringRef string
= NULL
;
909 if (U_SUCCESS(status
)) {
910 string
= CFStringCreateWithCharacters(allocator
, (const UniChar
*)(ustr
? ustr
: ubuffer
), used
);
912 if (ustr
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, ustr
);
916 static UDate
__CFDateFormatterCorrectTimeWithTarget(UCalendar
*calendar
, UDate at
, int32_t target
, Boolean isEra
, UErrorCode
*status
) {
917 ucal_setMillis(calendar
, at
, status
);
918 UCalendarDateFields field
= isEra
? UCAL_ERA
: UCAL_YEAR
;
919 ucal_set(calendar
, field
, target
);
920 return ucal_getMillis(calendar
, status
);
923 static UDate
__CFDateFormatterCorrectTimeToARangeAroundCurrentDate(UCalendar
*calendar
, UDate at
, CFIndex period
, CFIndex pastYears
, CFIndex futureYears
, Boolean isEra
, UErrorCode
*status
) {
924 ucal_setMillis(calendar
, ucal_getNow(), status
);
925 int32_t currYear
= ucal_get(calendar
, UCAL_YEAR
, status
);
926 UCalendarDateFields field
= isEra
? UCAL_ERA
: UCAL_YEAR
;
927 int32_t currEraOrCentury
= ucal_get(calendar
, field
, status
);
930 currEraOrCentury
= currEraOrCentury
/ 100 * 100; // get century
933 CFIndex futureMax
= currYear
+ futureYears
;
934 CFIndex pastMin
= currYear
- pastYears
;
936 CFRange currRange
, futureRange
, pastRange
;
937 currRange
.location
= futureRange
.location
= pastRange
.location
= kCFNotFound
;
938 currRange
.length
= futureRange
.length
= pastRange
.length
= 0;
940 if (period
< INT_MAX
&& futureMax
>= period
) {
941 futureRange
.location
= 0;
942 futureRange
.length
= futureMax
- period
+ 1;
945 pastRange
.location
= period
+ pastMin
;
946 pastRange
.length
= period
- pastRange
.location
;
948 if (pastRange
.location
!= kCFNotFound
) {
949 currRange
.location
= 0;
951 currRange
.location
= pastMin
;
954 if (period
< INT_MAX
&& futureMax
> period
) {
955 futureRange
.location
= 1,
956 futureRange
.length
= futureMax
- period
;
959 pastRange
.location
= period
+ pastMin
;
960 pastRange
.length
= period
- pastRange
.location
+ 1;
962 if (pastRange
.location
!= kCFNotFound
) {
963 currRange
.location
= 1;
965 currRange
.location
= pastMin
;
969 currRange
.length
= period
- pastRange
.length
- futureRange
.length
;
971 ucal_setMillis(calendar
, at
, status
);
972 int32_t atYear
= ucal_get(calendar
, UCAL_YEAR
, status
);
975 currEraOrCentury
+= atYear
;
978 int32_t offset
= 0; // current era or century
979 if (pastRange
.location
!= kCFNotFound
&& atYear
>= pastRange
.location
&& atYear
- pastRange
.location
+ 1 <= pastRange
.length
) {
980 offset
= -1; // past era or century
981 } else if (futureRange
.location
!= kCFNotFound
&& atYear
>= futureRange
.location
&& atYear
- futureRange
.location
+ 1 <= futureRange
.length
) {
982 offset
= 1; // next era or century
984 if (!isEra
) offset
*= 100;
985 return __CFDateFormatterCorrectTimeWithTarget(calendar
, at
, currEraOrCentury
+offset
, isEra
, status
);
988 static int32_t __CFDateFormatterGetMaxYearGivenJapaneseEra(UCalendar
*calendar
, int32_t era
, UErrorCode
*status
) {
990 ucal_clear(calendar
);
991 ucal_set(calendar
, UCAL_ERA
, era
+1);
992 UDate target
= ucal_getMillis(calendar
, status
);
993 ucal_set(calendar
, UCAL_ERA
, era
);
994 years
= ucal_getFieldDifference(calendar
, target
, UCAL_YEAR
, status
);
998 static Boolean
__CFDateFormatterHandleAmbiguousYear(CFDateFormatterRef formatter
, CFStringRef calendar_id
, UDateFormat
*df
, UCalendar
*cal
, UDate
*at
, const UChar
*ustr
, CFIndex length
, UErrorCode
*status
) {
999 Boolean success
= true;
1000 int64_t ambigStrat
= kCFDateFormatterAmbiguousYearAssumeToNone
;
1001 if (formatter
->_property
._AmbiguousYearStrategy
) {
1002 CFNumberGetValue(formatter
->_property
._AmbiguousYearStrategy
, kCFNumberSInt64Type
, &ambigStrat
);
1004 if (calendar_id
== kCFCalendarIdentifierChinese
) {
1005 // 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
1006 // now I just assume that the year will not be greater than 600 in the string
1007 if (ucal_get(cal
, UCAL_ERA
, status
) < 10) {
1008 switch (ambigStrat
) {
1009 case kCFDateFormatterAmbiguousYearFailToParse
:
1012 case kCFDateFormatterAmbiguousYearAssumeToCurrent
: {
1013 ucal_setMillis(cal
, ucal_getNow(), status
);
1014 int32_t currEra
= ucal_get(cal
, UCAL_ERA
, status
);
1015 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1018 case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate
:
1019 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 60, 29, 30, true, status
);
1021 case kCFDateFormatterAmbiguousYearAssumeToFuture
:
1022 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 60, 0, 59, true, status
);
1024 case kCFDateFormatterAmbiguousYearAssumeToPast
:
1025 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 60, 59, 0, true, status
);
1027 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture
:
1028 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 60, 10, 49, true, status
);
1030 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast
:
1031 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 60, 49, 10, true, status
);
1033 case kCFDateFormatterAmbiguousYearAssumeToNone
:
1035 break; // do nothing
1038 } else if (calendar_id
== kCFCalendarIdentifierJapanese
) { // ??? need more work
1040 ucal_set(cal
, UCAL_ERA
, 1);
1041 udat_parseCalendar(df
, cal
, ustr
, length
, NULL
, status
);
1042 UDate test
= ucal_getMillis(cal
, status
);
1043 if (test
!= *at
) { // missing era
1044 ucal_setMillis(cal
, *at
, status
);
1045 int32_t givenYear
= ucal_get(cal
, UCAL_YEAR
, status
);
1046 ucal_setMillis(cal
, ucal_getNow(), status
);
1047 int32_t currYear
= ucal_get(cal
, UCAL_YEAR
, status
);
1048 int32_t currEra
= ucal_get(cal
, UCAL_ERA
, status
);
1049 switch (ambigStrat
) {
1050 case kCFDateFormatterAmbiguousYearFailToParse
:
1053 case kCFDateFormatterAmbiguousYearAssumeToCurrent
:
1054 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1056 case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate
:
1057 // we allow the ball up to 30 years
1058 // if the given year is larger than the current year + 30 years, we check the previous era
1059 if (givenYear
> currYear
+ 30) {
1060 success
= false; // if the previous era cannot have the given year, fail the parse
1061 int32_t years
= __CFDateFormatterGetMaxYearGivenJapaneseEra(cal
, currEra
-1, status
);
1062 if (givenYear
<= years
) {
1064 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
-1, true, status
);
1066 } else { // current era
1067 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1070 case kCFDateFormatterAmbiguousYearAssumeToFuture
:
1071 if (givenYear
< currYear
) { // we only consider current or the future
1073 } else { // current era
1074 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1077 case kCFDateFormatterAmbiguousYearAssumeToPast
:
1078 if (givenYear
> currYear
) { // past era
1080 // we find the closest era that has the given year
1081 // if no era has such given year, we fail the parse
1082 for (CFIndex era
= currEra
-1; era
>= 234; era
--) { // Showa era (234) is the longest era
1083 int32_t years
= __CFDateFormatterGetMaxYearGivenJapaneseEra(cal
, era
, status
);
1084 if (givenYear
> years
) {
1088 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, era
, true, status
);
1091 } else { // current era
1092 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1095 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture
:
1096 if (givenYear
< currYear
- 10) { // we allow 10 years to the past
1099 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1102 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast
:
1103 if (givenYear
> currYear
+ 10) {
1105 // we find the closest era that has the given year
1106 // if no era has such given year, we fail the parse
1107 for (CFIndex era
= currEra
-1; era
>= 234; era
--) { // Showa era (234) is the longest era
1108 int32_t years
= __CFDateFormatterGetMaxYearGivenJapaneseEra(cal
, era
, status
);
1109 if (givenYear
> years
) {
1113 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, era
, true, status
);
1116 } else { // current era
1117 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, currEra
, true, status
);
1120 case kCFDateFormatterAmbiguousYearAssumeToNone
:
1122 break; // do nothing
1125 } else { // calenders other than chinese and japanese
1126 int32_t parsedYear
= ucal_get(cal
, UCAL_YEAR
, status
);
1127 ucal_setMillis(cal
, ucal_getNow(), status
);
1128 int32_t currYear
= ucal_get(cal
, UCAL_YEAR
, status
);
1129 if (currYear
+ 1500 < parsedYear
) { // most likely that the parsed string had a 2-digits year
1130 switch (ambigStrat
) {
1131 case kCFDateFormatterAmbiguousYearFailToParse
:
1134 case kCFDateFormatterAmbiguousYearAssumeToCurrent
:
1135 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, (currYear
/ 100 * 100) + parsedYear
% 100, false, status
);
1137 case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate
:
1138 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 100, 50, 49, false, status
);
1140 case kCFDateFormatterAmbiguousYearAssumeToFuture
:
1141 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 100, 0, 99, false, status
);
1143 case kCFDateFormatterAmbiguousYearAssumeToPast
:
1144 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 100, 99, 0, false, status
);
1146 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture
:
1147 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 100, 9, 90, false, status
);
1149 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast
:
1150 *at
= __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal
, *at
, 100, 90, 9, false, status
);
1152 case kCFDateFormatterAmbiguousYearAssumeToNone
:
1154 if (calendar_id
== kCFCalendarIdentifierGregorian
) { // historical default behavior of 1950 - 2049
1155 int32_t twoDigits
= parsedYear
% 100;
1156 *at
= __CFDateFormatterCorrectTimeWithTarget(cal
, *at
, ((twoDigits
< 50) ? 2000 : 1900) + twoDigits
, false, status
);
1158 break; // do nothing
1166 CFDateRef
CFDateFormatterCreateDateFromString(CFAllocatorRef allocator
, CFDateFormatterRef formatter
, CFStringRef string
, CFRange
*rangep
) {
1167 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
1168 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
1169 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1170 __CFGenericValidateType(string
, CFStringGetTypeID());
1172 if (CFDateFormatterGetAbsoluteTimeFromString(formatter
, string
, rangep
, &at
)) {
1173 return CFDateCreate(allocator
, at
);
1178 Boolean
CFDateFormatterGetAbsoluteTimeFromString(CFDateFormatterRef formatter
, CFStringRef string
, CFRange
*rangep
, CFAbsoluteTime
*atp
) {
1179 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1180 __CFGenericValidateType(string
, CFStringGetTypeID());
1181 CFRange range
= {0, 0};
1185 range
.length
= CFStringGetLength(string
);
1187 if (1024 < range
.length
) range
.length
= 1024;
1188 const UChar
*ustr
= (UChar
*)CFStringGetCharactersPtr(string
);
1189 STACK_BUFFER_DECL(UChar
, ubuffer
, (NULL
== ustr
) ? range
.length
: 1);
1191 CFStringGetCharacters(string
, range
, (UniChar
*)ubuffer
);
1194 ustr
+= range
.location
;
1198 UErrorCode status
= U_ZERO_ERROR
;
1199 UDateFormat
*df2
= udat_clone(formatter
->_df
, &status
);
1200 UCalendar
*cal2
= (UCalendar
*)udat_getCalendar(df2
);
1201 CFStringRef calendar_id
= (CFStringRef
) CFDateFormatterCopyProperty(formatter
, kCFDateFormatterCalendarIdentifierKey
);
1202 // we can do this direct comparison because locale in the formatter normalizes the identifier
1203 if (formatter
->_property
._TwoDigitStartDate
) {
1204 // if set, don't use hint, leave it all to ICU, as historically
1205 } else if (calendar_id
!= kCFCalendarIdentifierChinese
&& calendar_id
!= kCFCalendarIdentifierJapanese
) {
1206 ucal_setMillis(cal2
, ucal_getNow(), &status
);
1207 int32_t newYear
= ((ucal_get(cal2
, UCAL_YEAR
, &status
) / 100) + 16) * 100; // move ahead 1501-1600 years, to the beginning of a century
1208 ucal_set(cal2
, UCAL_YEAR
, newYear
);
1209 ucal_set(cal2
, UCAL_MONTH
, ucal_getLimit(cal2
, UCAL_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
1210 ucal_set(cal2
, UCAL_IS_LEAP_MONTH
, 0);
1211 ucal_set(cal2
, UCAL_DAY_OF_MONTH
, ucal_getLimit(cal2
, UCAL_DAY_OF_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
1212 ucal_set(cal2
, UCAL_HOUR_OF_DAY
, ucal_getLimit(cal2
, UCAL_HOUR_OF_DAY
, UCAL_ACTUAL_MINIMUM
, &status
));
1213 ucal_set(cal2
, UCAL_MINUTE
, ucal_getLimit(cal2
, UCAL_MINUTE
, UCAL_ACTUAL_MINIMUM
, &status
));
1214 ucal_set(cal2
, UCAL_SECOND
, ucal_getLimit(cal2
, UCAL_SECOND
, UCAL_ACTUAL_MINIMUM
, &status
));
1215 ucal_set(cal2
, UCAL_MILLISECOND
, 0);
1216 UDate future
= ucal_getMillis(cal2
, &status
);
1218 udat_set2DigitYearStart(df2
, future
, &status
);
1219 } else if (calendar_id
== kCFCalendarIdentifierChinese
) {
1221 ucal_set(cal2
, UCAL_ERA
, 1); // default to era 1 if no era info in the string for chinese
1222 } else if (calendar_id
== kCFCalendarIdentifierJapanese
) { // default to the current era
1223 ucal_setMillis(cal2
, ucal_getNow(), &status
);
1224 int32_t currEra
= ucal_get(cal2
, UCAL_ERA
, &status
);
1226 ucal_set(cal2
, UCAL_ERA
, currEra
);
1228 if (formatter
->_property
._DefaultDate
) {
1229 CFAbsoluteTime at
= CFDateGetAbsoluteTime(formatter
->_property
._DefaultDate
);
1230 udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
1231 ucal_setMillis(cal2
, udate
, &status
);
1233 udat_parseCalendar(df2
, cal2
, ustr
, range
.length
, &dpos
, &status
);
1234 udate
= ucal_getMillis(cal2
, &status
);
1235 if (rangep
) rangep
->length
= dpos
;
1236 Boolean success
= false;
1237 // first status check is for parsing and the second status check is for the work done inside __CFDateFormatterHandleAmbiguousYear()
1238 if (!U_FAILURE(status
) && (formatter
->_property
._TwoDigitStartDate
|| __CFDateFormatterHandleAmbiguousYear(formatter
, calendar_id
, df2
, cal2
, &udate
, ustr
, range
.length
, &status
)) && !U_FAILURE(status
)) {
1240 *atp
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
1244 CFRelease(calendar_id
);
1249 static void __CFDateFormatterSetSymbolsArray(UDateFormat
*icudf
, int32_t icucode
, int index_base
, CFTypeRef value
) {
1250 UErrorCode status
= U_ZERO_ERROR
;
1251 __CFGenericValidateType(value
, CFArrayGetTypeID());
1252 CFArrayRef array
= (CFArrayRef
)value
;
1253 CFIndex idx
, cnt
= CFArrayGetCount(array
);
1254 for (idx
= 0; idx
< cnt
; idx
++) {
1255 CFStringRef item
= (CFStringRef
)CFArrayGetValueAtIndex(array
, idx
);
1256 __CFGenericValidateType(item
, CFStringGetTypeID());
1257 CFIndex item_cnt
= CFStringGetLength(item
);
1258 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
1259 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr(item
);
1260 if (NULL
== item_ustr
) {
1261 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
1262 CFStringGetCharacters(item
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
1263 item_ustr
= item_buffer
;
1265 status
= U_ZERO_ERROR
;
1266 udat_setSymbols(icudf
, (UDateFormatSymbolType
)icucode
, idx
+ index_base
, item_ustr
, item_cnt
, &status
);
1270 static CFArrayRef
__CFDateFormatterGetSymbolsArray(UDateFormat
*icudf
, int32_t icucode
, int index_base
) {
1271 UErrorCode status
= U_ZERO_ERROR
;
1272 CFIndex idx
, cnt
= udat_countSymbols(icudf
, (UDateFormatSymbolType
)icucode
);
1273 if (cnt
<= index_base
) return CFArrayCreate(kCFAllocatorSystemDefault
, NULL
, 0, &kCFTypeArrayCallBacks
);
1274 cnt
= cnt
- index_base
;
1275 STACK_BUFFER_DECL(CFStringRef
, strings
, cnt
);
1276 for (idx
= 0; idx
< cnt
; idx
++) {
1277 UChar ubuffer
[BUFFER_SIZE
];
1278 CFStringRef str
= NULL
;
1279 status
= U_ZERO_ERROR
;
1280 CFIndex ucnt
= udat_getSymbols(icudf
, (UDateFormatSymbolType
)icucode
, idx
+ index_base
, ubuffer
, BUFFER_SIZE
, &status
);
1281 if (U_SUCCESS(status
) && cnt
<= BUFFER_SIZE
) {
1282 str
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)ubuffer
, ucnt
);
1284 strings
[idx
] = !str
? (CFStringRef
)CFRetain(CFSTR("<error>")) : str
;
1286 CFArrayRef array
= CFArrayCreate(kCFAllocatorSystemDefault
, (const void **)strings
, cnt
, &kCFTypeArrayCallBacks
);
1288 CFRelease(strings
[cnt
]);
1293 #define SET_SYMBOLS_ARRAY(A, B, C) \
1294 if (!directToICU) { \
1295 oldProperty = formatter->_property. C; \
1296 formatter->_property. C = NULL; \
1298 __CFDateFormatterSetSymbolsArray(formatter->_df, A, B, value); \
1299 if (!directToICU) { \
1300 formatter->_property. C = __CFDateFormatterGetSymbolsArray(formatter->_df, A, B); \
1303 static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter
, CFStringRef key
, CFTypeRef value
, Boolean directToICU
) {
1304 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1305 __CFGenericValidateType(key
, CFStringGetTypeID());
1306 CFTypeRef oldProperty
= NULL
;
1307 UErrorCode status
= U_ZERO_ERROR
;
1308 UChar ubuffer
[BUFFER_SIZE
];
1310 if (kCFDateFormatterIsLenientKey
== key
) {
1312 oldProperty
= formatter
->_property
. _IsLenient
;
1313 formatter
->_property
. _IsLenient
= NULL
;
1315 __CFGenericValidateType(value
, CFBooleanGetTypeID());
1316 udat_setLenient(formatter
->_df
, (kCFBooleanTrue
== value
));
1317 UCalendar
*cal
= (UCalendar
*)udat_getCalendar(formatter
->_df
);
1318 if (cal
) ucal_setAttribute(cal
, UCAL_LENIENT
, (kCFBooleanTrue
== value
));
1320 formatter
->_property
. _IsLenient
= (CFBooleanRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterIsLenientKey
);
1322 } else if (kCFDateFormatterDoesRelativeDateFormattingKey
== key
) {
1324 oldProperty
= formatter
->_property
. _DoesRelativeDateFormatting
;
1325 formatter
->_property
. _DoesRelativeDateFormatting
= NULL
;
1327 __CFGenericValidateType(value
, CFBooleanGetTypeID());
1329 if (kCFBooleanTrue
!= value
) value
= kCFBooleanFalse
;
1330 formatter
->_property
. _DoesRelativeDateFormatting
= value
? (CFBooleanRef
)CFRetain(value
) : NULL
;
1331 __ResetUDateFormat(formatter
, false);
1333 } else if (kCFDateFormatterCalendarKey
== key
) {
1335 oldProperty
= formatter
->_property
. _Calendar
;
1336 formatter
->_property
. _Calendar
= NULL
;
1338 __CFGenericValidateType(value
, CFCalendarGetTypeID());
1339 CFStringRef localeName
= CFLocaleGetIdentifier(formatter
->_locale
);
1340 CFDictionaryRef components
= CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault
, localeName
);
1341 CFMutableDictionaryRef mcomponents
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, 0, components
);
1342 CFDictionarySetValue(mcomponents
, kCFLocaleCalendarIdentifierKey
, CFCalendarGetIdentifier((CFCalendarRef
)value
));
1343 localeName
= CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault
, mcomponents
);
1344 CFRelease(mcomponents
);
1345 CFRelease(components
);
1346 CFLocaleRef newLocale
= CFLocaleCreate(CFGetAllocator(formatter
->_locale
), localeName
);
1347 CFRelease(localeName
);
1348 CFRelease(formatter
->_locale
);
1349 formatter
->_locale
= newLocale
;
1350 UCalendar
*cal
= __CFCalendarCreateUCalendar(NULL
, CFLocaleGetIdentifier(formatter
->_locale
), formatter
->_property
._TimeZone
);
1351 if (cal
) ucal_setAttribute(cal
, UCAL_FIRST_DAY_OF_WEEK
, CFCalendarGetFirstWeekday((CFCalendarRef
)value
));
1352 if (cal
) ucal_setAttribute(cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
, CFCalendarGetMinimumDaysInFirstWeek((CFCalendarRef
)value
));
1353 if (cal
) udat_setCalendar(formatter
->_df
, cal
);
1354 if (cal
) ucal_close(cal
);
1356 formatter
->_property
. _Calendar
= (CFCalendarRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterCalendarKey
);
1358 } else if (kCFDateFormatterCalendarIdentifierKey
== key
) {
1360 oldProperty
= formatter
->_property
. _CalendarName
;
1361 formatter
->_property
. _CalendarName
= NULL
;
1363 __CFGenericValidateType(value
, CFStringGetTypeID());
1364 CFStringRef localeName
= CFLocaleGetIdentifier(formatter
->_locale
);
1365 CFDictionaryRef components
= CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault
, localeName
);
1366 CFMutableDictionaryRef mcomponents
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, 0, components
);
1367 CFDictionarySetValue(mcomponents
, kCFLocaleCalendarIdentifierKey
, value
);
1368 localeName
= CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault
, mcomponents
);
1369 CFRelease(mcomponents
);
1370 CFRelease(components
);
1371 CFLocaleRef newLocale
= CFLocaleCreate(CFGetAllocator(formatter
->_locale
), localeName
);
1372 CFRelease(localeName
);
1373 CFRelease(formatter
->_locale
);
1374 formatter
->_locale
= newLocale
;
1375 UCalendar
*cal
= __CFCalendarCreateUCalendar(NULL
, CFLocaleGetIdentifier(formatter
->_locale
), formatter
->_property
._TimeZone
);
1376 if (cal
) udat_setCalendar(formatter
->_df
, cal
);
1377 if (cal
) ucal_close(cal
);
1379 formatter
->_property
. _CalendarName
= (CFStringRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterCalendarIdentifierKey
);
1381 } else if (kCFDateFormatterTimeZoneKey
== key
) {
1382 if (formatter
->_property
. _TimeZone
!= value
) {
1384 oldProperty
= formatter
->_property
. _TimeZone
;
1385 formatter
->_property
. _TimeZone
= NULL
;
1387 __CFGenericValidateType(value
, CFTimeZoneGetTypeID());
1388 CFTimeZoneRef old
= formatter
->_property
._TimeZone
;
1389 formatter
->_property
._TimeZone
= value
? (CFTimeZoneRef
)CFRetain(value
) : CFTimeZoneCopyDefault();
1390 if (old
) CFRelease(old
);
1391 CFStringRef tznam
= CFTimeZoneGetName(formatter
->_property
._TimeZone
);
1392 UCalendar
*cal
= (UCalendar
*)udat_getCalendar(formatter
->_df
);
1393 CFIndex ucnt
= CFStringGetLength(tznam
);
1394 if (BUFFER_SIZE
< ucnt
) ucnt
= BUFFER_SIZE
;
1395 CFStringGetCharacters(tznam
, CFRangeMake(0, ucnt
), (UniChar
*)ubuffer
);
1396 ucal_setTimeZone(cal
, ubuffer
, ucnt
, &status
);
1398 old
= formatter
->_property
._TimeZone
;
1399 formatter
->_property
. _TimeZone
= (CFTimeZoneRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterTimeZoneKey
);
1400 if (old
) CFRelease(old
);
1403 } else if (kCFDateFormatterDefaultFormatKey
== key
) {
1404 // read-only attribute
1405 } else if (kCFDateFormatterTwoDigitStartDateKey
== key
) {
1407 oldProperty
= formatter
->_property
. _TwoDigitStartDate
;
1408 formatter
->_property
. _TwoDigitStartDate
= NULL
;
1410 __CFGenericValidateType(value
, CFDateGetTypeID());
1411 CFAbsoluteTime at
= CFDateGetAbsoluteTime((CFDateRef
)value
);
1412 UDate udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
1413 udat_set2DigitYearStart(formatter
->_df
, udate
, &status
);
1415 formatter
->_property
. _TwoDigitStartDate
= (CFDateRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterTwoDigitStartDateKey
);
1417 } else if (kCFDateFormatterDefaultDateKey
== key
) {
1419 oldProperty
= formatter
->_property
. _DefaultDate
;
1420 formatter
->_property
. _DefaultDate
= NULL
;
1422 __CFGenericValidateType(value
, CFDateGetTypeID());
1424 formatter
->_property
._DefaultDate
= value
? (CFDateRef
)CFRetain(value
) : NULL
;
1426 } else if (kCFDateFormatterGregorianStartDateKey
== key
) {
1428 oldProperty
= formatter
->_property
. _GregorianStartDate
;
1429 formatter
->_property
. _GregorianStartDate
= NULL
;
1431 __CFGenericValidateType(value
, CFDateGetTypeID());
1432 CFAbsoluteTime at
= CFDateGetAbsoluteTime((CFDateRef
)value
);
1433 UDate udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
1434 UCalendar
*cal
= (UCalendar
*)udat_getCalendar(formatter
->_df
);
1435 ucal_setGregorianChange(cal
, udate
, &status
);
1437 formatter
->_property
. _GregorianStartDate
= (CFDateRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterGregorianStartDateKey
);
1439 } else if (kCFDateFormatterEraSymbolsKey
== key
) {
1440 SET_SYMBOLS_ARRAY(UDAT_ERAS
, 0, _EraSymbols
)
1441 } else if (kCFDateFormatterLongEraSymbolsKey
== key
) {
1442 SET_SYMBOLS_ARRAY(UDAT_ERA_NAMES
, 0, _LongEraSymbols
)
1443 } else if (kCFDateFormatterMonthSymbolsKey
== key
) {
1444 SET_SYMBOLS_ARRAY(UDAT_MONTHS
, 0, _MonthSymbols
)
1445 } else if (kCFDateFormatterShortMonthSymbolsKey
== key
) {
1446 SET_SYMBOLS_ARRAY(UDAT_SHORT_MONTHS
, 0, _ShortMonthSymbols
)
1447 } else if (kCFDateFormatterVeryShortMonthSymbolsKey
== key
) {
1448 SET_SYMBOLS_ARRAY(UDAT_NARROW_MONTHS
, 0, _VeryShortMonthSymbols
)
1449 } else if (kCFDateFormatterStandaloneMonthSymbolsKey
== key
) {
1450 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_MONTHS
, 0, _StandaloneMonthSymbols
)
1451 } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey
== key
) {
1452 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_MONTHS
, 0, _ShortStandaloneMonthSymbols
)
1453 } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey
== key
) {
1454 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_MONTHS
, 0, _VeryShortStandaloneMonthSymbols
)
1455 } else if (kCFDateFormatterWeekdaySymbolsKey
== key
) {
1456 SET_SYMBOLS_ARRAY(UDAT_WEEKDAYS
, 1, _WeekdaySymbols
)
1457 } else if (kCFDateFormatterShortWeekdaySymbolsKey
== key
) {
1458 SET_SYMBOLS_ARRAY(UDAT_SHORT_WEEKDAYS
, 1, _ShortWeekdaySymbols
)
1459 } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey
== key
) {
1460 SET_SYMBOLS_ARRAY(UDAT_NARROW_WEEKDAYS
, 1, _VeryShortWeekdaySymbols
)
1461 } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey
== key
) {
1462 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_WEEKDAYS
, 1, _StandaloneWeekdaySymbols
)
1463 } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey
== key
) {
1464 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_WEEKDAYS
, 1, _ShortStandaloneWeekdaySymbols
)
1465 } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey
== key
) {
1466 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_WEEKDAYS
, 1, _VeryShortStandaloneWeekdaySymbols
)
1467 } else if (kCFDateFormatterQuarterSymbolsKey
== key
) {
1468 SET_SYMBOLS_ARRAY(UDAT_QUARTERS
, 0, _QuarterSymbols
)
1469 } else if (kCFDateFormatterShortQuarterSymbolsKey
== key
) {
1470 SET_SYMBOLS_ARRAY(UDAT_SHORT_QUARTERS
, 0, _ShortQuarterSymbols
)
1471 } else if (kCFDateFormatterStandaloneQuarterSymbolsKey
== key
) {
1472 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_QUARTERS
, 0, _StandaloneQuarterSymbols
)
1473 } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey
== key
) {
1474 SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_QUARTERS
, 0, _ShortStandaloneQuarterSymbols
)
1475 } else if (kCFDateFormatterAMSymbolKey
== key
) {
1477 oldProperty
= formatter
->_property
. _AMSymbol
;
1478 formatter
->_property
. _AMSymbol
= NULL
;
1480 __CFGenericValidateType(value
, CFStringGetTypeID());
1481 CFIndex item_cnt
= CFStringGetLength((CFStringRef
)value
);
1482 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
1483 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)value
);
1484 if (NULL
== item_ustr
) {
1485 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
1486 CFStringGetCharacters((CFStringRef
)value
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
1487 item_ustr
= item_buffer
;
1489 udat_setSymbols(formatter
->_df
, UDAT_AM_PMS
, 0, item_ustr
, item_cnt
, &status
);
1491 formatter
->_property
. _AMSymbol
= (CFStringRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterAMSymbolKey
);
1493 } else if (kCFDateFormatterPMSymbolKey
== key
) {
1495 oldProperty
= formatter
->_property
. _PMSymbol
;
1496 formatter
->_property
. _PMSymbol
= NULL
;
1498 __CFGenericValidateType(value
, CFStringGetTypeID());
1499 CFIndex item_cnt
= CFStringGetLength((CFStringRef
)value
);
1500 STACK_BUFFER_DECL(UChar
, item_buffer
, __CFMin(BUFFER_SIZE
, item_cnt
));
1501 UChar
*item_ustr
= (UChar
*)CFStringGetCharactersPtr((CFStringRef
)value
);
1502 if (NULL
== item_ustr
) {
1503 item_cnt
= __CFMin(BUFFER_SIZE
, item_cnt
);
1504 CFStringGetCharacters((CFStringRef
)value
, CFRangeMake(0, item_cnt
), (UniChar
*)item_buffer
);
1505 item_ustr
= item_buffer
;
1507 udat_setSymbols(formatter
->_df
, UDAT_AM_PMS
, 1, item_ustr
, item_cnt
, &status
);
1509 formatter
->_property
. _PMSymbol
= (CFStringRef
)CFDateFormatterCopyProperty(formatter
, kCFDateFormatterPMSymbolKey
);
1511 } else if (kCFDateFormatterAmbiguousYearStrategyKey
== key
) {
1512 oldProperty
= formatter
->_property
._AmbiguousYearStrategy
;
1513 formatter
->_property
._AmbiguousYearStrategy
= NULL
;
1514 __CFGenericValidateType(value
, CFNumberGetTypeID());
1515 formatter
->_property
._AmbiguousYearStrategy
= (CFNumberRef
)CFRetain(value
);
1517 CFAssert3(0, __kCFLogAssertion
, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__
, key
, key
);
1519 if (oldProperty
) CFRelease(oldProperty
);
1522 void CFDateFormatterSetProperty(CFDateFormatterRef formatter
, CFStringRef key
, CFTypeRef value
) {
1523 __CFDateFormatterSetProperty(formatter
, key
, value
, false);
1526 CFTypeRef
CFDateFormatterCopyProperty(CFDateFormatterRef formatter
, CFStringRef key
) {
1527 __CFGenericValidateType(formatter
, CFDateFormatterGetTypeID());
1528 __CFGenericValidateType(key
, CFStringGetTypeID());
1529 UErrorCode status
= U_ZERO_ERROR
;
1530 UChar ubuffer
[BUFFER_SIZE
];
1532 if (kCFDateFormatterIsLenientKey
== key
) {
1533 if (formatter
->_property
._IsLenient
) return CFRetain(formatter
->_property
._IsLenient
);
1534 return CFRetain(udat_isLenient(formatter
->_df
) ? kCFBooleanTrue
: kCFBooleanFalse
);
1535 } else if (kCFDateFormatterDoesRelativeDateFormattingKey
== key
) {
1536 if (formatter
->_property
._DoesRelativeDateFormatting
) return CFRetain(formatter
->_property
._DoesRelativeDateFormatting
);
1537 return CFRetain(kCFBooleanFalse
);
1538 } else if (kCFDateFormatterCalendarKey
== key
) {
1539 if (formatter
->_property
._Calendar
) return CFRetain(formatter
->_property
._Calendar
);
1540 CFCalendarRef calendar
= (CFCalendarRef
)CFLocaleGetValue(formatter
->_locale
, kCFLocaleCalendarKey
);
1541 return calendar
? CFRetain(calendar
) : NULL
;
1542 } else if (kCFDateFormatterCalendarIdentifierKey
== key
) {
1543 if (formatter
->_property
._CalendarName
) return CFRetain(formatter
->_property
._CalendarName
);
1544 CFStringRef ident
= (CFStringRef
)CFLocaleGetValue(formatter
->_locale
, kCFLocaleCalendarIdentifierKey
);
1545 return ident
? CFRetain(ident
) : NULL
;
1546 } else if (kCFDateFormatterTimeZoneKey
== key
) {
1547 if (formatter
->_property
._TwoDigitStartDate
) return CFRetain(formatter
->_property
._TwoDigitStartDate
);
1548 return CFRetain(formatter
->_property
._TimeZone
);
1549 } else if (kCFDateFormatterDefaultFormatKey
== key
) {
1550 return formatter
->_defformat
? CFRetain(formatter
->_defformat
) : NULL
;
1551 } else if (kCFDateFormatterTwoDigitStartDateKey
== key
) {
1552 if (formatter
->_property
._TwoDigitStartDate
) return CFRetain(formatter
->_property
._TwoDigitStartDate
);
1553 UDate udate
= udat_get2DigitYearStart(formatter
->_df
, &status
);
1554 if (U_SUCCESS(status
)) {
1555 CFAbsoluteTime at
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
1556 return CFDateCreate(CFGetAllocator(formatter
), at
);
1558 } else if (kCFDateFormatterDefaultDateKey
== key
) {
1559 return formatter
->_property
._DefaultDate
? CFRetain(formatter
->_property
._DefaultDate
) : NULL
;
1560 } else if (kCFDateFormatterGregorianStartDateKey
== key
) {
1561 if (formatter
->_property
._GregorianStartDate
) return CFRetain(formatter
->_property
._GregorianStartDate
);
1562 UCalendar
*cal
= (UCalendar
*)udat_getCalendar(formatter
->_df
);
1563 UDate udate
= ucal_getGregorianChange(cal
, &status
);
1564 if (U_SUCCESS(status
)) {
1565 CFAbsoluteTime at
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
1566 return CFDateCreate(CFGetAllocator(formatter
), at
);
1568 } else if (kCFDateFormatterEraSymbolsKey
== key
) {
1569 if (formatter
->_property
._EraSymbols
) return CFRetain(formatter
->_property
._EraSymbols
);
1570 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_ERAS
, 0);
1571 } else if (kCFDateFormatterLongEraSymbolsKey
== key
) {
1572 if (formatter
->_property
._LongEraSymbols
) return CFRetain(formatter
->_property
._LongEraSymbols
);
1573 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_ERA_NAMES
, 0);
1574 } else if (kCFDateFormatterMonthSymbolsKey
== key
) {
1575 if (formatter
->_property
._MonthSymbols
) return CFRetain(formatter
->_property
._MonthSymbols
);
1576 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_MONTHS
, 0);
1577 } else if (kCFDateFormatterShortMonthSymbolsKey
== key
) {
1578 if (formatter
->_property
._ShortMonthSymbols
) return CFRetain(formatter
->_property
._ShortMonthSymbols
);
1579 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_SHORT_MONTHS
, 0);
1580 } else if (kCFDateFormatterVeryShortMonthSymbolsKey
== key
) {
1581 if (formatter
->_property
._VeryShortMonthSymbols
) return CFRetain(formatter
->_property
._VeryShortMonthSymbols
);
1582 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_NARROW_MONTHS
, 0);
1583 } else if (kCFDateFormatterStandaloneMonthSymbolsKey
== key
) {
1584 if (formatter
->_property
._StandaloneMonthSymbols
) return CFRetain(formatter
->_property
._StandaloneMonthSymbols
);
1585 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_MONTHS
, 0);
1586 } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey
== key
) {
1587 if (formatter
->_property
._ShortStandaloneMonthSymbols
) return CFRetain(formatter
->_property
._ShortStandaloneMonthSymbols
);
1588 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_SHORT_MONTHS
, 0);
1589 } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey
== key
) {
1590 if (formatter
->_property
._VeryShortStandaloneMonthSymbols
) return CFRetain(formatter
->_property
._VeryShortStandaloneMonthSymbols
);
1591 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_NARROW_MONTHS
, 0);
1592 } else if (kCFDateFormatterWeekdaySymbolsKey
== key
) {
1593 if (formatter
->_property
._WeekdaySymbols
) return CFRetain(formatter
->_property
._WeekdaySymbols
);
1594 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_WEEKDAYS
, 1);
1595 } else if (kCFDateFormatterShortWeekdaySymbolsKey
== key
) {
1596 if (formatter
->_property
._ShortWeekdaySymbols
) return CFRetain(formatter
->_property
._ShortWeekdaySymbols
);
1597 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_SHORT_WEEKDAYS
, 1);
1598 } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey
== key
) {
1599 if (formatter
->_property
._VeryShortWeekdaySymbols
) return CFRetain(formatter
->_property
._VeryShortWeekdaySymbols
);
1600 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_NARROW_WEEKDAYS
, 1);
1601 } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey
== key
) {
1602 if (formatter
->_property
._StandaloneWeekdaySymbols
) return CFRetain(formatter
->_property
._StandaloneWeekdaySymbols
);
1603 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_WEEKDAYS
, 1);
1604 } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey
== key
) {
1605 if (formatter
->_property
._ShortStandaloneWeekdaySymbols
) return CFRetain(formatter
->_property
._ShortStandaloneWeekdaySymbols
);
1606 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_SHORT_WEEKDAYS
, 1);
1607 } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey
== key
) {
1608 if (formatter
->_property
._VeryShortStandaloneWeekdaySymbols
) return CFRetain(formatter
->_property
._VeryShortStandaloneWeekdaySymbols
);
1609 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_NARROW_WEEKDAYS
, 1);
1610 } else if (kCFDateFormatterQuarterSymbolsKey
== key
) {
1611 if (formatter
->_property
._QuarterSymbols
) return CFRetain(formatter
->_property
._QuarterSymbols
);
1612 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_QUARTERS
, 0);
1613 } else if (kCFDateFormatterShortQuarterSymbolsKey
== key
) {
1614 if (formatter
->_property
._ShortQuarterSymbols
) return CFRetain(formatter
->_property
._ShortQuarterSymbols
);
1615 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_SHORT_QUARTERS
, 0);
1616 } else if (kCFDateFormatterStandaloneQuarterSymbolsKey
== key
) {
1617 if (formatter
->_property
._StandaloneQuarterSymbols
) return CFRetain(formatter
->_property
._StandaloneQuarterSymbols
);
1618 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_QUARTERS
, 0);
1619 } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey
== key
) {
1620 if (formatter
->_property
._ShortStandaloneQuarterSymbols
) return CFRetain(formatter
->_property
._ShortStandaloneQuarterSymbols
);
1621 return __CFDateFormatterGetSymbolsArray(formatter
->_df
, UDAT_STANDALONE_SHORT_QUARTERS
, 0);
1622 } else if (kCFDateFormatterAMSymbolKey
== key
) {
1623 if (formatter
->_property
._AMSymbol
) return CFRetain(formatter
->_property
._AMSymbol
);
1624 CFIndex cnt
= udat_countSymbols(formatter
->_df
, UDAT_AM_PMS
);
1626 CFIndex ucnt
= udat_getSymbols(formatter
->_df
, UDAT_AM_PMS
, 0, ubuffer
, BUFFER_SIZE
, &status
);
1627 if (U_SUCCESS(status
) && cnt
<= BUFFER_SIZE
) {
1628 return CFStringCreateWithCharacters(CFGetAllocator(formatter
), (UniChar
*)ubuffer
, ucnt
);
1631 } else if (kCFDateFormatterPMSymbolKey
== key
) {
1632 if (formatter
->_property
._PMSymbol
) return CFRetain(formatter
->_property
._PMSymbol
);
1633 CFIndex cnt
= udat_countSymbols(formatter
->_df
, UDAT_AM_PMS
);
1635 CFIndex ucnt
= udat_getSymbols(formatter
->_df
, UDAT_AM_PMS
, 1, ubuffer
, BUFFER_SIZE
, &status
);
1636 if (U_SUCCESS(status
) && cnt
<= BUFFER_SIZE
) {
1637 return CFStringCreateWithCharacters(CFGetAllocator(formatter
), (UniChar
*)ubuffer
, ucnt
);
1640 } else if (kCFDateFormatterAmbiguousYearStrategyKey
== key
) {
1641 if (formatter
->_property
._AmbiguousYearStrategy
) return CFRetain(formatter
->_property
._AmbiguousYearStrategy
);
1643 CFAssert3(0, __kCFLogAssertion
, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__
, key
, key
);