]> git.saurik.com Git - apple/cf.git/blob - CFDateFormatter.c
CF-744.12.tar.gz
[apple/cf.git] / CFDateFormatter.c
1 /*
2 * Copyright (c) 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFDateFormatter.c
25 Copyright (c) 2002-2012, Apple Inc. All rights reserved.
26 Responsibility: David Smith
27 */
28
29 #define U_SHOW_INTERNAL_API 1
30
31 #include <CoreFoundation/CFDateFormatter.h>
32 #include <CoreFoundation/CFDate.h>
33 #include <CoreFoundation/CFTimeZone.h>
34 #include <CoreFoundation/CFCalendar.h>
35 #include <CoreFoundation/CFNumber.h>
36 #include "CFPriv.h"
37 #include "CFInternal.h"
38 #include "CFLocaleInternal.h"
39 #include <unicode/udat.h>
40 #include <unicode/udatpg.h>
41 #include <math.h>
42 #include <float.h>
43
44 typedef CF_ENUM(CFIndex, CFDateFormatterAmbiguousYearHandling) {
45 kCFDateFormatterAmbiguousYearFailToParse = 0, // fail the parse; the default formatter behavior
46 kCFDateFormatterAmbiguousYearAssumeToNone = 1, // default to assuming era 1, or the year 0-99
47 kCFDateFormatterAmbiguousYearAssumeToCurrent = 2, // default to assuming the current century or era
48 kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate = 3,
49 kCFDateFormatterAmbiguousYearAssumeToFuture = 4,
50 kCFDateFormatterAmbiguousYearAssumeToPast = 5,
51 kCFDateFormatterAmbiguousYearAssumeToLikelyFuture = 6,
52 kCFDateFormatterAmbiguousYearAssumeToLikelyPast = 7
53 };
54
55 extern UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz);
56 static void __CFDateFormatterCustomize(CFDateFormatterRef formatter);
57
58 CF_EXPORT const CFStringRef kCFDateFormatterCalendarIdentifierKey;
59
60 #undef CFReleaseIfNotNull
61 #define CFReleaseIfNotNull(X) if (X) CFRelease(X)
62
63 #define BUFFER_SIZE 768
64
65 static CFStringRef __CFDateFormatterCreateForcedTemplate(CFLocaleRef locale, CFStringRef inString);
66
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.
69
70 CFArrayRef CFDateFormatterCreateDateFormatsFromTemplates(CFAllocatorRef allocator, CFArrayRef tmplates, CFOptionFlags options, CFLocaleRef locale) {
71 return (CFArrayRef)CFDateFormatterCreateDateFormatFromTemplate(allocator, (CFStringRef)tmplates, options, locale);
72 }
73
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());
80 }
81
82 CFStringRef localeName = locale ? CFLocaleGetIdentifier(locale) : CFSTR("");
83 char buffer[BUFFER_SIZE];
84 const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII);
85 if (NULL == cstr) {
86 if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
87 }
88 if (NULL == cstr) {
89 return NULL;
90 }
91
92 UErrorCode status = U_ZERO_ERROR;
93 UDateTimePatternGenerator *ptg = udatpg_open(cstr, &status);
94 if (NULL == ptg || U_FAILURE(status)) {
95 return NULL;
96 }
97
98 CFTypeRef result = tmplateIsString ? NULL : (CFTypeRef)CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
99
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;
103
104 tmplateString = __CFDateFormatterCreateForcedTemplate(locale ? locale : CFLocaleGetSystem(), tmplateString);
105
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) {
109 jCount++;
110 if ((r.location + 1 < CFStringGetLength(tmplateString)) && ('j' == CFStringGetCharacterAtIndex(tmplateString, r.location + 1))) {
111 jCount++;
112 }
113 }
114
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);
120
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';
128 }
129
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);
134 }
135 }
136
137 if (tmplateIsString) {
138 result = (CFTypeRef)resultString;
139 } else {
140 CFArrayAppendValue((CFMutableArrayRef)result, resultString ? (CFTypeRef)resultString : (CFTypeRef)kCFNull);
141 if (resultString) CFRelease(resultString);
142 }
143 }
144
145 udatpg_close(ptg);
146
147 return (CFStringRef)result;
148 }
149
150 struct __CFDateFormatter {
151 CFRuntimeBase _base;
152 UDateFormat *_df;
153 CFLocaleRef _locale;
154 CFDateFormatterStyle _timeStyle;
155 CFDateFormatterStyle _dateStyle;
156 CFStringRef _format;
157 CFStringRef _defformat;
158 struct {
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;
189 } _property;
190 };
191
192 static CFStringRef __CFDateFormatterCopyDescription(CFTypeRef cf) {
193 CFDateFormatterRef formatter = (CFDateFormatterRef)cf;
194 return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("<CFDateFormatter %p [%p]>"), cf, CFGetAllocator(formatter));
195 }
196
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);
232 }
233
234 static CFStringRef __CFDateFormatterCreateForcedString(CFDateFormatterRef formatter, CFStringRef inString);
235
236 static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value, Boolean directToICU);
237
238 #define RESET_PROPERTY(C, K) \
239 if (df->_property. C) __CFDateFormatterSetProperty(df, K, df->_property. C, true);
240
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);
245 df->_df = NULL;
246
247 // uses _timeStyle, _dateStyle, _locale, _property._TimeZone; sets _df, _format, _defformat
248 char loc_buffer[BUFFER_SIZE];
249 loc_buffer[0] = 0;
250 CFStringRef tmpLocName = df->_locale ? CFLocaleGetIdentifier(df->_locale) : CFSTR("");
251 CFStringGetCString(tmpLocName, loc_buffer, BUFFER_SIZE, kCFStringEncodingASCII);
252
253 UChar tz_buffer[BUFFER_SIZE];
254 tz_buffer[0] = 0;
255 CFStringRef tmpTZName = df->_property._TimeZone ? CFTimeZoneGetName(df->_property._TimeZone) : CFSTR("GMT");
256 CFStringGetCharacters(tmpTZName, CFRangeMake(0, CFStringGetLength(tmpTZName)), (UniChar *)tz_buffer);
257
258 df->_property._HasCustomFormat = NULL;
259
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;
267 }
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;
274 }
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;
279 }
280
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)) {
284 return;
285 }
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);
291 } else {
292 udat_applyPattern(icudf, false, NULL, 0);
293 }
294 }
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
299 }
300 df->_df = icudf;
301
302 __CFDateFormatterCustomize(df);
303
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);
318 timeLen = cnt;
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?
322
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);
329 }
330 }
331 CFRelease(formatString);
332 CFRelease(newFormat);
333 }
334 } else {
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);
346 if (NULL == ustr) {
347 CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer);
348 ustr = ubuffer;
349 }
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);
356 }
357 }
358 CFRelease(formatString);
359 CFRelease(newFormat);
360 }
361 }
362 if (df->_defformat) CFRelease(df->_defformat);
363 df->_defformat = df->_format ? (CFStringRef)CFRetain(df->_format) : NULL;
364
365 CFStringRef calName = df->_property._CalendarName ? (df->_property._CalendarName) : NULL;
366 if (!calName) {
367 calName = (CFStringRef)CFLocaleGetValue(df->_locale, kCFLocaleCalendarIdentifierKey);
368 }
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);
377 }
378
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);
408 }
409
410 static CFTypeID __kCFDateFormatterTypeID = _kCFRuntimeNotATypeID;
411
412 static const CFRuntimeClass __CFDateFormatterClass = {
413 0,
414 "CFDateFormatter",
415 NULL, // init
416 NULL, // copy
417 __CFDateFormatterDeallocate,
418 NULL,
419 NULL,
420 NULL, //
421 __CFDateFormatterCopyDescription
422 };
423
424 static void __CFDateFormatterInitialize(void) {
425 __kCFDateFormatterTypeID = _CFRuntimeRegisterClass(&__CFDateFormatterClass);
426 }
427
428 CFTypeID CFDateFormatterGetTypeID(void) {
429 if (_kCFRuntimeNotATypeID == __kCFDateFormatterTypeID) __CFDateFormatterInitialize();
430 return __kCFDateFormatterTypeID;
431 }
432
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) {
441 return NULL;
442 }
443 memory->_df = NULL;
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;
479
480 switch (dateStyle) {
481 case kCFDateFormatterNoStyle:
482 case kCFDateFormatterShortStyle:
483 case kCFDateFormatterMediumStyle:
484 case kCFDateFormatterLongStyle:
485 case kCFDateFormatterFullStyle: break;
486 default:
487 CFAssert2(0, __kCFLogAssertion, "%s(): unknown date style %d", __PRETTY_FUNCTION__, dateStyle);
488 memory->_dateStyle = kCFDateFormatterMediumStyle;
489 break;
490 }
491 switch (timeStyle) {
492 case kCFDateFormatterNoStyle:
493 case kCFDateFormatterShortStyle:
494 case kCFDateFormatterMediumStyle:
495 case kCFDateFormatterLongStyle:
496 case kCFDateFormatterFullStyle: break;
497 default:
498 CFAssert2(0, __kCFLogAssertion, "%s(): unknown time style %d", __PRETTY_FUNCTION__, timeStyle);
499 memory->_timeStyle = kCFDateFormatterMediumStyle;
500 break;
501 }
502
503 memory->_locale = locale ? CFLocaleCreateCopy(allocator, locale) : (CFLocaleRef)CFRetain(CFLocaleGetSystem());
504 memory->_property._TimeZone = CFTimeZoneCopyDefault();
505 __ResetUDateFormat(memory, false);
506 if (!memory->_df) {
507 CFRelease(memory);
508 return NULL;
509 }
510 return (CFDateFormatterRef)memory;
511 }
512
513 extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale);
514
515 static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter) {
516 CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale);
517
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()) {
523 CFStringRef key;
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;
530 }
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);
535 }
536 }
537 }
538 if (-1 == dateLen) {
539 UErrorCode status = U_ZERO_ERROR;
540 int32_t ret = udat_toPatternRelativeDate(formatter->_df, dateBuffer, BUFFER_SIZE, &status);
541 if (!U_FAILURE(status)) {
542 dateLen = ret;
543 }
544 }
545
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()) {
551 CFStringRef key;
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;
558 }
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);
563 }
564 }
565 }
566 if (-1 == timeLen) {
567 UErrorCode status = U_ZERO_ERROR;
568 int32_t ret = udat_toPatternRelativeTime(formatter->_df, timeBuffer, BUFFER_SIZE, &status);
569 if (!U_FAILURE(status)) {
570 timeLen = ret;
571 }
572 }
573
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);
576 }
577
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()) {
586 CFStringRef key;
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;
593 }
594 pref = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key);
595 }
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;
603 }
604 CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale);
605 char buffer[BUFFER_SIZE];
606 const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII);
607 if (NULL == cstr) {
608 if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
609 }
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);
612 if (NULL != df) {
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
624 CFRange result;
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;
633 }
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);
637 }
638 CFRelease(formatString);
639 }
640 CFRelease(dateString);
641 }
642 udat_close(df);
643 }
644 }
645 }
646 }
647
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;
664 }
665 UErrorCode status = U_ZERO_ERROR;
666 udat_setSymbols(formatter->_df, sym, idx, item_ustr, item_cnt, &status);
667 }
668 }
669 }
670
671 static CFStringRef __CFDateFormatterCreateForcedTemplate(CFLocaleRef locale, CFStringRef inString) {
672 if (!inString) return NULL;
673
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);
679 }
680 pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce12HourTime")) : NULL;
681 if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) {
682 doForce12 = CFBooleanGetValue((CFBooleanRef)pref);
683 }
684 if (doForce24) doForce12 = false; // if both are set, Force24 wins, period
685 if (!doForce24 && !doForce12) return (CFStringRef)CFRetain(inString);
686
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++) {
692 Boolean emit = true;
693 UniChar ch = CFStringGetCharacterAtIndex(inString, idx);
694 switch (ch) {
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;
704 break;
705 }
706 if (emit) CFStringAppendCharacters(outString, &ch, 1);
707 }
708
709 return outString;
710 }
711
712 static CFStringRef __CFDateFormatterCreateForcedString(CFDateFormatterRef formatter, CFStringRef inString) {
713 if (!inString) return NULL;
714
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);
720 }
721 pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce12HourTime")) : NULL;
722 if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) {
723 doForce12 = CFBooleanGetValue((CFBooleanRef)pref);
724 }
725 if (doForce24) doForce12 = false; // if both are set, Force24 wins, period
726 if (!doForce24 && !doForce12) return (CFStringRef)CFRetain(inString);
727
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++) {
733 Boolean emit = true;
734 UniChar ch = CFStringGetCharacterAtIndex(inString, idx);
735 switch (ch) {
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
746 emit = false;
747 if (idx + 1 < cnt && ' ' == CFStringGetCharacterAtIndex(inString, idx + 1)) idx++;
748 }
749 break;
750 case ' ':
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) {
754 emit = false;
755 idx++;
756 }
757 }
758 break;
759 }
760 if (emit) CFStringAppendCharacters(outString, &ch, 1);
761 }
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"));
768 } else {
769 CFIndex lastPos = (-1 != lastSecond) ? lastSecond : ((-1 != lastMinute) ? lastMinute : -1);
770 if (-1 != lastPos) {
771 cnt = CFStringGetLength(outString);
772 lastPos++;
773 UniChar ch = (lastPos < cnt) ? CFStringGetCharacterAtIndex(outString, lastPos) : 0;
774 switch (ch) {
775 case '\"': lastPos++; break;
776 case '\'':;
777 again:;
778 do {
779 lastPos++;
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;
785 break;
786 }
787 CFStringInsert(outString, lastPos, CFSTR(" a"));
788 }
789 }
790 }
791 return outString;
792 }
793
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);
799 } else {
800 __substituteFormatStringFromPrefsDF(formatter, false);
801 __substituteFormatStringFromPrefsDF(formatter, true);
802 }
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);
807 }
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);
812 }
813 if (NULL != metapref && CFGetTypeID(metapref) == CFNumberGetTypeID()) {
814 CFIndex wkdy;
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);
818 }
819 }
820 metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleMinDaysInFirstWeek")) : NULL;
821 if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
822 metapref = (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)metapref, calID);
823 }
824 if (NULL != metapref && CFGetTypeID(metapref) == CFNumberGetTypeID()) {
825 CFIndex mwd;
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);
829 }
830 }
831 }
832
833 CFLocaleRef CFDateFormatterGetLocale(CFDateFormatterRef formatter) {
834 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
835 return formatter->_locale;
836 }
837
838 CFDateFormatterStyle CFDateFormatterGetDateStyle(CFDateFormatterRef formatter) {
839 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
840 return formatter->_dateStyle;
841 }
842
843 CFDateFormatterStyle CFDateFormatterGetTimeStyle(CFDateFormatterRef formatter) {
844 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
845 return formatter->_timeStyle;
846 }
847
848 CFStringRef CFDateFormatterGetFormat(CFDateFormatterRef formatter) {
849 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
850 return formatter->_format;
851 }
852
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
866 }
867 STACK_BUFFER_DECL(UChar, ubuffer, cnt);
868 const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)formatString);
869 if (NULL == ustr) {
870 CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer);
871 ustr = ubuffer;
872 }
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;
880 }
881 }
882 if (formatString) CFRelease(formatString);
883 }
884
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));
891 }
892
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) {
903 cnt = used + 1;
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);
907 }
908 CFStringRef string = NULL;
909 if (U_SUCCESS(status)) {
910 string = CFStringCreateWithCharacters(allocator, (const UniChar *)(ustr ? ustr : ubuffer), used);
911 }
912 if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr);
913 return string;
914 }
915
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);
921 }
922
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);
928 if (!isEra) {
929 currYear %= 100;
930 currEraOrCentury = currEraOrCentury / 100 * 100; // get century
931 }
932
933 CFIndex futureMax = currYear + futureYears;
934 CFIndex pastMin = currYear - pastYears;
935
936 CFRange currRange, futureRange, pastRange;
937 currRange.location = futureRange.location = pastRange.location = kCFNotFound;
938 currRange.length = futureRange.length = pastRange.length = 0;
939 if (!isEra) {
940 if (period < INT_MAX && futureMax >= period) {
941 futureRange.location = 0;
942 futureRange.length = futureMax - period + 1;
943 }
944 if (pastMin < 0) {
945 pastRange.location = period + pastMin;
946 pastRange.length = period - pastRange.location;
947 }
948 if (pastRange.location != kCFNotFound) {
949 currRange.location = 0;
950 } else {
951 currRange.location = pastMin;
952 }
953 } else {
954 if (period < INT_MAX && futureMax > period) {
955 futureRange.location = 1,
956 futureRange.length = futureMax - period;
957 }
958 if (pastMin <= 0) {
959 pastRange.location = period + pastMin;
960 pastRange.length = period - pastRange.location + 1;
961 }
962 if (pastRange.location != kCFNotFound) {
963 currRange.location = 1;
964 } else {
965 currRange.location = pastMin;
966 }
967
968 }
969 currRange.length = period - pastRange.length - futureRange.length;
970
971 ucal_setMillis(calendar, at, status);
972 int32_t atYear = ucal_get(calendar, UCAL_YEAR, status);
973 if (!isEra) {
974 atYear %= 100;
975 currEraOrCentury += atYear;
976 }
977
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
983 }
984 if (!isEra) offset *= 100;
985 return __CFDateFormatterCorrectTimeWithTarget(calendar, at, currEraOrCentury+offset, isEra, status);
986 }
987
988 static int32_t __CFDateFormatterGetMaxYearGivenJapaneseEra(UCalendar *calendar, int32_t era, UErrorCode *status) {
989 int32_t years = 0;
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);
995 return years+1;
996 }
997
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);
1003 }
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:
1010 success = false;
1011 break;
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);
1016 break;
1017 }
1018 case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate:
1019 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 29, 30, true, status);
1020 break;
1021 case kCFDateFormatterAmbiguousYearAssumeToFuture:
1022 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 0, 59, true, status);
1023 break;
1024 case kCFDateFormatterAmbiguousYearAssumeToPast:
1025 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 59, 0, true, status);
1026 break;
1027 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture:
1028 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 10, 49, true, status);
1029 break;
1030 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast:
1031 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 49, 10, true, status);
1032 break;
1033 case kCFDateFormatterAmbiguousYearAssumeToNone:
1034 default:
1035 break; // do nothing
1036 }
1037 }
1038 } else if (calendar_id == kCFCalendarIdentifierJapanese) { // ??? need more work
1039 ucal_clear(cal);
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:
1051 success = false;
1052 break;
1053 case kCFDateFormatterAmbiguousYearAssumeToCurrent:
1054 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1055 break;
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) {
1063 success = true;
1064 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra-1, true, status);
1065 }
1066 } else { // current era
1067 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1068 }
1069 break;
1070 case kCFDateFormatterAmbiguousYearAssumeToFuture:
1071 if (givenYear < currYear) { // we only consider current or the future
1072 success = false;
1073 } else { // current era
1074 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1075 }
1076 break;
1077 case kCFDateFormatterAmbiguousYearAssumeToPast:
1078 if (givenYear > currYear) { // past era
1079 success = false;
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) {
1085 continue;
1086 }
1087 success = true;
1088 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, era, true, status);
1089 break;
1090 }
1091 } else { // current era
1092 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1093 }
1094 break;
1095 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture:
1096 if (givenYear < currYear - 10) { // we allow 10 years to the past
1097 success = false;
1098 } else {
1099 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1100 }
1101 break;
1102 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast:
1103 if (givenYear > currYear + 10) {
1104 success = false;
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) {
1110 continue;
1111 }
1112 success = true;
1113 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, era, true, status);
1114 break;
1115 }
1116 } else { // current era
1117 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1118 }
1119 break;
1120 case kCFDateFormatterAmbiguousYearAssumeToNone:
1121 default:
1122 break; // do nothing
1123 }
1124 }
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:
1132 success = false;
1133 break;
1134 case kCFDateFormatterAmbiguousYearAssumeToCurrent:
1135 *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, (currYear / 100 * 100) + parsedYear % 100, false, status);
1136 break;
1137 case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate:
1138 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 50, 49, false, status);
1139 break;
1140 case kCFDateFormatterAmbiguousYearAssumeToFuture:
1141 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 0, 99, false, status);
1142 break;
1143 case kCFDateFormatterAmbiguousYearAssumeToPast:
1144 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 99, 0, false, status);
1145 break;
1146 case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture:
1147 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 9, 90, false, status);
1148 break;
1149 case kCFDateFormatterAmbiguousYearAssumeToLikelyPast:
1150 *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 90, 9, false, status);
1151 break;
1152 case kCFDateFormatterAmbiguousYearAssumeToNone:
1153 default:
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);
1157 }
1158 break; // do nothing
1159 }
1160 }
1161
1162 }
1163 return success;
1164 }
1165
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());
1171 CFAbsoluteTime at;
1172 if (CFDateFormatterGetAbsoluteTimeFromString(formatter, string, rangep, &at)) {
1173 return CFDateCreate(allocator, at);
1174 }
1175 return NULL;
1176 }
1177
1178 Boolean CFDateFormatterGetAbsoluteTimeFromString(CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep, CFAbsoluteTime *atp) {
1179 __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1180 __CFGenericValidateType(string, CFStringGetTypeID());
1181 CFRange range = {0, 0};
1182 if (rangep) {
1183 range = *rangep;
1184 } else {
1185 range.length = CFStringGetLength(string);
1186 }
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);
1190 if (NULL == ustr) {
1191 CFStringGetCharacters(string, range, (UniChar *)ubuffer);
1192 ustr = ubuffer;
1193 } else {
1194 ustr += range.location;
1195 }
1196 UDate udate;
1197 int32_t dpos = 0;
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);
1217 ucal_clear(cal2);
1218 udat_set2DigitYearStart(df2, future, &status);
1219 } else if (calendar_id == kCFCalendarIdentifierChinese) {
1220 ucal_clear(cal2);
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);
1225 ucal_clear(cal2);
1226 ucal_set(cal2, UCAL_ERA, currEra);
1227 }
1228 if (formatter->_property._DefaultDate) {
1229 CFAbsoluteTime at = CFDateGetAbsoluteTime(formatter->_property._DefaultDate);
1230 udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
1231 ucal_setMillis(cal2, udate, &status);
1232 }
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)) {
1239 if (atp) {
1240 *atp = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
1241 }
1242 success = true;
1243 }
1244 CFRelease(calendar_id);
1245 udat_close(df2);
1246 return success;
1247 }
1248
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;
1264 }
1265 status = U_ZERO_ERROR;
1266 udat_setSymbols(icudf, (UDateFormatSymbolType)icucode, idx + index_base, item_ustr, item_cnt, &status);
1267 }
1268 }
1269
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);
1283 }
1284 strings[idx] = !str ? (CFStringRef)CFRetain(CFSTR("<error>")) : str;
1285 }
1286 CFArrayRef array = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)strings, cnt, &kCFTypeArrayCallBacks);
1287 while (cnt--) {
1288 CFRelease(strings[cnt]);
1289 }
1290 return array;
1291 }
1292
1293 #define SET_SYMBOLS_ARRAY(A, B, C) \
1294 if (!directToICU) { \
1295 oldProperty = formatter->_property. C; \
1296 formatter->_property. C = NULL; \
1297 } \
1298 __CFDateFormatterSetSymbolsArray(formatter->_df, A, B, value); \
1299 if (!directToICU) { \
1300 formatter->_property. C = __CFDateFormatterGetSymbolsArray(formatter->_df, A, B); \
1301 }
1302
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];
1309
1310 if (kCFDateFormatterIsLenientKey == key) {
1311 if (!directToICU) {
1312 oldProperty = formatter->_property. _IsLenient;
1313 formatter->_property. _IsLenient = NULL;
1314 }
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));
1319 if (!directToICU) {
1320 formatter->_property. _IsLenient = (CFBooleanRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterIsLenientKey);
1321 }
1322 } else if (kCFDateFormatterDoesRelativeDateFormattingKey == key) {
1323 if (!directToICU) {
1324 oldProperty = formatter->_property. _DoesRelativeDateFormatting;
1325 formatter->_property. _DoesRelativeDateFormatting = NULL;
1326 }
1327 __CFGenericValidateType(value, CFBooleanGetTypeID());
1328 if (!directToICU) {
1329 if (kCFBooleanTrue != value) value = kCFBooleanFalse;
1330 formatter->_property. _DoesRelativeDateFormatting = value ? (CFBooleanRef)CFRetain(value) : NULL;
1331 __ResetUDateFormat(formatter, false);
1332 }
1333 } else if (kCFDateFormatterCalendarKey == key) {
1334 if (!directToICU) {
1335 oldProperty = formatter->_property. _Calendar;
1336 formatter->_property. _Calendar = NULL;
1337 }
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);
1355 if (!directToICU) {
1356 formatter->_property. _Calendar = (CFCalendarRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarKey);
1357 }
1358 } else if (kCFDateFormatterCalendarIdentifierKey == key) {
1359 if (!directToICU) {
1360 oldProperty = formatter->_property. _CalendarName;
1361 formatter->_property. _CalendarName = NULL;
1362 }
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);
1378 if (!directToICU) {
1379 formatter->_property. _CalendarName = (CFStringRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarIdentifierKey);
1380 }
1381 } else if (kCFDateFormatterTimeZoneKey == key) {
1382 if (formatter->_property. _TimeZone != value) {
1383 if (!directToICU) {
1384 oldProperty = formatter->_property. _TimeZone;
1385 formatter->_property. _TimeZone = NULL;
1386 }
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);
1397 if (!directToICU) {
1398 old = formatter->_property._TimeZone;
1399 formatter->_property. _TimeZone = (CFTimeZoneRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterTimeZoneKey);
1400 if (old) CFRelease(old);
1401 }
1402 }
1403 } else if (kCFDateFormatterDefaultFormatKey == key) {
1404 // read-only attribute
1405 } else if (kCFDateFormatterTwoDigitStartDateKey == key) {
1406 if (!directToICU) {
1407 oldProperty = formatter->_property. _TwoDigitStartDate;
1408 formatter->_property. _TwoDigitStartDate = NULL;
1409 }
1410 __CFGenericValidateType(value, CFDateGetTypeID());
1411 CFAbsoluteTime at = CFDateGetAbsoluteTime((CFDateRef)value);
1412 UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
1413 udat_set2DigitYearStart(formatter->_df, udate, &status);
1414 if (!directToICU) {
1415 formatter->_property. _TwoDigitStartDate = (CFDateRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterTwoDigitStartDateKey);
1416 }
1417 } else if (kCFDateFormatterDefaultDateKey == key) {
1418 if (!directToICU) {
1419 oldProperty = formatter->_property. _DefaultDate;
1420 formatter->_property. _DefaultDate = NULL;
1421 }
1422 __CFGenericValidateType(value, CFDateGetTypeID());
1423 if (!directToICU) {
1424 formatter->_property._DefaultDate = value ? (CFDateRef)CFRetain(value) : NULL;
1425 }
1426 } else if (kCFDateFormatterGregorianStartDateKey == key) {
1427 if (!directToICU) {
1428 oldProperty = formatter->_property. _GregorianStartDate;
1429 formatter->_property. _GregorianStartDate = NULL;
1430 }
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);
1436 if (!directToICU) {
1437 formatter->_property. _GregorianStartDate = (CFDateRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterGregorianStartDateKey);
1438 }
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) {
1476 if (!directToICU) {
1477 oldProperty = formatter->_property. _AMSymbol;
1478 formatter->_property. _AMSymbol = NULL;
1479 }
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;
1488 }
1489 udat_setSymbols(formatter->_df, UDAT_AM_PMS, 0, item_ustr, item_cnt, &status);
1490 if (!directToICU) {
1491 formatter->_property. _AMSymbol = (CFStringRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterAMSymbolKey);
1492 }
1493 } else if (kCFDateFormatterPMSymbolKey == key) {
1494 if (!directToICU) {
1495 oldProperty = formatter->_property. _PMSymbol;
1496 formatter->_property. _PMSymbol = NULL;
1497 }
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;
1506 }
1507 udat_setSymbols(formatter->_df, UDAT_AM_PMS, 1, item_ustr, item_cnt, &status);
1508 if (!directToICU) {
1509 formatter->_property. _PMSymbol = (CFStringRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterPMSymbolKey);
1510 }
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);
1516 } else {
1517 CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key);
1518 }
1519 if (oldProperty) CFRelease(oldProperty);
1520 }
1521
1522 void CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value) {
1523 __CFDateFormatterSetProperty(formatter, key, value, false);
1524 }
1525
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];
1531
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);
1557 }
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);
1567 }
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);
1625 if (2 <= cnt) {
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);
1629 }
1630 }
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);
1634 if (2 <= cnt) {
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);
1638 }
1639 }
1640 } else if (kCFDateFormatterAmbiguousYearStrategyKey == key) {
1641 if (formatter->_property._AmbiguousYearStrategy) return CFRetain(formatter->_property._AmbiguousYearStrategy);
1642 } else {
1643 CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key);
1644 }
1645 return NULL;
1646 }
1647
1648