]>
Commit | Line | Data |
---|---|---|
bd5b749c | 1 | /* |
8ca704e1 | 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. |
bd5b749c A |
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 | */ | |
f64f9b69 | 23 | |
cf7d2af9 | 24 | /* CFDateFormatter.c |
8ca704e1 A |
25 | Copyright (c) 2002-2011, Apple Inc. All rights reserved. |
26 | Responsibility: David Smith | |
bd5b749c A |
27 | */ |
28 | ||
8ca704e1 A |
29 | #define U_SHOW_INTERNAL_API 1 |
30 | ||
bd5b749c A |
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> | |
8ca704e1 | 36 | #include "CFPriv.h" |
bd5b749c | 37 | #include "CFInternal.h" |
cf7d2af9 | 38 | #include "CFLocaleInternal.h" |
bd5b749c | 39 | #include <unicode/udat.h> |
cf7d2af9 | 40 | #include <unicode/udatpg.h> |
bd5b749c A |
41 | #include <math.h> |
42 | #include <float.h> | |
43 | ||
44 | extern UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz); | |
45 | static void __CFDateFormatterCustomize(CFDateFormatterRef formatter); | |
46 | ||
cf7d2af9 A |
47 | CF_EXPORT const CFStringRef kCFDateFormatterCalendarIdentifierKey; |
48 | ||
49 | #undef CFReleaseIfNotNull | |
50 | #define CFReleaseIfNotNull(X) if (X) CFRelease(X) | |
bd5b749c A |
51 | |
52 | #define BUFFER_SIZE 768 | |
53 | ||
8ca704e1 A |
54 | static CFStringRef __CFDateFormatterCreateForcedTemplate(CFLocaleRef locale, CFStringRef inString); |
55 | ||
56 | // If you pass in a string in tmplate, you get back NULL (failure) or a CFStringRef. | |
57 | // 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. | |
58 | ||
59 | CFArrayRef CFDateFormatterCreateDateFormatsFromTemplates(CFAllocatorRef allocator, CFArrayRef tmplates, CFOptionFlags options, CFLocaleRef locale) { | |
60 | return (CFArrayRef)CFDateFormatterCreateDateFormatFromTemplate(allocator, (CFStringRef)tmplates, options, locale); | |
61 | } | |
62 | ||
cf7d2af9 A |
63 | CFStringRef CFDateFormatterCreateDateFormatFromTemplate(CFAllocatorRef allocator, CFStringRef tmplate, CFOptionFlags options, CFLocaleRef locale) { |
64 | if (allocator) __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
65 | if (locale) __CFGenericValidateType(locale, CFLocaleGetTypeID()); | |
8ca704e1 A |
66 | Boolean tmplateIsString = (CFStringGetTypeID() == CFGetTypeID(tmplate)); |
67 | if (!tmplateIsString) { | |
68 | __CFGenericValidateType(tmplate, CFArrayGetTypeID()); | |
69 | } | |
cf7d2af9 A |
70 | |
71 | CFStringRef localeName = locale ? CFLocaleGetIdentifier(locale) : CFSTR(""); | |
72 | char buffer[BUFFER_SIZE]; | |
73 | const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); | |
74 | if (NULL == cstr) { | |
75 | if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; | |
76 | } | |
77 | if (NULL == cstr) { | |
78 | return NULL; | |
79 | } | |
80 | ||
81 | UErrorCode status = U_ZERO_ERROR; | |
82 | UDateTimePatternGenerator *ptg = udatpg_open(cstr, &status); | |
83 | if (NULL == ptg || U_FAILURE(status)) { | |
84 | return NULL; | |
85 | } | |
86 | ||
8ca704e1 A |
87 | CFTypeRef result = tmplateIsString ? NULL : (CFTypeRef)CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks); |
88 | ||
89 | for (CFIndex idx = 0, cnt = tmplateIsString ? 1 : CFArrayGetCount((CFArrayRef)tmplate); idx < cnt; idx++) { | |
90 | CFStringRef tmplateString = tmplateIsString ? (CFStringRef)tmplate : (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)tmplate, idx); | |
91 | CFStringRef resultString = NULL; | |
92 | ||
93 | tmplateString = __CFDateFormatterCreateForcedTemplate(locale ? locale : CFLocaleGetSystem(), tmplateString); | |
94 | ||
95 | CFIndex jCount = 0; // the only interesting cases are 0, 1, and 2 (adjacent) | |
96 | CFRange r = CFStringFind(tmplateString, CFSTR("j"), 0); | |
97 | if (kCFNotFound != r.location) { | |
cf7d2af9 | 98 | jCount++; |
8ca704e1 A |
99 | if ((r.location + 1 < CFStringGetLength(tmplateString)) && ('j' == CFStringGetCharacterAtIndex(tmplateString, r.location + 1))) { |
100 | jCount++; | |
101 | } | |
cf7d2af9 | 102 | } |
cf7d2af9 | 103 | |
8ca704e1 A |
104 | UChar pattern[BUFFER_SIZE], skel[BUFFER_SIZE], bpat[BUFFER_SIZE]; |
105 | CFIndex tmpltLen = CFStringGetLength(tmplateString); | |
106 | if (BUFFER_SIZE < tmpltLen) tmpltLen = BUFFER_SIZE; | |
107 | CFStringGetCharacters(tmplateString, CFRangeMake(0, tmpltLen), (UniChar *)pattern); | |
108 | CFRelease(tmplateString); | |
cf7d2af9 | 109 | |
8ca704e1 A |
110 | int32_t patlen = tmpltLen; |
111 | status = U_ZERO_ERROR; | |
112 | int32_t skellen = udatpg_getSkeleton(ptg, pattern, patlen, skel, sizeof(skel) / sizeof(skel[0]), &status); | |
113 | if (!U_FAILURE(status)) { | |
114 | if ((0 < jCount) && (skellen + jCount < (sizeof(skel) / sizeof(skel[0])))) { | |
115 | skel[skellen++] = 'j'; | |
116 | if (1 < jCount) skel[skellen++] = 'j'; | |
117 | } | |
cf7d2af9 | 118 | |
8ca704e1 A |
119 | status = U_ZERO_ERROR; |
120 | int32_t bpatlen = udatpg_getBestPattern(ptg, skel, skellen, bpat, sizeof(bpat) / sizeof(bpat[0]), &status); | |
121 | if (!U_FAILURE(status)) { | |
122 | resultString = CFStringCreateWithCharacters(allocator, (const UniChar *)bpat, bpatlen); | |
123 | } | |
124 | } | |
cf7d2af9 | 125 | |
8ca704e1 A |
126 | if (tmplateIsString) { |
127 | result = (CFTypeRef)resultString; | |
128 | } else { | |
129 | CFArrayAppendValue((CFMutableArrayRef)result, resultString ? (CFTypeRef)resultString : (CFTypeRef)kCFNull); | |
130 | if (resultString) CFRelease(resultString); | |
131 | } | |
cf7d2af9 | 132 | } |
8ca704e1 | 133 | |
cf7d2af9 A |
134 | udatpg_close(ptg); |
135 | ||
8ca704e1 | 136 | return (CFStringRef)result; |
cf7d2af9 A |
137 | } |
138 | ||
bd5b749c A |
139 | struct __CFDateFormatter { |
140 | CFRuntimeBase _base; | |
141 | UDateFormat *_df; | |
142 | CFLocaleRef _locale; | |
143 | CFDateFormatterStyle _timeStyle; | |
144 | CFDateFormatterStyle _dateStyle; | |
145 | CFStringRef _format; | |
146 | CFStringRef _defformat; | |
cf7d2af9 A |
147 | struct { |
148 | CFBooleanRef _IsLenient; | |
149 | CFBooleanRef _DoesRelativeDateFormatting; | |
150 | CFBooleanRef _HasCustomFormat; | |
151 | CFTimeZoneRef _TimeZone; | |
152 | CFCalendarRef _Calendar; | |
153 | CFStringRef _CalendarName; | |
154 | CFDateRef _TwoDigitStartDate; | |
155 | CFDateRef _DefaultDate; | |
156 | CFDateRef _GregorianStartDate; | |
157 | CFArrayRef _EraSymbols; | |
158 | CFArrayRef _LongEraSymbols; | |
159 | CFArrayRef _MonthSymbols; | |
160 | CFArrayRef _ShortMonthSymbols; | |
161 | CFArrayRef _VeryShortMonthSymbols; | |
162 | CFArrayRef _StandaloneMonthSymbols; | |
163 | CFArrayRef _ShortStandaloneMonthSymbols; | |
164 | CFArrayRef _VeryShortStandaloneMonthSymbols; | |
165 | CFArrayRef _WeekdaySymbols; | |
166 | CFArrayRef _ShortWeekdaySymbols; | |
167 | CFArrayRef _VeryShortWeekdaySymbols; | |
168 | CFArrayRef _StandaloneWeekdaySymbols; | |
169 | CFArrayRef _ShortStandaloneWeekdaySymbols; | |
170 | CFArrayRef _VeryShortStandaloneWeekdaySymbols; | |
171 | CFArrayRef _QuarterSymbols; | |
172 | CFArrayRef _ShortQuarterSymbols; | |
173 | CFArrayRef _StandaloneQuarterSymbols; | |
174 | CFArrayRef _ShortStandaloneQuarterSymbols; | |
175 | CFStringRef _AMSymbol; | |
176 | CFStringRef _PMSymbol; | |
177 | } _property; | |
bd5b749c A |
178 | }; |
179 | ||
180 | static CFStringRef __CFDateFormatterCopyDescription(CFTypeRef cf) { | |
181 | CFDateFormatterRef formatter = (CFDateFormatterRef)cf; | |
182 | return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("<CFDateFormatter %p [%p]>"), cf, CFGetAllocator(formatter)); | |
183 | } | |
184 | ||
185 | static void __CFDateFormatterDeallocate(CFTypeRef cf) { | |
186 | CFDateFormatterRef formatter = (CFDateFormatterRef)cf; | |
187 | if (formatter->_df) udat_close(formatter->_df); | |
188 | if (formatter->_locale) CFRelease(formatter->_locale); | |
189 | if (formatter->_format) CFRelease(formatter->_format); | |
190 | if (formatter->_defformat) CFRelease(formatter->_defformat); | |
cf7d2af9 A |
191 | CFReleaseIfNotNull(formatter->_property._IsLenient); |
192 | CFReleaseIfNotNull(formatter->_property._DoesRelativeDateFormatting); | |
193 | CFReleaseIfNotNull(formatter->_property._TimeZone); | |
194 | CFReleaseIfNotNull(formatter->_property._Calendar); | |
195 | CFReleaseIfNotNull(formatter->_property._CalendarName); | |
196 | CFReleaseIfNotNull(formatter->_property._TwoDigitStartDate); | |
197 | CFReleaseIfNotNull(formatter->_property._DefaultDate); | |
198 | CFReleaseIfNotNull(formatter->_property._GregorianStartDate); | |
199 | CFReleaseIfNotNull(formatter->_property._EraSymbols); | |
200 | CFReleaseIfNotNull(formatter->_property._LongEraSymbols); | |
201 | CFReleaseIfNotNull(formatter->_property._MonthSymbols); | |
202 | CFReleaseIfNotNull(formatter->_property._ShortMonthSymbols); | |
203 | CFReleaseIfNotNull(formatter->_property._VeryShortMonthSymbols); | |
204 | CFReleaseIfNotNull(formatter->_property._StandaloneMonthSymbols); | |
205 | CFReleaseIfNotNull(formatter->_property._ShortStandaloneMonthSymbols); | |
206 | CFReleaseIfNotNull(formatter->_property._VeryShortStandaloneMonthSymbols); | |
207 | CFReleaseIfNotNull(formatter->_property._WeekdaySymbols); | |
208 | CFReleaseIfNotNull(formatter->_property._ShortWeekdaySymbols); | |
209 | CFReleaseIfNotNull(formatter->_property._VeryShortWeekdaySymbols); | |
210 | CFReleaseIfNotNull(formatter->_property._StandaloneWeekdaySymbols); | |
211 | CFReleaseIfNotNull(formatter->_property._ShortStandaloneWeekdaySymbols); | |
212 | CFReleaseIfNotNull(formatter->_property._VeryShortStandaloneWeekdaySymbols); | |
213 | CFReleaseIfNotNull(formatter->_property._QuarterSymbols); | |
214 | CFReleaseIfNotNull(formatter->_property._ShortQuarterSymbols); | |
215 | CFReleaseIfNotNull(formatter->_property._StandaloneQuarterSymbols); | |
216 | CFReleaseIfNotNull(formatter->_property._ShortStandaloneQuarterSymbols); | |
217 | CFReleaseIfNotNull(formatter->_property._AMSymbol); | |
218 | CFReleaseIfNotNull(formatter->_property._PMSymbol); | |
219 | } | |
220 | ||
221 | static CFStringRef __CFDateFormatterCreateForcedString(CFDateFormatterRef formatter, CFStringRef inString); | |
222 | ||
223 | static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value, Boolean directToICU); | |
224 | ||
225 | #define RESET_PROPERTY(C, K) \ | |
226 | if (df->_property. C) __CFDateFormatterSetProperty(df, K, df->_property. C, true); | |
227 | ||
8ca704e1 A |
228 | // This blows away any custom format string the client may have set |
229 | // on the date formatter with CFDateFormatterSetFormat(). | |
cf7d2af9 A |
230 | static void __ResetUDateFormat(CFDateFormatterRef df, Boolean goingToHaveCustomFormat) { |
231 | if (df->_df) udat_close(df->_df); | |
232 | df->_df = NULL; | |
233 | ||
234 | // uses _timeStyle, _dateStyle, _locale, _property._TimeZone; sets _df, _format, _defformat | |
235 | char loc_buffer[BUFFER_SIZE]; | |
236 | loc_buffer[0] = 0; | |
237 | CFStringRef tmpLocName = df->_locale ? CFLocaleGetIdentifier(df->_locale) : CFSTR(""); | |
238 | CFStringGetCString(tmpLocName, loc_buffer, BUFFER_SIZE, kCFStringEncodingASCII); | |
239 | ||
240 | UChar tz_buffer[BUFFER_SIZE]; | |
241 | tz_buffer[0] = 0; | |
242 | CFStringRef tmpTZName = df->_property._TimeZone ? CFTimeZoneGetName(df->_property._TimeZone) : CFSTR("GMT"); | |
243 | CFStringGetCharacters(tmpTZName, CFRangeMake(0, CFStringGetLength(tmpTZName)), (UniChar *)tz_buffer); | |
244 | ||
8ca704e1 | 245 | df->_property._HasCustomFormat = NULL; |
cf7d2af9 A |
246 | |
247 | int32_t udstyle = 0, utstyle = 0; | |
248 | switch (df->_dateStyle) { | |
249 | case kCFDateFormatterNoStyle: udstyle = UDAT_NONE; break; | |
250 | case kCFDateFormatterShortStyle: udstyle = UDAT_SHORT; break; | |
251 | case kCFDateFormatterMediumStyle: udstyle = UDAT_MEDIUM; break; | |
252 | case kCFDateFormatterLongStyle: udstyle = UDAT_LONG; break; | |
253 | case kCFDateFormatterFullStyle: udstyle = UDAT_FULL; break; | |
254 | } | |
255 | switch (df->_timeStyle) { | |
256 | case kCFDateFormatterNoStyle: utstyle = UDAT_NONE; break; | |
257 | case kCFDateFormatterShortStyle: utstyle = UDAT_SHORT; break; | |
258 | case kCFDateFormatterMediumStyle: utstyle = UDAT_MEDIUM; break; | |
259 | case kCFDateFormatterLongStyle: utstyle = UDAT_LONG; break; | |
260 | case kCFDateFormatterFullStyle: utstyle = UDAT_FULL; break; | |
261 | } | |
262 | Boolean wantRelative = (NULL != df->_property._DoesRelativeDateFormatting && df->_property._DoesRelativeDateFormatting == kCFBooleanTrue); | |
263 | Boolean hasFormat = (NULL != df->_property._HasCustomFormat && df->_property._HasCustomFormat == kCFBooleanTrue) || goingToHaveCustomFormat; | |
264 | if (wantRelative && !hasFormat) { | |
265 | udstyle |= UDAT_RELATIVE; | |
266 | } | |
267 | ||
268 | UErrorCode status = U_ZERO_ERROR; | |
269 | UDateFormat *icudf = udat_open((UDateFormatStyle)utstyle, (UDateFormatStyle)udstyle, loc_buffer, tz_buffer, CFStringGetLength(tmpTZName), NULL, 0, &status); | |
270 | if (NULL == icudf || U_FAILURE(status)) { | |
271 | return; | |
272 | } | |
273 | udat_setLenient(icudf, 0); | |
274 | if (kCFDateFormatterNoStyle == df->_dateStyle && kCFDateFormatterNoStyle == df->_timeStyle) { | |
275 | udat_applyPattern(icudf, false, NULL, 0); | |
276 | } | |
277 | CFStringRef calident = (CFStringRef)CFLocaleGetValue(df->_locale, kCFLocaleCalendarIdentifierKey); | |
278 | if (calident && CFEqual(calident, kCFCalendarIdentifierGregorian)) { | |
279 | status = U_ZERO_ERROR; | |
280 | udat_set2DigitYearStart(icudf, -631152000000.0, &status); // 1950-01-01 00:00:00 GMT | |
281 | } | |
282 | df->_df = icudf; | |
283 | ||
284 | __CFDateFormatterCustomize(df); | |
285 | ||
286 | UChar ubuffer[BUFFER_SIZE]; | |
287 | status = U_ZERO_ERROR; | |
288 | int32_t ret = udat_toPattern(icudf, false, ubuffer, BUFFER_SIZE, &status); | |
289 | if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { | |
290 | CFStringRef newFormat = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ret); | |
291 | CFStringRef formatString = __CFDateFormatterCreateForcedString(df, newFormat); | |
292 | CFIndex cnt = CFStringGetLength(formatString); | |
293 | CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__); | |
294 | if (df->_format != formatString && cnt <= 1024) { | |
295 | STACK_BUFFER_DECL(UChar, ubuffer, cnt); | |
296 | const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)formatString); | |
297 | if (NULL == ustr) { | |
298 | CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
299 | ustr = ubuffer; | |
300 | } | |
301 | UErrorCode status = U_ZERO_ERROR; | |
302 | // udat_applyPattern(df->_df, false, ustr, cnt, &status); | |
303 | udat_applyPattern(df->_df, false, ustr, cnt); | |
304 | if (U_SUCCESS(status)) { | |
305 | if (df->_format) CFRelease(df->_format); | |
306 | df->_format = (CFStringRef)CFStringCreateCopy(CFGetAllocator(df), formatString); | |
307 | } | |
308 | } | |
309 | CFRelease(formatString); | |
310 | CFRelease(newFormat); | |
311 | } | |
312 | if (df->_defformat) CFRelease(df->_defformat); | |
313 | df->_defformat = df->_format ? (CFStringRef)CFRetain(df->_format) : NULL; | |
314 | ||
315 | RESET_PROPERTY(_IsLenient, kCFDateFormatterIsLenientKey); | |
316 | RESET_PROPERTY(_DoesRelativeDateFormatting, kCFDateFormatterDoesRelativeDateFormattingKey); | |
317 | RESET_PROPERTY(_Calendar, kCFDateFormatterCalendarKey); | |
318 | RESET_PROPERTY(_CalendarName, kCFDateFormatterCalendarIdentifierKey); | |
319 | RESET_PROPERTY(_TimeZone, kCFDateFormatterTimeZoneKey); | |
320 | RESET_PROPERTY(_TwoDigitStartDate, kCFDateFormatterTwoDigitStartDateKey); | |
321 | RESET_PROPERTY(_DefaultDate, kCFDateFormatterDefaultDateKey); | |
322 | RESET_PROPERTY(_GregorianStartDate, kCFDateFormatterGregorianStartDateKey); | |
323 | RESET_PROPERTY(_EraSymbols, kCFDateFormatterEraSymbolsKey); | |
324 | RESET_PROPERTY(_LongEraSymbols, kCFDateFormatterLongEraSymbolsKey); | |
325 | RESET_PROPERTY(_MonthSymbols, kCFDateFormatterMonthSymbolsKey); | |
326 | RESET_PROPERTY(_ShortMonthSymbols, kCFDateFormatterShortMonthSymbolsKey); | |
327 | RESET_PROPERTY(_VeryShortMonthSymbols, kCFDateFormatterVeryShortMonthSymbolsKey); | |
328 | RESET_PROPERTY(_StandaloneMonthSymbols, kCFDateFormatterStandaloneMonthSymbolsKey); | |
329 | RESET_PROPERTY(_ShortStandaloneMonthSymbols, kCFDateFormatterShortStandaloneMonthSymbolsKey); | |
330 | RESET_PROPERTY(_VeryShortStandaloneMonthSymbols, kCFDateFormatterVeryShortStandaloneMonthSymbolsKey); | |
331 | RESET_PROPERTY(_WeekdaySymbols, kCFDateFormatterWeekdaySymbolsKey); | |
332 | RESET_PROPERTY(_ShortWeekdaySymbols, kCFDateFormatterShortWeekdaySymbolsKey); | |
333 | RESET_PROPERTY(_VeryShortWeekdaySymbols, kCFDateFormatterVeryShortWeekdaySymbolsKey); | |
334 | RESET_PROPERTY(_StandaloneWeekdaySymbols, kCFDateFormatterStandaloneWeekdaySymbolsKey); | |
335 | RESET_PROPERTY(_ShortStandaloneWeekdaySymbols, kCFDateFormatterShortStandaloneWeekdaySymbolsKey); | |
336 | RESET_PROPERTY(_VeryShortStandaloneWeekdaySymbols, kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey); | |
337 | RESET_PROPERTY(_QuarterSymbols, kCFDateFormatterQuarterSymbolsKey); | |
338 | RESET_PROPERTY(_ShortQuarterSymbols, kCFDateFormatterShortQuarterSymbolsKey); | |
339 | RESET_PROPERTY(_StandaloneQuarterSymbols, kCFDateFormatterStandaloneQuarterSymbolsKey); | |
340 | RESET_PROPERTY(_ShortStandaloneQuarterSymbols, kCFDateFormatterShortStandaloneQuarterSymbolsKey); | |
341 | RESET_PROPERTY(_AMSymbol, kCFDateFormatterAMSymbolKey); | |
342 | RESET_PROPERTY(_PMSymbol, kCFDateFormatterPMSymbolKey); | |
bd5b749c A |
343 | } |
344 | ||
345 | static CFTypeID __kCFDateFormatterTypeID = _kCFRuntimeNotATypeID; | |
346 | ||
347 | static const CFRuntimeClass __CFDateFormatterClass = { | |
348 | 0, | |
349 | "CFDateFormatter", | |
cf7d2af9 A |
350 | NULL, // init |
351 | NULL, // copy | |
bd5b749c A |
352 | __CFDateFormatterDeallocate, |
353 | NULL, | |
354 | NULL, | |
cf7d2af9 | 355 | NULL, // |
bd5b749c A |
356 | __CFDateFormatterCopyDescription |
357 | }; | |
358 | ||
359 | static void __CFDateFormatterInitialize(void) { | |
360 | __kCFDateFormatterTypeID = _CFRuntimeRegisterClass(&__CFDateFormatterClass); | |
361 | } | |
362 | ||
363 | CFTypeID CFDateFormatterGetTypeID(void) { | |
364 | if (_kCFRuntimeNotATypeID == __kCFDateFormatterTypeID) __CFDateFormatterInitialize(); | |
365 | return __kCFDateFormatterTypeID; | |
366 | } | |
367 | ||
368 | CFDateFormatterRef CFDateFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFDateFormatterStyle dateStyle, CFDateFormatterStyle timeStyle) { | |
369 | struct __CFDateFormatter *memory; | |
370 | uint32_t size = sizeof(struct __CFDateFormatter) - sizeof(CFRuntimeBase); | |
371 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
372 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
373 | if (locale) __CFGenericValidateType(locale, CFLocaleGetTypeID()); | |
374 | memory = (struct __CFDateFormatter *)_CFRuntimeCreateInstance(allocator, CFDateFormatterGetTypeID(), size, NULL); | |
375 | if (NULL == memory) { | |
cf7d2af9 | 376 | return NULL; |
bd5b749c A |
377 | } |
378 | memory->_df = NULL; | |
379 | memory->_locale = NULL; | |
380 | memory->_format = NULL; | |
381 | memory->_defformat = NULL; | |
bd5b749c A |
382 | memory->_dateStyle = dateStyle; |
383 | memory->_timeStyle = timeStyle; | |
cf7d2af9 A |
384 | memory->_property._IsLenient = NULL; |
385 | memory->_property._DoesRelativeDateFormatting = NULL; | |
386 | memory->_property._HasCustomFormat = NULL; | |
387 | memory->_property._TimeZone = NULL; | |
388 | memory->_property._Calendar = NULL; | |
389 | memory->_property._CalendarName = NULL; | |
390 | memory->_property._TwoDigitStartDate = NULL; | |
391 | memory->_property._DefaultDate = NULL; | |
392 | memory->_property._GregorianStartDate = NULL; | |
393 | memory->_property._EraSymbols = NULL; | |
394 | memory->_property._LongEraSymbols = NULL; | |
395 | memory->_property._MonthSymbols = NULL; | |
396 | memory->_property._ShortMonthSymbols = NULL; | |
397 | memory->_property._VeryShortMonthSymbols = NULL; | |
398 | memory->_property._StandaloneMonthSymbols = NULL; | |
399 | memory->_property._ShortStandaloneMonthSymbols = NULL; | |
400 | memory->_property._VeryShortStandaloneMonthSymbols = NULL; | |
401 | memory->_property._WeekdaySymbols = NULL; | |
402 | memory->_property._ShortWeekdaySymbols = NULL; | |
403 | memory->_property._VeryShortWeekdaySymbols = NULL; | |
404 | memory->_property._StandaloneWeekdaySymbols = NULL; | |
405 | memory->_property._ShortStandaloneWeekdaySymbols = NULL; | |
406 | memory->_property._VeryShortStandaloneWeekdaySymbols = NULL; | |
407 | memory->_property._QuarterSymbols = NULL; | |
408 | memory->_property._ShortQuarterSymbols = NULL; | |
409 | memory->_property._StandaloneQuarterSymbols = NULL; | |
410 | memory->_property._ShortStandaloneQuarterSymbols = NULL; | |
411 | memory->_property._AMSymbol = NULL; | |
412 | memory->_property._PMSymbol = NULL; | |
413 | ||
bd5b749c | 414 | switch (dateStyle) { |
cf7d2af9 A |
415 | case kCFDateFormatterNoStyle: |
416 | case kCFDateFormatterShortStyle: | |
417 | case kCFDateFormatterMediumStyle: | |
418 | case kCFDateFormatterLongStyle: | |
419 | case kCFDateFormatterFullStyle: break; | |
bd5b749c | 420 | default: |
cf7d2af9 A |
421 | CFAssert2(0, __kCFLogAssertion, "%s(): unknown date style %d", __PRETTY_FUNCTION__, dateStyle); |
422 | memory->_dateStyle = kCFDateFormatterMediumStyle; | |
423 | break; | |
bd5b749c A |
424 | } |
425 | switch (timeStyle) { | |
cf7d2af9 A |
426 | case kCFDateFormatterNoStyle: |
427 | case kCFDateFormatterShortStyle: | |
428 | case kCFDateFormatterMediumStyle: | |
429 | case kCFDateFormatterLongStyle: | |
430 | case kCFDateFormatterFullStyle: break; | |
bd5b749c | 431 | default: |
cf7d2af9 A |
432 | CFAssert2(0, __kCFLogAssertion, "%s(): unknown time style %d", __PRETTY_FUNCTION__, timeStyle); |
433 | memory->_timeStyle = kCFDateFormatterMediumStyle; | |
434 | break; | |
bd5b749c | 435 | } |
cf7d2af9 A |
436 | |
437 | memory->_locale = locale ? CFLocaleCreateCopy(allocator, locale) : (CFLocaleRef)CFRetain(CFLocaleGetSystem()); | |
438 | memory->_property._TimeZone = CFTimeZoneCopyDefault(); | |
439 | __ResetUDateFormat(memory, false); | |
440 | if (!memory->_df) { | |
441 | CFRelease(memory); | |
bd5b749c A |
442 | return NULL; |
443 | } | |
cf7d2af9 A |
444 | return (CFDateFormatterRef)memory; |
445 | } | |
446 | ||
447 | extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale); | |
448 | ||
449 | static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter) { | |
450 | CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); | |
451 | ||
452 | CFIndex dateLen = -1; | |
453 | UChar dateBuffer[BUFFER_SIZE]; | |
454 | if (kCFDateFormatterNoStyle != formatter->_dateStyle) { | |
455 | CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUDateFormatStrings")) : NULL; | |
456 | if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { | |
457 | CFStringRef key; | |
458 | switch (formatter->_dateStyle) { | |
459 | case kCFDateFormatterShortStyle: key = CFSTR("1"); break; | |
460 | case kCFDateFormatterMediumStyle: key = CFSTR("2"); break; | |
461 | case kCFDateFormatterLongStyle: key = CFSTR("3"); break; | |
462 | case kCFDateFormatterFullStyle: key = CFSTR("4"); break; | |
463 | default: key = CFSTR("0"); break; | |
464 | } | |
465 | CFStringRef pref = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key); | |
466 | if (NULL != pref && CFGetTypeID(pref) == CFStringGetTypeID()) { | |
467 | dateLen = __CFMin(CFStringGetLength(pref), BUFFER_SIZE); | |
468 | CFStringGetCharacters(pref, CFRangeMake(0, dateLen), (UniChar *)dateBuffer); | |
469 | } | |
470 | } | |
bd5b749c | 471 | } |
cf7d2af9 A |
472 | if (-1 == dateLen) { |
473 | UErrorCode status = U_ZERO_ERROR; | |
474 | int32_t ret = udat_toPatternRelativeDate(formatter->_df, dateBuffer, BUFFER_SIZE, &status); | |
475 | if (!U_FAILURE(status)) { | |
476 | dateLen = ret; | |
477 | } | |
bd5b749c | 478 | } |
cf7d2af9 A |
479 | |
480 | CFIndex timeLen = -1; | |
481 | UChar timeBuffer[BUFFER_SIZE]; | |
482 | if (kCFDateFormatterNoStyle != formatter->_timeStyle) { | |
483 | CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUTimeFormatStrings")) : NULL; | |
484 | if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { | |
485 | CFStringRef key; | |
486 | switch (formatter->_timeStyle) { | |
487 | case kCFDateFormatterShortStyle: key = CFSTR("1"); break; | |
488 | case kCFDateFormatterMediumStyle: key = CFSTR("2"); break; | |
489 | case kCFDateFormatterLongStyle: key = CFSTR("3"); break; | |
490 | case kCFDateFormatterFullStyle: key = CFSTR("4"); break; | |
491 | default: key = CFSTR("0"); break; | |
492 | } | |
493 | CFStringRef pref = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key); | |
494 | if (NULL != pref && CFGetTypeID(pref) == CFStringGetTypeID()) { | |
495 | timeLen = __CFMin(CFStringGetLength(pref), BUFFER_SIZE); | |
496 | CFStringGetCharacters(pref, CFRangeMake(0, timeLen), (UniChar *)timeBuffer); | |
497 | } | |
498 | } | |
bd5b749c | 499 | } |
cf7d2af9 A |
500 | if (-1 == timeLen) { |
501 | UErrorCode status = U_ZERO_ERROR; | |
502 | int32_t ret = udat_toPatternRelativeTime(formatter->_df, timeBuffer, BUFFER_SIZE, &status); | |
503 | if (!U_FAILURE(status)) { | |
504 | timeLen = ret; | |
505 | } | |
bd5b749c | 506 | } |
bd5b749c | 507 | |
cf7d2af9 A |
508 | UErrorCode status = U_ZERO_ERROR; |
509 | udat_applyPatternRelative(formatter->_df, (0 <= dateLen) ? dateBuffer : NULL, (0 <= dateLen) ? dateLen : 0, (0 <= timeLen) ? timeBuffer : NULL, (0 <= timeLen) ? timeLen : 0, &status); | |
510 | } | |
bd5b749c A |
511 | |
512 | static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter, bool doTime) { | |
513 | CFIndex formatStyle = doTime ? formatter->_timeStyle : formatter->_dateStyle; | |
514 | CFStringRef prefName = doTime ? CFSTR("AppleICUTimeFormatStrings") : CFSTR("AppleICUDateFormatStrings"); | |
515 | if (kCFDateFormatterNoStyle != formatStyle) { | |
cf7d2af9 A |
516 | CFStringRef pref = NULL; |
517 | CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); | |
518 | CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, prefName) : NULL; | |
519 | if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { | |
520 | CFStringRef key; | |
521 | switch (formatStyle) { | |
522 | case kCFDateFormatterShortStyle: key = CFSTR("1"); break; | |
523 | case kCFDateFormatterMediumStyle: key = CFSTR("2"); break; | |
524 | case kCFDateFormatterLongStyle: key = CFSTR("3"); break; | |
525 | case kCFDateFormatterFullStyle: key = CFSTR("4"); break; | |
526 | default: key = CFSTR("0"); break; | |
527 | } | |
528 | pref = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key); | |
529 | } | |
530 | if (NULL != pref && CFGetTypeID(pref) == CFStringGetTypeID()) { | |
531 | int32_t icustyle = UDAT_NONE; | |
532 | switch (formatStyle) { | |
533 | case kCFDateFormatterShortStyle: icustyle = UDAT_SHORT; break; | |
534 | case kCFDateFormatterMediumStyle: icustyle = UDAT_MEDIUM; break; | |
535 | case kCFDateFormatterLongStyle: icustyle = UDAT_LONG; break; | |
536 | case kCFDateFormatterFullStyle: icustyle = UDAT_FULL; break; | |
537 | } | |
538 | CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); | |
539 | char buffer[BUFFER_SIZE]; | |
540 | const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); | |
541 | if (NULL == cstr) { | |
542 | if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; | |
543 | } | |
544 | UErrorCode status = U_ZERO_ERROR; | |
545 | UDateFormat *df = udat_open((UDateFormatStyle)(doTime ? icustyle : UDAT_NONE), (UDateFormatStyle)(doTime ? UDAT_NONE : icustyle), cstr, NULL, 0, NULL, 0, &status); | |
546 | if (NULL != df) { | |
547 | UChar ubuffer[BUFFER_SIZE]; | |
548 | status = U_ZERO_ERROR; | |
549 | int32_t date_len = udat_toPattern(df, false, ubuffer, BUFFER_SIZE, &status); | |
550 | if (U_SUCCESS(status) && date_len <= BUFFER_SIZE) { | |
551 | CFStringRef dateString = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)ubuffer, date_len); | |
552 | status = U_ZERO_ERROR; | |
553 | int32_t formatter_len = udat_toPattern(formatter->_df, false, ubuffer, BUFFER_SIZE, &status); | |
554 | if (U_SUCCESS(status) && formatter_len <= BUFFER_SIZE) { | |
555 | CFMutableStringRef formatString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); | |
556 | CFStringAppendCharacters(formatString, (UniChar *)ubuffer, formatter_len); | |
557 | // find dateString inside formatString, substitute the pref in that range | |
558 | CFRange result; | |
559 | if (CFStringFindWithOptions(formatString, dateString, CFRangeMake(0, formatter_len), 0, &result)) { | |
560 | CFStringReplace(formatString, result, pref); | |
561 | int32_t new_len = CFStringGetLength(formatString); | |
562 | STACK_BUFFER_DECL(UChar, new_buffer, new_len); | |
563 | const UChar *new_ustr = (UChar *)CFStringGetCharactersPtr(formatString); | |
564 | if (NULL == new_ustr) { | |
565 | CFStringGetCharacters(formatString, CFRangeMake(0, new_len), (UniChar *)new_buffer); | |
566 | new_ustr = new_buffer; | |
567 | } | |
568 | status = U_ZERO_ERROR; | |
569 | // udat_applyPattern(formatter->_df, false, new_ustr, new_len, &status); | |
570 | udat_applyPattern(formatter->_df, false, new_ustr, new_len); | |
571 | } | |
572 | CFRelease(formatString); | |
573 | } | |
574 | CFRelease(dateString); | |
575 | } | |
576 | udat_close(df); | |
577 | } | |
578 | } | |
bd5b749c A |
579 | } |
580 | } | |
581 | ||
582 | static void __CFDateFormatterApplySymbolPrefs(const void *key, const void *value, void *context) { | |
583 | if (CFGetTypeID(key) == CFStringGetTypeID() && CFGetTypeID(value) == CFArrayGetTypeID()) { | |
cf7d2af9 A |
584 | CFDateFormatterRef formatter = (CFDateFormatterRef)context; |
585 | UDateFormatSymbolType sym = (UDateFormatSymbolType)CFStringGetIntValue((CFStringRef)key); | |
586 | CFArrayRef array = (CFArrayRef)value; | |
587 | CFIndex idx, cnt = CFArrayGetCount(array); | |
588 | for (idx = 0; idx < cnt; idx++) { | |
589 | CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx); | |
590 | if (CFGetTypeID(item) != CFStringGetTypeID()) continue; | |
591 | CFIndex item_cnt = CFStringGetLength(item); | |
592 | STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); | |
593 | UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item); | |
594 | if (NULL == item_ustr) { | |
595 | item_cnt = __CFMin(BUFFER_SIZE, item_cnt); | |
596 | CFStringGetCharacters(item, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); | |
597 | item_ustr = item_buffer; | |
598 | } | |
599 | UErrorCode status = U_ZERO_ERROR; | |
600 | udat_setSymbols(formatter->_df, sym, idx, item_ustr, item_cnt, &status); | |
601 | } | |
bd5b749c A |
602 | } |
603 | } | |
604 | ||
8ca704e1 A |
605 | static CFStringRef __CFDateFormatterCreateForcedTemplate(CFLocaleRef locale, CFStringRef inString) { |
606 | if (!inString) return NULL; | |
607 | ||
608 | Boolean doForce24 = false, doForce12 = false; | |
609 | CFDictionaryRef prefs = __CFLocaleGetPrefs(locale); | |
610 | CFPropertyListRef pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce24HourTime")) : NULL; | |
611 | if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) { | |
612 | doForce24 = CFBooleanGetValue((CFBooleanRef)pref); | |
613 | } | |
614 | pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce12HourTime")) : NULL; | |
615 | if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) { | |
616 | doForce12 = CFBooleanGetValue((CFBooleanRef)pref); | |
617 | } | |
618 | if (doForce24) doForce12 = false; // if both are set, Force24 wins, period | |
619 | if (!doForce24 && !doForce12) return (CFStringRef)CFRetain(inString); | |
620 | ||
621 | CFMutableStringRef outString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); | |
622 | CFIndex cnt = CFStringGetLength(inString); | |
623 | CFIndex lastSecond = -1, lastMinute = -1, firstHour = -1; | |
624 | Boolean isInQuote = false, hasA = false, had12Hour = false, had24Hour = false; | |
625 | for (CFIndex idx = 0; idx < cnt; idx++) { | |
626 | Boolean emit = true; | |
627 | UniChar ch = CFStringGetCharacterAtIndex(inString, idx); | |
628 | switch (ch) { | |
629 | case '\'': isInQuote = !isInQuote; break; | |
630 | case 'j': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); if (doForce24) ch = 'H'; else ch = 'h';} break; | |
631 | case 'h': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had12Hour = true; if (doForce24) ch = 'H';} break; // switch 12-hour to 24-hour | |
632 | case 'K': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had12Hour = true; if (doForce24) ch = 'k';} break; // switch 12-hour to 24-hour | |
633 | case 'H': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had24Hour = true; if (doForce12) ch = 'h';} break; // switch 24-hour to 12-hour | |
634 | case 'k': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had24Hour = true; if (doForce12) ch = 'K';} break; // switch 24-hour to 12-hour | |
635 | case 'm': if (!isInQuote) lastMinute = CFStringGetLength(outString); break; | |
636 | case 's': if (!isInQuote) lastSecond = CFStringGetLength(outString); break; | |
637 | case 'a': if (!isInQuote) {hasA = true; if (doForce24) emit = false;} break; | |
638 | break; | |
639 | } | |
640 | if (emit) CFStringAppendCharacters(outString, &ch, 1); | |
641 | } | |
642 | ||
643 | return outString; | |
644 | } | |
645 | ||
cf7d2af9 A |
646 | static CFStringRef __CFDateFormatterCreateForcedString(CFDateFormatterRef formatter, CFStringRef inString) { |
647 | if (!inString) return NULL; | |
8ca704e1 | 648 | |
cf7d2af9 A |
649 | Boolean doForce24 = false, doForce12 = false; |
650 | CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); | |
651 | CFPropertyListRef pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce24HourTime")) : NULL; | |
652 | if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) { | |
653 | doForce24 = CFBooleanGetValue((CFBooleanRef)pref); | |
654 | } | |
655 | pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce12HourTime")) : NULL; | |
656 | if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) { | |
657 | doForce12 = CFBooleanGetValue((CFBooleanRef)pref); | |
658 | } | |
cf7d2af9 | 659 | if (doForce24) doForce12 = false; // if both are set, Force24 wins, period |
8ca704e1 A |
660 | if (!doForce24 && !doForce12) return (CFStringRef)CFRetain(inString); |
661 | ||
cf7d2af9 A |
662 | CFMutableStringRef outString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); |
663 | CFIndex cnt = CFStringGetLength(inString); | |
664 | CFIndex lastSecond = -1, lastMinute = -1, firstHour = -1; | |
665 | Boolean isInQuote = false, hasA = false, had12Hour = false, had24Hour = false; | |
666 | for (CFIndex idx = 0; idx < cnt; idx++) { | |
667 | Boolean emit = true; | |
668 | UniChar ch = CFStringGetCharacterAtIndex(inString, idx); | |
669 | switch (ch) { | |
670 | case '\'': isInQuote = !isInQuote; break; | |
671 | case 'h': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had12Hour = true; if (doForce24) ch = 'H';} break; // switch 12-hour to 24-hour | |
672 | case 'K': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had12Hour = true; if (doForce24) ch = 'k';} break; // switch 12-hour to 24-hour | |
673 | case 'H': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had24Hour = true; if (doForce12) ch = 'h';} break; // switch 24-hour to 12-hour | |
674 | case 'k': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had24Hour = true; if (doForce12) ch = 'K';} break; // switch 24-hour to 12-hour | |
675 | case 'm': if (!isInQuote) lastMinute = CFStringGetLength(outString); break; | |
676 | case 's': if (!isInQuote) lastSecond = CFStringGetLength(outString); break; | |
677 | case 'a': if (!isInQuote) hasA = true; | |
678 | if (!isInQuote && doForce24) { | |
679 | // skip 'a' and one optional trailing space | |
680 | emit = false; | |
681 | if (idx + 1 < cnt && ' ' == CFStringGetCharacterAtIndex(inString, idx + 1)) idx++; | |
682 | } | |
683 | break; | |
684 | case ' ': | |
685 | if (!isInQuote && doForce24) { | |
686 | // if next character is 'a' AND we have seen the hour designator, skip space and 'a' | |
687 | if (idx + 1 < cnt && 'a' == CFStringGetCharacterAtIndex(inString, idx + 1) && -1 != firstHour) { | |
688 | emit = false; | |
689 | idx++; | |
690 | } | |
691 | } | |
692 | break; | |
693 | } | |
694 | if (emit) CFStringAppendCharacters(outString, &ch, 1); | |
695 | } | |
696 | if (doForce12 && !hasA && had24Hour) { | |
697 | CFStringRef locName = CFLocaleGetIdentifier(formatter->_locale); | |
698 | if (-1 != firstHour && (CFStringHasPrefix(locName, CFSTR("ko")) || CFEqual(locName, CFSTR("zh_SG")))) { | |
699 | CFStringInsert(outString, firstHour, CFSTR("a ")); | |
700 | } else if (-1 != firstHour && (CFStringHasPrefix(locName, CFSTR("zh")) || CFStringHasPrefix(locName, CFSTR("ja")))) { | |
701 | CFStringInsert(outString, firstHour, CFSTR("a")); | |
702 | } else { | |
703 | CFIndex lastPos = (-1 != lastSecond) ? lastSecond : ((-1 != lastMinute) ? lastMinute : -1); | |
704 | if (-1 != lastPos) { | |
705 | cnt = CFStringGetLength(outString); | |
706 | lastPos++; | |
707 | UniChar ch = (lastPos < cnt) ? CFStringGetCharacterAtIndex(outString, lastPos) : 0; | |
708 | switch (ch) { | |
709 | case '\"': lastPos++; break; | |
710 | case '\'':; | |
711 | again:; | |
712 | do { | |
713 | lastPos++; | |
714 | ch = (lastPos < cnt) ? CFStringGetCharacterAtIndex(outString, lastPos) : 0; | |
715 | } while ('\'' != ch && '\0' != ch); | |
716 | if ('\'' == ch) lastPos++; | |
717 | ch = (lastPos < cnt) ? CFStringGetCharacterAtIndex(outString, lastPos) : 0; | |
718 | if ('\'' == ch) goto again; | |
719 | break; | |
720 | } | |
721 | CFStringInsert(outString, lastPos, CFSTR(" a")); | |
722 | } | |
723 | } | |
724 | } | |
725 | return outString; | |
726 | } | |
727 | ||
bd5b749c | 728 | static void __CFDateFormatterCustomize(CFDateFormatterRef formatter) { |
cf7d2af9 A |
729 | Boolean wantRelative = (NULL != formatter->_property._DoesRelativeDateFormatting && formatter->_property._DoesRelativeDateFormatting == kCFBooleanTrue); |
730 | Boolean hasFormat = (NULL != formatter->_property._HasCustomFormat && formatter->_property._HasCustomFormat == kCFBooleanTrue); | |
731 | if (wantRelative && !hasFormat) { | |
732 | __substituteFormatStringFromPrefsDFRelative(formatter); | |
733 | } else { | |
734 | __substituteFormatStringFromPrefsDF(formatter, false); | |
735 | __substituteFormatStringFromPrefsDF(formatter, true); | |
736 | } | |
bd5b749c A |
737 | CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); |
738 | CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUDateTimeSymbols")) : NULL; | |
739 | if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { | |
cf7d2af9 A |
740 | CFDictionaryApplyFunction((CFDictionaryRef)metapref, __CFDateFormatterApplySymbolPrefs, formatter); |
741 | } | |
742 | metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleFirstWeekday")) : NULL; | |
743 | CFStringRef calID = (CFStringRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendarIdentifierKey); | |
744 | if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { | |
745 | metapref = (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)metapref, calID); | |
746 | } | |
747 | if (NULL != metapref && CFGetTypeID(metapref) == CFNumberGetTypeID()) { | |
748 | CFIndex wkdy; | |
749 | if (CFNumberGetValue((CFNumberRef)metapref, kCFNumberCFIndexType, &wkdy)) { | |
750 | UCalendar *cal = (UCalendar *)udat_getCalendar(formatter->_df); | |
751 | if (cal) ucal_setAttribute(cal, UCAL_FIRST_DAY_OF_WEEK, wkdy); | |
752 | } | |
753 | } | |
754 | metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleMinDaysInFirstWeek")) : NULL; | |
755 | if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { | |
756 | metapref = (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)metapref, calID); | |
757 | } | |
758 | if (NULL != metapref && CFGetTypeID(metapref) == CFNumberGetTypeID()) { | |
759 | CFIndex mwd; | |
760 | if (CFNumberGetValue((CFNumberRef)metapref, kCFNumberCFIndexType, &mwd)) { | |
761 | UCalendar *cal = (UCalendar *)udat_getCalendar(formatter->_df); | |
762 | if (cal) ucal_setAttribute(cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, mwd); | |
763 | } | |
bd5b749c A |
764 | } |
765 | } | |
766 | ||
767 | CFLocaleRef CFDateFormatterGetLocale(CFDateFormatterRef formatter) { | |
768 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); | |
769 | return formatter->_locale; | |
770 | } | |
771 | ||
772 | CFDateFormatterStyle CFDateFormatterGetDateStyle(CFDateFormatterRef formatter) { | |
773 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); | |
774 | return formatter->_dateStyle; | |
775 | } | |
776 | ||
777 | CFDateFormatterStyle CFDateFormatterGetTimeStyle(CFDateFormatterRef formatter) { | |
778 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); | |
779 | return formatter->_timeStyle; | |
780 | } | |
781 | ||
782 | CFStringRef CFDateFormatterGetFormat(CFDateFormatterRef formatter) { | |
783 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); | |
784 | return formatter->_format; | |
785 | } | |
786 | ||
787 | void CFDateFormatterSetFormat(CFDateFormatterRef formatter, CFStringRef formatString) { | |
788 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); | |
789 | __CFGenericValidateType(formatString, CFStringGetTypeID()); | |
cf7d2af9 | 790 | formatString = __CFDateFormatterCreateForcedString(formatter, formatString); |
bd5b749c A |
791 | CFIndex cnt = CFStringGetLength(formatString); |
792 | CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__); | |
793 | if (formatter->_format != formatString && cnt <= 1024) { | |
8ca704e1 A |
794 | // When going from a situation where there is no custom format already, |
795 | // and the "relative date formatting" property is set, we need to reset | |
796 | // the whole UDateFormat. | |
797 | if (formatter->_property._HasCustomFormat != kCFBooleanTrue && formatter->_property._DoesRelativeDateFormatting == kCFBooleanTrue) { | |
798 | __ResetUDateFormat(formatter, true); | |
799 | } | |
cf7d2af9 A |
800 | STACK_BUFFER_DECL(UChar, ubuffer, cnt); |
801 | const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)formatString); | |
802 | if (NULL == ustr) { | |
803 | CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer); | |
804 | ustr = ubuffer; | |
805 | } | |
806 | UErrorCode status = U_ZERO_ERROR; | |
807 | // udat_applyPattern(formatter->_df, false, ustr, cnt, &status); | |
808 | udat_applyPattern(formatter->_df, false, ustr, cnt); | |
809 | if (U_SUCCESS(status)) { | |
810 | if (formatter->_format) CFRelease(formatter->_format); | |
811 | formatter->_format = (CFStringRef)CFStringCreateCopy(CFGetAllocator(formatter), formatString); | |
8ca704e1 | 812 | formatter->_property._HasCustomFormat = kCFBooleanTrue; |
cf7d2af9 | 813 | } |
bd5b749c | 814 | } |
cf7d2af9 | 815 | if (formatString) CFRelease(formatString); |
bd5b749c A |
816 | } |
817 | ||
818 | CFStringRef CFDateFormatterCreateStringWithDate(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFDateRef date) { | |
819 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
820 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
821 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); | |
822 | __CFGenericValidateType(date, CFDateGetTypeID()); | |
823 | return CFDateFormatterCreateStringWithAbsoluteTime(allocator, formatter, CFDateGetAbsoluteTime(date)); | |
824 | } | |
825 | ||
826 | CFStringRef CFDateFormatterCreateStringWithAbsoluteTime(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFAbsoluteTime at) { | |
827 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
828 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
829 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); | |
830 | UChar *ustr = NULL, ubuffer[BUFFER_SIZE]; | |
831 | UErrorCode status = U_ZERO_ERROR; | |
832 | CFIndex used, cnt = BUFFER_SIZE; | |
833 | UDate ud = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0 + 0.5; | |
834 | used = udat_format(formatter->_df, ud, ubuffer, cnt, NULL, &status); | |
835 | if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) { | |
cf7d2af9 A |
836 | cnt = used + 1; |
837 | ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0); | |
838 | status = U_ZERO_ERROR; | |
839 | used = udat_format(formatter->_df, ud, ustr, cnt, NULL, &status); | |
bd5b749c A |
840 | } |
841 | CFStringRef string = NULL; | |
842 | if (U_SUCCESS(status)) { | |
cf7d2af9 | 843 | string = CFStringCreateWithCharacters(allocator, (const UniChar *)(ustr ? ustr : ubuffer), used); |
bd5b749c A |
844 | } |
845 | if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr); | |
846 | return string; | |
847 | } | |
848 | ||
849 | CFDateRef CFDateFormatterCreateDateFromString(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep) { | |
850 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
851 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
852 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); | |
853 | __CFGenericValidateType(string, CFStringGetTypeID()); | |
854 | CFAbsoluteTime at; | |
855 | if (CFDateFormatterGetAbsoluteTimeFromString(formatter, string, rangep, &at)) { | |
cf7d2af9 | 856 | return CFDateCreate(allocator, at); |
bd5b749c A |
857 | } |
858 | return NULL; | |
859 | } | |
860 | ||
861 | Boolean CFDateFormatterGetAbsoluteTimeFromString(CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep, CFAbsoluteTime *atp) { | |
862 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); | |
863 | __CFGenericValidateType(string, CFStringGetTypeID()); | |
864 | CFRange range = {0, 0}; | |
865 | if (rangep) { | |
866 | range = *rangep; | |
867 | } else { | |
868 | range.length = CFStringGetLength(string); | |
869 | } | |
870 | if (1024 < range.length) range.length = 1024; | |
871 | const UChar *ustr = (UChar *)CFStringGetCharactersPtr(string); | |
872 | STACK_BUFFER_DECL(UChar, ubuffer, (NULL == ustr) ? range.length : 1); | |
873 | if (NULL == ustr) { | |
cf7d2af9 A |
874 | CFStringGetCharacters(string, range, (UniChar *)ubuffer); |
875 | ustr = ubuffer; | |
bd5b749c A |
876 | } else { |
877 | ustr += range.location; | |
878 | } | |
879 | UDate udate; | |
880 | int32_t dpos = 0; | |
881 | UErrorCode status = U_ZERO_ERROR; | |
cf7d2af9 A |
882 | if (formatter->_property._DefaultDate) { |
883 | CFAbsoluteTime at = CFDateGetAbsoluteTime(formatter->_property._DefaultDate); | |
884 | udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; | |
885 | UDateFormat *df2 = udat_clone(formatter->_df, &status); | |
886 | UCalendar *cal2 = (UCalendar *)udat_getCalendar(df2); | |
887 | ucal_setMillis(cal2, udate, &status); | |
bd5b749c | 888 | udat_parseCalendar(formatter->_df, cal2, ustr, range.length, &dpos, &status); |
cf7d2af9 A |
889 | udate = ucal_getMillis(cal2, &status); |
890 | udat_close(df2); | |
bd5b749c A |
891 | } else { |
892 | udate = udat_parse(formatter->_df, ustr, range.length, &dpos, &status); | |
893 | } | |
894 | if (rangep) rangep->length = dpos; | |
895 | if (U_FAILURE(status)) { | |
cf7d2af9 | 896 | return false; |
bd5b749c A |
897 | } |
898 | if (atp) { | |
cf7d2af9 | 899 | *atp = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; |
bd5b749c A |
900 | } |
901 | return true; | |
902 | } | |
903 | ||
cf7d2af9 A |
904 | static void __CFDateFormatterSetSymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base, CFTypeRef value) { |
905 | UErrorCode status = U_ZERO_ERROR; | |
906 | __CFGenericValidateType(value, CFArrayGetTypeID()); | |
907 | CFArrayRef array = (CFArrayRef)value; | |
908 | CFIndex idx, cnt = CFArrayGetCount(array); | |
909 | for (idx = 0; idx < cnt; idx++) { | |
910 | CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx); | |
911 | __CFGenericValidateType(item, CFStringGetTypeID()); | |
912 | CFIndex item_cnt = CFStringGetLength(item); | |
913 | STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); | |
914 | UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item); | |
915 | if (NULL == item_ustr) { | |
916 | item_cnt = __CFMin(BUFFER_SIZE, item_cnt); | |
917 | CFStringGetCharacters(item, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); | |
918 | item_ustr = item_buffer; | |
919 | } | |
920 | status = U_ZERO_ERROR; | |
921 | udat_setSymbols(icudf, (UDateFormatSymbolType)icucode, idx + index_base, item_ustr, item_cnt, &status); | |
922 | } | |
923 | } | |
924 | ||
925 | static CFArrayRef __CFDateFormatterGetSymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base) { | |
926 | UErrorCode status = U_ZERO_ERROR; | |
8ca704e1 A |
927 | CFIndex idx, cnt = udat_countSymbols(icudf, (UDateFormatSymbolType)icucode); |
928 | if (cnt <= index_base) return CFArrayCreate(kCFAllocatorSystemDefault, NULL, 0, &kCFTypeArrayCallBacks); | |
929 | cnt = cnt - index_base; | |
cf7d2af9 A |
930 | STACK_BUFFER_DECL(CFStringRef, strings, cnt); |
931 | for (idx = 0; idx < cnt; idx++) { | |
932 | UChar ubuffer[BUFFER_SIZE]; | |
933 | CFStringRef str = NULL; | |
934 | status = U_ZERO_ERROR; | |
935 | CFIndex ucnt = udat_getSymbols(icudf, (UDateFormatSymbolType)icucode, idx + index_base, ubuffer, BUFFER_SIZE, &status); | |
936 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
937 | str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ucnt); | |
bd5b749c | 938 | } |
cf7d2af9 A |
939 | strings[idx] = !str ? (CFStringRef)CFRetain(CFSTR("<error>")) : str; |
940 | } | |
941 | CFArrayRef array = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)strings, cnt, &kCFTypeArrayCallBacks); | |
942 | while (cnt--) { | |
943 | CFRelease(strings[cnt]); | |
944 | } | |
945 | return array; | |
946 | } | |
bd5b749c | 947 | |
cf7d2af9 A |
948 | #define SET_SYMBOLS_ARRAY(A, B, C) \ |
949 | if (!directToICU) { \ | |
950 | oldProperty = formatter->_property. C; \ | |
951 | formatter->_property. C = NULL; \ | |
952 | } \ | |
953 | __CFDateFormatterSetSymbolsArray(formatter->_df, A, B, value); \ | |
954 | if (!directToICU) { \ | |
955 | formatter->_property. C = __CFDateFormatterGetSymbolsArray(formatter->_df, A, B); \ | |
956 | } | |
957 | ||
958 | static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value, Boolean directToICU) { | |
bd5b749c A |
959 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
960 | __CFGenericValidateType(key, CFStringGetTypeID()); | |
cf7d2af9 | 961 | CFTypeRef oldProperty = NULL; |
bd5b749c A |
962 | UErrorCode status = U_ZERO_ERROR; |
963 | UChar ubuffer[BUFFER_SIZE]; | |
964 | ||
cf7d2af9 A |
965 | if (kCFDateFormatterIsLenientKey == key) { |
966 | if (!directToICU) { | |
967 | oldProperty = formatter->_property. _IsLenient; | |
968 | formatter->_property. _IsLenient = NULL; | |
969 | } | |
970 | __CFGenericValidateType(value, CFBooleanGetTypeID()); | |
971 | udat_setLenient(formatter->_df, (kCFBooleanTrue == value)); | |
972 | UCalendar *cal = (UCalendar *)udat_getCalendar(formatter->_df); | |
973 | if (cal) ucal_setAttribute(cal, UCAL_LENIENT, (kCFBooleanTrue == value)); | |
974 | if (!directToICU) { | |
975 | formatter->_property. _IsLenient = (CFBooleanRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterIsLenientKey); | |
976 | } | |
977 | } else if (kCFDateFormatterDoesRelativeDateFormattingKey == key) { | |
978 | if (!directToICU) { | |
979 | oldProperty = formatter->_property. _DoesRelativeDateFormatting; | |
980 | formatter->_property. _DoesRelativeDateFormatting = NULL; | |
981 | } | |
982 | __CFGenericValidateType(value, CFBooleanGetTypeID()); | |
983 | if (!directToICU) { | |
984 | if (kCFBooleanTrue != value) value = kCFBooleanFalse; | |
985 | formatter->_property. _DoesRelativeDateFormatting = value ? (CFBooleanRef)CFRetain(value) : NULL; | |
986 | __ResetUDateFormat(formatter, false); | |
987 | } | |
988 | } else if (kCFDateFormatterCalendarKey == key) { | |
989 | if (!directToICU) { | |
990 | oldProperty = formatter->_property. _Calendar; | |
991 | formatter->_property. _Calendar = NULL; | |
992 | } | |
993 | __CFGenericValidateType(value, CFCalendarGetTypeID()); | |
994 | CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); | |
995 | CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeName); | |
996 | CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components); | |
997 | CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifierKey, CFCalendarGetIdentifier((CFCalendarRef)value)); | |
998 | localeName = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents); | |
999 | CFRelease(mcomponents); | |
1000 | CFRelease(components); | |
1001 | CFLocaleRef newLocale = CFLocaleCreate(CFGetAllocator(formatter->_locale), localeName); | |
1002 | CFRelease(localeName); | |
1003 | CFRelease(formatter->_locale); | |
1004 | formatter->_locale = newLocale; | |
1005 | UCalendar *cal = __CFCalendarCreateUCalendar(NULL, CFLocaleGetIdentifier(formatter->_locale), formatter->_property._TimeZone); | |
1006 | if (cal) ucal_setAttribute(cal, UCAL_FIRST_DAY_OF_WEEK, CFCalendarGetFirstWeekday((CFCalendarRef)value)); | |
1007 | if (cal) ucal_setAttribute(cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, CFCalendarGetMinimumDaysInFirstWeek((CFCalendarRef)value)); | |
1008 | if (cal) udat_setCalendar(formatter->_df, cal); | |
1009 | if (cal) ucal_close(cal); | |
1010 | if (!directToICU) { | |
1011 | formatter->_property. _Calendar = (CFCalendarRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarKey); | |
1012 | } | |
1013 | } else if (kCFDateFormatterCalendarIdentifierKey == key) { | |
1014 | if (!directToICU) { | |
1015 | oldProperty = formatter->_property. _CalendarName; | |
1016 | formatter->_property. _CalendarName = NULL; | |
1017 | } | |
1018 | __CFGenericValidateType(value, CFStringGetTypeID()); | |
1019 | CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); | |
1020 | CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeName); | |
1021 | CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components); | |
1022 | CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifierKey, value); | |
1023 | localeName = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents); | |
1024 | CFRelease(mcomponents); | |
1025 | CFRelease(components); | |
1026 | CFLocaleRef newLocale = CFLocaleCreate(CFGetAllocator(formatter->_locale), localeName); | |
1027 | CFRelease(localeName); | |
1028 | CFRelease(formatter->_locale); | |
1029 | formatter->_locale = newLocale; | |
1030 | UCalendar *cal = __CFCalendarCreateUCalendar(NULL, CFLocaleGetIdentifier(formatter->_locale), formatter->_property._TimeZone); | |
1031 | if (cal) udat_setCalendar(formatter->_df, cal); | |
1032 | if (cal) ucal_close(cal); | |
1033 | if (!directToICU) { | |
1034 | formatter->_property. _CalendarName = (CFStringRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarIdentifierKey); | |
1035 | } | |
1036 | } else if (kCFDateFormatterTimeZoneKey == key) { | |
1037 | if (formatter->_property. _TimeZone != value) { | |
1038 | if (!directToICU) { | |
1039 | oldProperty = formatter->_property. _TimeZone; | |
1040 | formatter->_property. _TimeZone = NULL; | |
1041 | } | |
1042 | __CFGenericValidateType(value, CFTimeZoneGetTypeID()); | |
1043 | CFTimeZoneRef old = formatter->_property._TimeZone; | |
1044 | formatter->_property._TimeZone = value ? (CFTimeZoneRef)CFRetain(value) : CFTimeZoneCopyDefault(); | |
1045 | if (old) CFRelease(old); | |
1046 | CFStringRef tznam = CFTimeZoneGetName(formatter->_property._TimeZone); | |
1047 | UCalendar *cal = (UCalendar *)udat_getCalendar(formatter->_df); | |
1048 | CFIndex ucnt = CFStringGetLength(tznam); | |
1049 | if (BUFFER_SIZE < ucnt) ucnt = BUFFER_SIZE; | |
1050 | CFStringGetCharacters(tznam, CFRangeMake(0, ucnt), (UniChar *)ubuffer); | |
1051 | ucal_setTimeZone(cal, ubuffer, ucnt, &status); | |
1052 | if (!directToICU) { | |
1053 | old = formatter->_property._TimeZone; | |
1054 | formatter->_property. _TimeZone = (CFTimeZoneRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterTimeZoneKey); | |
1055 | if (old) CFRelease(old); | |
1056 | } | |
1057 | } | |
1058 | } else if (kCFDateFormatterDefaultFormatKey == key) { | |
1059 | // read-only attribute | |
1060 | } else if (kCFDateFormatterTwoDigitStartDateKey == key) { | |
1061 | if (!directToICU) { | |
1062 | oldProperty = formatter->_property. _TwoDigitStartDate; | |
1063 | formatter->_property. _TwoDigitStartDate = NULL; | |
1064 | } | |
bd5b749c | 1065 | __CFGenericValidateType(value, CFDateGetTypeID()); |
cf7d2af9 A |
1066 | CFAbsoluteTime at = CFDateGetAbsoluteTime((CFDateRef)value); |
1067 | UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; | |
1068 | udat_set2DigitYearStart(formatter->_df, udate, &status); | |
1069 | if (!directToICU) { | |
1070 | formatter->_property. _TwoDigitStartDate = (CFDateRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterTwoDigitStartDateKey); | |
1071 | } | |
1072 | } else if (kCFDateFormatterDefaultDateKey == key) { | |
1073 | if (!directToICU) { | |
1074 | oldProperty = formatter->_property. _DefaultDate; | |
1075 | formatter->_property. _DefaultDate = NULL; | |
1076 | } | |
bd5b749c | 1077 | __CFGenericValidateType(value, CFDateGetTypeID()); |
cf7d2af9 A |
1078 | if (!directToICU) { |
1079 | formatter->_property._DefaultDate = value ? (CFDateRef)CFRetain(value) : NULL; | |
1080 | } | |
1081 | } else if (kCFDateFormatterGregorianStartDateKey == key) { | |
1082 | if (!directToICU) { | |
1083 | oldProperty = formatter->_property. _GregorianStartDate; | |
1084 | formatter->_property. _GregorianStartDate = NULL; | |
1085 | } | |
1086 | __CFGenericValidateType(value, CFDateGetTypeID()); | |
1087 | CFAbsoluteTime at = CFDateGetAbsoluteTime((CFDateRef)value); | |
1088 | UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; | |
1089 | UCalendar *cal = (UCalendar *)udat_getCalendar(formatter->_df); | |
1090 | ucal_setGregorianChange(cal, udate, &status); | |
1091 | if (!directToICU) { | |
1092 | formatter->_property. _GregorianStartDate = (CFDateRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterGregorianStartDateKey); | |
1093 | } | |
1094 | } else if (kCFDateFormatterEraSymbolsKey == key) { | |
1095 | SET_SYMBOLS_ARRAY(UDAT_ERAS, 0, _EraSymbols) | |
1096 | } else if (kCFDateFormatterLongEraSymbolsKey == key) { | |
1097 | SET_SYMBOLS_ARRAY(UDAT_ERA_NAMES, 0, _LongEraSymbols) | |
1098 | } else if (kCFDateFormatterMonthSymbolsKey == key) { | |
1099 | SET_SYMBOLS_ARRAY(UDAT_MONTHS, 0, _MonthSymbols) | |
1100 | } else if (kCFDateFormatterShortMonthSymbolsKey == key) { | |
1101 | SET_SYMBOLS_ARRAY(UDAT_SHORT_MONTHS, 0, _ShortMonthSymbols) | |
1102 | } else if (kCFDateFormatterVeryShortMonthSymbolsKey == key) { | |
1103 | SET_SYMBOLS_ARRAY(UDAT_NARROW_MONTHS, 0, _VeryShortMonthSymbols) | |
1104 | } else if (kCFDateFormatterStandaloneMonthSymbolsKey == key) { | |
1105 | SET_SYMBOLS_ARRAY(UDAT_STANDALONE_MONTHS, 0, _StandaloneMonthSymbols) | |
1106 | } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey == key) { | |
1107 | SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_MONTHS, 0, _ShortStandaloneMonthSymbols) | |
1108 | } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey == key) { | |
1109 | SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_MONTHS, 0, _VeryShortStandaloneMonthSymbols) | |
1110 | } else if (kCFDateFormatterWeekdaySymbolsKey == key) { | |
1111 | SET_SYMBOLS_ARRAY(UDAT_WEEKDAYS, 1, _WeekdaySymbols) | |
1112 | } else if (kCFDateFormatterShortWeekdaySymbolsKey == key) { | |
1113 | SET_SYMBOLS_ARRAY(UDAT_SHORT_WEEKDAYS, 1, _ShortWeekdaySymbols) | |
1114 | } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey == key) { | |
1115 | SET_SYMBOLS_ARRAY(UDAT_NARROW_WEEKDAYS, 1, _VeryShortWeekdaySymbols) | |
1116 | } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey == key) { | |
1117 | SET_SYMBOLS_ARRAY(UDAT_STANDALONE_WEEKDAYS, 1, _StandaloneWeekdaySymbols) | |
1118 | } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey == key) { | |
1119 | SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_WEEKDAYS, 1, _ShortStandaloneWeekdaySymbols) | |
1120 | } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey == key) { | |
1121 | SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_WEEKDAYS, 1, _VeryShortStandaloneWeekdaySymbols) | |
1122 | } else if (kCFDateFormatterQuarterSymbolsKey == key) { | |
1123 | SET_SYMBOLS_ARRAY(UDAT_QUARTERS, 0, _QuarterSymbols) | |
1124 | } else if (kCFDateFormatterShortQuarterSymbolsKey == key) { | |
1125 | SET_SYMBOLS_ARRAY(UDAT_SHORT_QUARTERS, 0, _ShortQuarterSymbols) | |
1126 | } else if (kCFDateFormatterStandaloneQuarterSymbolsKey == key) { | |
1127 | SET_SYMBOLS_ARRAY(UDAT_STANDALONE_QUARTERS, 0, _StandaloneQuarterSymbols) | |
1128 | } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey == key) { | |
1129 | SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_QUARTERS, 0, _ShortStandaloneQuarterSymbols) | |
1130 | } else if (kCFDateFormatterAMSymbolKey == key) { | |
1131 | if (!directToICU) { | |
1132 | oldProperty = formatter->_property. _AMSymbol; | |
1133 | formatter->_property. _AMSymbol = NULL; | |
1134 | } | |
bd5b749c | 1135 | __CFGenericValidateType(value, CFStringGetTypeID()); |
cf7d2af9 A |
1136 | CFIndex item_cnt = CFStringGetLength((CFStringRef)value); |
1137 | STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); | |
1138 | UChar *item_ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)value); | |
1139 | if (NULL == item_ustr) { | |
1140 | item_cnt = __CFMin(BUFFER_SIZE, item_cnt); | |
1141 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); | |
1142 | item_ustr = item_buffer; | |
1143 | } | |
1144 | udat_setSymbols(formatter->_df, UDAT_AM_PMS, 0, item_ustr, item_cnt, &status); | |
1145 | if (!directToICU) { | |
1146 | formatter->_property. _AMSymbol = (CFStringRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterAMSymbolKey); | |
1147 | } | |
1148 | } else if (kCFDateFormatterPMSymbolKey == key) { | |
1149 | if (!directToICU) { | |
1150 | oldProperty = formatter->_property. _PMSymbol; | |
1151 | formatter->_property. _PMSymbol = NULL; | |
bd5b749c | 1152 | } |
bd5b749c | 1153 | __CFGenericValidateType(value, CFStringGetTypeID()); |
cf7d2af9 A |
1154 | CFIndex item_cnt = CFStringGetLength((CFStringRef)value); |
1155 | STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); | |
1156 | UChar *item_ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)value); | |
1157 | if (NULL == item_ustr) { | |
1158 | item_cnt = __CFMin(BUFFER_SIZE, item_cnt); | |
1159 | CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); | |
1160 | item_ustr = item_buffer; | |
1161 | } | |
1162 | udat_setSymbols(formatter->_df, UDAT_AM_PMS, 1, item_ustr, item_cnt, &status); | |
1163 | if (!directToICU) { | |
1164 | formatter->_property. _PMSymbol = (CFStringRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterPMSymbolKey); | |
bd5b749c | 1165 | } |
bd5b749c | 1166 | } else { |
cf7d2af9 | 1167 | CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); |
bd5b749c | 1168 | } |
cf7d2af9 | 1169 | if (oldProperty) CFRelease(oldProperty); |
bd5b749c A |
1170 | } |
1171 | ||
cf7d2af9 A |
1172 | void CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value) { |
1173 | __CFDateFormatterSetProperty(formatter, key, value, false); | |
1174 | } | |
bd5b749c A |
1175 | |
1176 | CFTypeRef CFDateFormatterCopyProperty(CFDateFormatterRef formatter, CFStringRef key) { | |
1177 | __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); | |
1178 | __CFGenericValidateType(key, CFStringGetTypeID()); | |
1179 | UErrorCode status = U_ZERO_ERROR; | |
1180 | UChar ubuffer[BUFFER_SIZE]; | |
1181 | ||
cf7d2af9 A |
1182 | if (kCFDateFormatterIsLenientKey == key) { |
1183 | if (formatter->_property._IsLenient) return CFRetain(formatter->_property._IsLenient); | |
1184 | return CFRetain(udat_isLenient(formatter->_df) ? kCFBooleanTrue : kCFBooleanFalse); | |
1185 | } else if (kCFDateFormatterDoesRelativeDateFormattingKey == key) { | |
1186 | if (formatter->_property._DoesRelativeDateFormatting) return CFRetain(formatter->_property._DoesRelativeDateFormatting); | |
1187 | return CFRetain(kCFBooleanFalse); | |
1188 | } else if (kCFDateFormatterCalendarKey == key) { | |
1189 | if (formatter->_property._Calendar) return CFRetain(formatter->_property._Calendar); | |
1190 | CFCalendarRef calendar = (CFCalendarRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendarKey); | |
1191 | return calendar ? CFRetain(calendar) : NULL; | |
1192 | } else if (kCFDateFormatterCalendarIdentifierKey == key) { | |
1193 | if (formatter->_property._CalendarName) return CFRetain(formatter->_property._CalendarName); | |
1194 | CFStringRef ident = (CFStringRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendarIdentifierKey); | |
1195 | return ident ? CFRetain(ident) : NULL; | |
1196 | } else if (kCFDateFormatterTimeZoneKey == key) { | |
1197 | if (formatter->_property._TwoDigitStartDate) return CFRetain(formatter->_property._TwoDigitStartDate); | |
1198 | return CFRetain(formatter->_property._TimeZone); | |
1199 | } else if (kCFDateFormatterDefaultFormatKey == key) { | |
1200 | return formatter->_defformat ? CFRetain(formatter->_defformat) : NULL; | |
1201 | } else if (kCFDateFormatterTwoDigitStartDateKey == key) { | |
1202 | if (formatter->_property._TwoDigitStartDate) return CFRetain(formatter->_property._TwoDigitStartDate); | |
1203 | UDate udate = udat_get2DigitYearStart(formatter->_df, &status); | |
bd5b749c | 1204 | if (U_SUCCESS(status)) { |
cf7d2af9 | 1205 | CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; |
bd5b749c A |
1206 | return CFDateCreate(CFGetAllocator(formatter), at); |
1207 | } | |
cf7d2af9 A |
1208 | } else if (kCFDateFormatterDefaultDateKey == key) { |
1209 | return formatter->_property._DefaultDate ? CFRetain(formatter->_property._DefaultDate) : NULL; | |
1210 | } else if (kCFDateFormatterGregorianStartDateKey == key) { | |
1211 | if (formatter->_property._GregorianStartDate) return CFRetain(formatter->_property._GregorianStartDate); | |
1212 | UCalendar *cal = (UCalendar *)udat_getCalendar(formatter->_df); | |
1213 | UDate udate = ucal_getGregorianChange(cal, &status); | |
bd5b749c | 1214 | if (U_SUCCESS(status)) { |
cf7d2af9 | 1215 | CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; |
bd5b749c A |
1216 | return CFDateCreate(CFGetAllocator(formatter), at); |
1217 | } | |
cf7d2af9 A |
1218 | } else if (kCFDateFormatterEraSymbolsKey == key) { |
1219 | if (formatter->_property._EraSymbols) return CFRetain(formatter->_property._EraSymbols); | |
1220 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_ERAS, 0); | |
1221 | } else if (kCFDateFormatterLongEraSymbolsKey == key) { | |
1222 | if (formatter->_property._LongEraSymbols) return CFRetain(formatter->_property._LongEraSymbols); | |
1223 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_ERA_NAMES, 0); | |
1224 | } else if (kCFDateFormatterMonthSymbolsKey == key) { | |
1225 | if (formatter->_property._MonthSymbols) return CFRetain(formatter->_property._MonthSymbols); | |
1226 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_MONTHS, 0); | |
1227 | } else if (kCFDateFormatterShortMonthSymbolsKey == key) { | |
1228 | if (formatter->_property._ShortMonthSymbols) return CFRetain(formatter->_property._ShortMonthSymbols); | |
1229 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_SHORT_MONTHS, 0); | |
1230 | } else if (kCFDateFormatterVeryShortMonthSymbolsKey == key) { | |
1231 | if (formatter->_property._VeryShortMonthSymbols) return CFRetain(formatter->_property._VeryShortMonthSymbols); | |
1232 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_NARROW_MONTHS, 0); | |
1233 | } else if (kCFDateFormatterStandaloneMonthSymbolsKey == key) { | |
1234 | if (formatter->_property._StandaloneMonthSymbols) return CFRetain(formatter->_property._StandaloneMonthSymbols); | |
1235 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_MONTHS, 0); | |
1236 | } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey == key) { | |
1237 | if (formatter->_property._ShortStandaloneMonthSymbols) return CFRetain(formatter->_property._ShortStandaloneMonthSymbols); | |
1238 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_MONTHS, 0); | |
1239 | } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey == key) { | |
1240 | if (formatter->_property._VeryShortStandaloneMonthSymbols) return CFRetain(formatter->_property._VeryShortStandaloneMonthSymbols); | |
1241 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_NARROW_MONTHS, 0); | |
1242 | } else if (kCFDateFormatterWeekdaySymbolsKey == key) { | |
1243 | if (formatter->_property._WeekdaySymbols) return CFRetain(formatter->_property._WeekdaySymbols); | |
1244 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_WEEKDAYS, 1); | |
1245 | } else if (kCFDateFormatterShortWeekdaySymbolsKey == key) { | |
1246 | if (formatter->_property._ShortWeekdaySymbols) return CFRetain(formatter->_property._ShortWeekdaySymbols); | |
1247 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_SHORT_WEEKDAYS, 1); | |
1248 | } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey == key) { | |
1249 | if (formatter->_property._VeryShortWeekdaySymbols) return CFRetain(formatter->_property._VeryShortWeekdaySymbols); | |
1250 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_NARROW_WEEKDAYS, 1); | |
1251 | } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey == key) { | |
1252 | if (formatter->_property._StandaloneWeekdaySymbols) return CFRetain(formatter->_property._StandaloneWeekdaySymbols); | |
1253 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_WEEKDAYS, 1); | |
1254 | } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey == key) { | |
1255 | if (formatter->_property._ShortStandaloneWeekdaySymbols) return CFRetain(formatter->_property._ShortStandaloneWeekdaySymbols); | |
1256 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_WEEKDAYS, 1); | |
1257 | } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey == key) { | |
1258 | if (formatter->_property._VeryShortStandaloneWeekdaySymbols) return CFRetain(formatter->_property._VeryShortStandaloneWeekdaySymbols); | |
1259 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_NARROW_WEEKDAYS, 1); | |
1260 | } else if (kCFDateFormatterQuarterSymbolsKey == key) { | |
1261 | if (formatter->_property._QuarterSymbols) return CFRetain(formatter->_property._QuarterSymbols); | |
1262 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_QUARTERS, 0); | |
1263 | } else if (kCFDateFormatterShortQuarterSymbolsKey == key) { | |
1264 | if (formatter->_property._ShortQuarterSymbols) return CFRetain(formatter->_property._ShortQuarterSymbols); | |
1265 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_SHORT_QUARTERS, 0); | |
1266 | } else if (kCFDateFormatterStandaloneQuarterSymbolsKey == key) { | |
1267 | if (formatter->_property._StandaloneQuarterSymbols) return CFRetain(formatter->_property._StandaloneQuarterSymbols); | |
1268 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_QUARTERS, 0); | |
1269 | } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey == key) { | |
1270 | if (formatter->_property._ShortStandaloneQuarterSymbols) return CFRetain(formatter->_property._ShortStandaloneQuarterSymbols); | |
1271 | return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_QUARTERS, 0); | |
1272 | } else if (kCFDateFormatterAMSymbolKey == key) { | |
1273 | if (formatter->_property._AMSymbol) return CFRetain(formatter->_property._AMSymbol); | |
1274 | CFIndex cnt = udat_countSymbols(formatter->_df, UDAT_AM_PMS); | |
1275 | if (2 <= cnt) { | |
1276 | CFIndex ucnt = udat_getSymbols(formatter->_df, UDAT_AM_PMS, 0, ubuffer, BUFFER_SIZE, &status); | |
1277 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1278 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (UniChar *)ubuffer, ucnt); | |
1279 | } | |
1280 | } | |
1281 | } else if (kCFDateFormatterPMSymbolKey == key) { | |
1282 | if (formatter->_property._PMSymbol) return CFRetain(formatter->_property._PMSymbol); | |
1283 | CFIndex cnt = udat_countSymbols(formatter->_df, UDAT_AM_PMS); | |
1284 | if (2 <= cnt) { | |
1285 | CFIndex ucnt = udat_getSymbols(formatter->_df, UDAT_AM_PMS, 1, ubuffer, BUFFER_SIZE, &status); | |
1286 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1287 | return CFStringCreateWithCharacters(CFGetAllocator(formatter), (UniChar *)ubuffer, ucnt); | |
1288 | } | |
1289 | } | |
bd5b749c | 1290 | } else { |
cf7d2af9 | 1291 | CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); |
bd5b749c A |
1292 | } |
1293 | return NULL; | |
1294 | } | |
1295 | ||
bd5b749c | 1296 |