]> git.saurik.com Git - apple/cf.git/blob - CFCalendar.c
CF-550.tar.gz
[apple/cf.git] / CFCalendar.c
1 /*
2 * Copyright (c) 2009 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 /* CFCalendar.c
24 Copyright 2004-2004, Apple Computer, Inc. All rights reserved.
25 Responsibility: Christopher Kane
26 */
27
28
29
30 #include <CoreFoundation/CFCalendar.h>
31 #include <CoreFoundation/CFRuntime.h>
32 #include "CFInternal.h"
33 #include "CFPriv.h"
34 #include <unicode/ucal.h>
35
36 #define BUFFER_SIZE 512
37
38 struct __CFCalendar {
39 CFRuntimeBase _base;
40 CFStringRef _identifier; // canonical identifier, never NULL
41 CFLocaleRef _locale;
42 CFStringRef _localeID;
43 CFTimeZoneRef _tz;
44 UCalendar *_cal;
45 };
46
47 static Boolean __CFCalendarEqual(CFTypeRef cf1, CFTypeRef cf2) {
48 CFCalendarRef calendar1 = (CFCalendarRef)cf1;
49 CFCalendarRef calendar2 = (CFCalendarRef)cf2;
50 return CFEqual(calendar1->_identifier, calendar2->_identifier);
51 }
52
53 static CFHashCode __CFCalendarHash(CFTypeRef cf) {
54 CFCalendarRef calendar = (CFCalendarRef)cf;
55 return CFHash(calendar->_identifier);
56 }
57
58 static CFStringRef __CFCalendarCopyDescription(CFTypeRef cf) {
59 CFCalendarRef calendar = (CFCalendarRef)cf;
60 return CFStringCreateWithFormat(CFGetAllocator(calendar), NULL, CFSTR("<CFCalendar %p [%p]>{identifier = '%@'}"), cf, CFGetAllocator(calendar), calendar->_identifier);
61 }
62
63 static void __CFCalendarDeallocate(CFTypeRef cf) {
64 CFCalendarRef calendar = (CFCalendarRef)cf;
65 CFRelease(calendar->_identifier);
66 if (calendar->_locale) CFRelease(calendar->_locale);
67 if (calendar->_localeID) CFRelease(calendar->_localeID);
68 CFRelease(calendar->_tz);
69 if (calendar->_cal) ucal_close(calendar->_cal);
70 }
71
72 static CFTypeID __kCFCalendarTypeID = _kCFRuntimeNotATypeID;
73
74 static const CFRuntimeClass __CFCalendarClass = {
75 0,
76 "CFCalendar",
77 NULL, // init
78 NULL, // copy
79 __CFCalendarDeallocate,
80 __CFCalendarEqual,
81 __CFCalendarHash,
82 NULL, //
83 __CFCalendarCopyDescription
84 };
85
86 static void __CFCalendarInitialize(void) {
87 __kCFCalendarTypeID = _CFRuntimeRegisterClass(&__CFCalendarClass);
88 }
89
90 CFTypeID CFCalendarGetTypeID(void) {
91 if (_kCFRuntimeNotATypeID == __kCFCalendarTypeID) __CFCalendarInitialize();
92 return __kCFCalendarTypeID;
93 }
94
95 __private_extern__ UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz) {
96 if (calendarID) {
97 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeID);
98 CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components);
99 CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifier, calendarID);
100 localeID = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents);
101 CFRelease(mcomponents);
102 CFRelease(components);
103 }
104
105 char buffer[BUFFER_SIZE];
106 const char *cstr = CFStringGetCStringPtr(localeID, kCFStringEncodingASCII);
107 if (NULL == cstr) {
108 if (CFStringGetCString(localeID, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
109 }
110 if (NULL == cstr) {
111 if (calendarID) CFRelease(localeID);
112 return NULL;
113 }
114
115 UChar ubuffer[BUFFER_SIZE];
116 CFStringRef tznam = CFTimeZoneGetName(tz);
117 CFIndex cnt = CFStringGetLength(tznam);
118 if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
119 CFStringGetCharacters(tznam, CFRangeMake(0, cnt), (UniChar *)ubuffer);
120
121 UErrorCode status = U_ZERO_ERROR;
122 UCalendar *cal = ucal_open(ubuffer, cnt, cstr, UCAL_TRADITIONAL, &status);
123 if (calendarID) CFRelease(localeID);
124 return cal;
125 }
126
127 static void __CFCalendarSetupCal(CFCalendarRef calendar) {
128 calendar->_cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz);
129 }
130
131 static void __CFCalendarZapCal(CFCalendarRef calendar) {
132 ucal_close(calendar->_cal);
133 calendar->_cal = NULL;
134 }
135
136 CFCalendarRef CFCalendarCopyCurrent(void) {
137 CFLocaleRef locale = CFLocaleCopyCurrent();
138 CFCalendarRef calID = (CFCalendarRef)CFLocaleGetValue(locale, kCFLocaleCalendarIdentifier);
139 if (calID) {
140 CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, (CFStringRef)calID);
141 CFCalendarSetLocale(calendar, locale);
142 CFRelease(locale);
143 return calendar;
144 }
145 return NULL;
146 }
147
148 CFCalendarRef CFCalendarCreateWithIdentifier(CFAllocatorRef allocator, CFStringRef identifier) {
149 if (allocator == NULL) allocator = __CFGetDefaultAllocator();
150 __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
151 __CFGenericValidateType(identifier, CFStringGetTypeID());
152 // return NULL until Chinese calendar is available
153 if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar) {
154 // if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) {
155 if (CFEqual(kCFGregorianCalendar, identifier)) identifier = kCFGregorianCalendar;
156 else if (CFEqual(kCFBuddhistCalendar, identifier)) identifier = kCFBuddhistCalendar;
157 else if (CFEqual(kCFJapaneseCalendar, identifier)) identifier = kCFJapaneseCalendar;
158 else if (CFEqual(kCFIslamicCalendar, identifier)) identifier = kCFIslamicCalendar;
159 else if (CFEqual(kCFIslamicCivilCalendar, identifier)) identifier = kCFIslamicCivilCalendar;
160 else if (CFEqual(kCFHebrewCalendar, identifier)) identifier = kCFHebrewCalendar;
161 // else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar;
162 else return NULL;
163 }
164 struct __CFCalendar *calendar = NULL;
165 uint32_t size = sizeof(struct __CFCalendar) - sizeof(CFRuntimeBase);
166 calendar = (struct __CFCalendar *)_CFRuntimeCreateInstance(allocator, CFCalendarGetTypeID(), size, NULL);
167 if (NULL == calendar) {
168 return NULL;
169 }
170 calendar->_identifier = (CFStringRef)CFRetain(identifier);
171 calendar->_locale = NULL;
172 calendar->_localeID = CFLocaleGetIdentifier(CFLocaleGetSystem());
173 calendar->_tz = CFTimeZoneCopyDefault();
174 calendar->_cal = NULL;
175 return (CFCalendarRef)calendar;
176 }
177
178 CFStringRef CFCalendarGetIdentifier(CFCalendarRef calendar) {
179 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFStringRef, calendar, "calendarIdentifier");
180 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
181 return calendar->_identifier;
182 }
183
184 CFLocaleRef CFCalendarCopyLocale(CFCalendarRef calendar) {
185 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFLocaleRef, calendar, "_copyLocale");
186 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
187 return (CFLocaleRef)CFLocaleCreate(kCFAllocatorSystemDefault, calendar->_localeID);
188 }
189
190 void CFCalendarSetLocale(CFCalendarRef calendar, CFLocaleRef locale) {
191 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setLocale:", locale);
192 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
193 __CFGenericValidateType(locale, CFLocaleGetTypeID());
194 CFStringRef localeID = CFLocaleGetIdentifier(locale);
195 if (localeID != calendar->_localeID) {
196 CFRelease(calendar->_localeID);
197 CFRetain(localeID);
198 calendar->_localeID = localeID;
199 if (calendar->_cal) __CFCalendarZapCal(calendar);
200 }
201 }
202
203 CFTimeZoneRef CFCalendarCopyTimeZone(CFCalendarRef calendar) {
204 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFTimeZoneRef, calendar, "_copyTimeZone");
205 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
206 return (CFTimeZoneRef)CFRetain(calendar->_tz);
207 }
208
209 void CFCalendarSetTimeZone(CFCalendarRef calendar, CFTimeZoneRef tz) {
210 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setTimeZone:", tz);
211 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
212 if (tz) __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
213 if (tz != calendar->_tz) {
214 CFRelease(calendar->_tz);
215 calendar->_tz = tz ? (CFTimeZoneRef)CFRetain(tz) : CFTimeZoneCopyDefault();
216 if (calendar->_cal) __CFCalendarZapCal(calendar);
217 }
218 }
219
220 CFIndex CFCalendarGetFirstWeekday(CFCalendarRef calendar) {
221 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex, calendar, "firstWeekday");
222 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
223 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
224 if (calendar->_cal) {
225 return ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK);
226 }
227 return -1;
228 }
229
230 void CFCalendarSetFirstWeekday(CFCalendarRef calendar, CFIndex wkdy) {
231 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setFirstWeekday:", wkdy);
232 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
233 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
234 if (calendar->_cal) {
235 ucal_setAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK, wkdy);
236 }
237 }
238
239 CFIndex CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar) {
240 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex, calendar, "minimumDaysInFirstWeek");
241 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
242 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
243 return calendar->_cal ? ucal_getAttribute(calendar->_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK) : -1;
244 }
245
246 void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar, CFIndex mwd) {
247 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setMinimumDaysInFirstWeek:", mwd);
248 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
249 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
250 if (calendar->_cal) ucal_setAttribute(calendar->_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, mwd);
251 }
252
253 CFDateRef CFCalendarCopyGregorianStartDate(CFCalendarRef calendar) {
254 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFDateRef, calendar, "_gregorianStartDate");
255 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
256 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
257 UErrorCode status = U_ZERO_ERROR;
258 UDate udate = calendar->_cal ? ucal_getGregorianChange(calendar->_cal, &status) : 0;
259 if (calendar->_cal && U_SUCCESS(status)) {
260 CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
261 return CFDateCreate(CFGetAllocator(calendar), at);
262 }
263 return NULL;
264 }
265
266 void CFCalendarSetGregorianStartDate(CFCalendarRef calendar, CFDateRef date) {
267 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "_setGregorianStartDate:", date);
268 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
269 if (date) __CFGenericValidateType(date, CFDateGetTypeID());
270 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
271 if (!calendar->_cal) return;
272 if (!date) {
273 UErrorCode status = U_ZERO_ERROR;
274 UCalendar *cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz);
275 UDate udate = cal ? ucal_getGregorianChange(cal, &status) : 0;
276 if (cal && U_SUCCESS(status)) {
277 status = U_ZERO_ERROR;
278 if (calendar->_cal) ucal_setGregorianChange(calendar->_cal, udate, &status);
279 }
280 if (cal) ucal_close(cal);
281 } else {
282 CFAbsoluteTime at = CFDateGetAbsoluteTime(date);
283 UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
284 UErrorCode status = U_ZERO_ERROR;
285 if (calendar->_cal) ucal_setGregorianChange(calendar->_cal, udate, &status);
286 }
287 }
288
289
290 static UCalendarDateFields __CFCalendarGetICUFieldCode(CFCalendarUnit unit) {
291 switch (unit) {
292 case kCFCalendarUnitEra: return UCAL_ERA;
293 case kCFCalendarUnitYear: return UCAL_YEAR;
294 case kCFCalendarUnitMonth: return UCAL_MONTH;
295 case kCFCalendarUnitDay: return UCAL_DAY_OF_MONTH;
296 case kCFCalendarUnitHour: return UCAL_HOUR_OF_DAY;
297 case kCFCalendarUnitMinute: return UCAL_MINUTE;
298 case kCFCalendarUnitSecond: return UCAL_SECOND;
299 case kCFCalendarUnitWeek: return UCAL_WEEK_OF_YEAR;
300 case kCFCalendarUnitWeekday: return UCAL_DAY_OF_WEEK;
301 case kCFCalendarUnitWeekdayOrdinal: return UCAL_DAY_OF_WEEK_IN_MONTH;
302 }
303 return (UCalendarDateFields)-1;
304 }
305
306 static UCalendarDateFields __CFCalendarGetICUFieldCodeFromChar(char ch) {
307 switch (ch) {
308 case 'G': return UCAL_ERA;
309 case 'y': return UCAL_YEAR;
310 case 'M': return UCAL_MONTH;
311 case 'd': return UCAL_DAY_OF_MONTH;
312 case 'h': return UCAL_HOUR;
313 case 'H': return UCAL_HOUR_OF_DAY;
314 case 'm': return UCAL_MINUTE;
315 case 's': return UCAL_SECOND;
316 case 'S': return UCAL_MILLISECOND;
317 case 'w': return UCAL_WEEK_OF_YEAR;
318 case 'W': return UCAL_WEEK_OF_MONTH;
319 case 'E': return UCAL_DAY_OF_WEEK;
320 case 'D': return UCAL_DAY_OF_YEAR;
321 case 'F': return UCAL_DAY_OF_WEEK_IN_MONTH;
322 case 'a': return UCAL_AM_PM;
323 case 'g': return UCAL_JULIAN_DAY;
324 }
325 return (UCalendarDateFields)-1;
326 }
327
328 static UCalendarDateFields __CFCalendarGetCalendarUnitFromChar(char ch) {
329 switch (ch) {
330 case 'G': return (UCalendarDateFields)kCFCalendarUnitEra;
331 case 'y': return (UCalendarDateFields)kCFCalendarUnitYear;
332 case 'M': return (UCalendarDateFields)kCFCalendarUnitMonth;
333 case 'd': return (UCalendarDateFields)kCFCalendarUnitDay;
334 case 'H': return (UCalendarDateFields)kCFCalendarUnitHour;
335 case 'm': return (UCalendarDateFields)kCFCalendarUnitMinute;
336 case 's': return (UCalendarDateFields)kCFCalendarUnitSecond;
337 case 'w': return (UCalendarDateFields)kCFCalendarUnitWeek;
338 case 'E': return (UCalendarDateFields)kCFCalendarUnitWeekday;
339 case 'F': return (UCalendarDateFields)kCFCalendarUnitWeekdayOrdinal;
340 }
341 return (UCalendarDateFields)-1;
342 }
343
344 CFRange CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) {
345 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange, calendar, "_minimumRangeOfUnit:", unit);
346 CFRange range = {kCFNotFound, kCFNotFound};
347 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
348 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
349 if (calendar->_cal) {
350 ucal_clear(calendar->_cal);
351 UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit);
352 UErrorCode status = U_ZERO_ERROR;
353 range.location = ucal_getLimit(calendar->_cal, field, UCAL_GREATEST_MINIMUM, &status);
354 range.length = ucal_getLimit(calendar->_cal, field, UCAL_LEAST_MAXIMUM, &status) - range.location + 1;
355 if (UCAL_MONTH == field) range.location++;
356 if (100000 < range.length) range.length = 100000;
357 }
358 return range;
359 }
360
361 CFRange CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) {
362 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange, calendar, "_maximumRangeOfUnit:", unit);
363 CFRange range = {kCFNotFound, kCFNotFound};
364 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
365 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
366 if (calendar->_cal) {
367 ucal_clear(calendar->_cal);
368 UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit);
369 UErrorCode status = U_ZERO_ERROR;
370 range.location = ucal_getLimit(calendar->_cal, field, UCAL_MINIMUM, &status);
371 range.length = ucal_getLimit(calendar->_cal, field, UCAL_MAXIMUM, &status) - range.location + 1;
372 if (UCAL_MONTH == field) range.location++;
373 if (100000 < range.length) range.length = 100000;
374 }
375 return range;
376 }
377
378 static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at) {
379 // Set UCalendar to first instant of unit prior to 'at'
380 UErrorCode status = U_ZERO_ERROR;
381 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
382 ucal_setMillis(calendar->_cal, udate, &status);
383 int target_era = INT_MIN;
384 switch (unit) { // largest to smallest, we set the fields to their minimum value
385 case kCFCalendarUnitWeek:
386 {
387 // reduce to first day of week, then reduce the rest of the day
388 int32_t goal = ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK);
389 int32_t dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status);
390 while (dow != goal) {
391 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, -1, &status);
392 dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status);
393 }
394 goto day;
395 }
396 case kCFCalendarUnitEra:
397 {
398 target_era = ucal_get(calendar->_cal, UCAL_ERA, &status);
399 ucal_set(calendar->_cal, UCAL_YEAR, ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_ACTUAL_MINIMUM, &status));
400 }
401 case kCFCalendarUnitYear:
402 ucal_set(calendar->_cal, UCAL_MONTH, ucal_getLimit(calendar->_cal, UCAL_MONTH, UCAL_ACTUAL_MINIMUM, &status));
403 case kCFCalendarUnitMonth:
404 ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, ucal_getLimit(calendar->_cal, UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MINIMUM, &status));
405 case kCFCalendarUnitWeekday:
406 case kCFCalendarUnitDay:
407 day:;
408 ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, ucal_getLimit(calendar->_cal, UCAL_HOUR_OF_DAY, UCAL_ACTUAL_MINIMUM, &status));
409 case kCFCalendarUnitHour:
410 ucal_set(calendar->_cal, UCAL_MINUTE, ucal_getLimit(calendar->_cal, UCAL_MINUTE, UCAL_ACTUAL_MINIMUM, &status));
411 case kCFCalendarUnitMinute:
412 ucal_set(calendar->_cal, UCAL_SECOND, ucal_getLimit(calendar->_cal, UCAL_SECOND, UCAL_ACTUAL_MINIMUM, &status));
413 case kCFCalendarUnitSecond:
414 ucal_set(calendar->_cal, UCAL_MILLISECOND, 0);
415 }
416 if (INT_MIN != target_era && ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) {
417 // In the Japanese calendar, and possibly others, eras don't necessarily
418 // start on the first day of a year, so the previous code may have backed
419 // up into the previous era, and we have to correct forward.
420 UDate bad_udate = ucal_getMillis(calendar->_cal, &status);
421 ucal_add(calendar->_cal, UCAL_MONTH, 1, &status);
422 while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) {
423 bad_udate = ucal_getMillis(calendar->_cal, &status);
424 ucal_add(calendar->_cal, UCAL_MONTH, 1, &status);
425 }
426 udate = ucal_getMillis(calendar->_cal, &status);
427 // target date is between bad_udate and udate
428 for (;;) {
429 UDate test_udate = (udate + bad_udate) / 2;
430 ucal_setMillis(calendar->_cal, test_udate, &status);
431 if (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) {
432 bad_udate = test_udate;
433 } else {
434 udate = test_udate;
435 }
436 if (fabs(udate - bad_udate) < 1000) break;
437 }
438 do {
439 bad_udate = floor((bad_udate + 1000) / 1000) * 1000;
440 ucal_setMillis(calendar->_cal, bad_udate, &status);
441 } while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era);
442 }
443 }
444
445 static Boolean __validUnits(CFCalendarUnit smaller, CFCalendarUnit bigger) {
446 switch (bigger) {
447 case kCFCalendarUnitEra:
448 if (kCFCalendarUnitEra == smaller) return false;
449 if (kCFCalendarUnitWeekday == smaller) return false;
450 if (kCFCalendarUnitMinute == smaller) return false; // this causes CFIndex overflow in range.length
451 if (kCFCalendarUnitSecond == smaller) return false; // this causes CFIndex overflow in range.length
452 return true;
453 case kCFCalendarUnitYear:
454 if (kCFCalendarUnitEra == smaller) return false;
455 if (kCFCalendarUnitYear == smaller) return false;
456 if (kCFCalendarUnitWeekday == smaller) return false;
457 return true;
458 case kCFCalendarUnitMonth:
459 if (kCFCalendarUnitEra == smaller) return false;
460 if (kCFCalendarUnitYear == smaller) return false;
461 if (kCFCalendarUnitMonth == smaller) return false;
462 if (kCFCalendarUnitWeekday == smaller) return false;
463 return true;
464 case kCFCalendarUnitDay:
465 if (kCFCalendarUnitHour == smaller) return true;
466 if (kCFCalendarUnitMinute == smaller) return true;
467 if (kCFCalendarUnitSecond == smaller) return true;
468 return false;
469 case kCFCalendarUnitHour:
470 if (kCFCalendarUnitMinute == smaller) return true;
471 if (kCFCalendarUnitSecond == smaller) return true;
472 return false;
473 case kCFCalendarUnitMinute:
474 if (kCFCalendarUnitSecond == smaller) return true;
475 return false;
476 case kCFCalendarUnitWeek:
477 if (kCFCalendarUnitWeekday == smaller) return true;
478 if (kCFCalendarUnitDay == smaller) return true;
479 if (kCFCalendarUnitHour == smaller) return true;
480 if (kCFCalendarUnitMinute == smaller) return true;
481 if (kCFCalendarUnitSecond == smaller) return true;
482 return false;
483 case kCFCalendarUnitSecond:
484 case kCFCalendarUnitWeekday:
485 case kCFCalendarUnitWeekdayOrdinal:
486 return false;
487 }
488 return false;
489 };
490
491 static CFRange __CFCalendarGetRangeOfUnit1(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
492 CFRange range = {kCFNotFound, kCFNotFound};
493 if (!__validUnits(smallerUnit, biggerUnit)) return range;
494 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange, calendar, "_rangeOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at);
495 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
496 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
497 if (calendar->_cal) {
498 int32_t dow = -1;
499 ucal_clear(calendar->_cal);
500 UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit);
501 UCalendarDateFields bigField = __CFCalendarGetICUFieldCode(biggerUnit);
502 if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) {
503 UErrorCode status = U_ZERO_ERROR;
504 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
505 ucal_setMillis(calendar->_cal, udate, &status);
506 dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status);
507 }
508 // Set calendar to first instant of big unit
509 __CFCalendarSetToFirstInstant(calendar, biggerUnit, at);
510 UErrorCode status = U_ZERO_ERROR;
511 UDate start = ucal_getMillis(calendar->_cal, &status);
512 if (kCFCalendarUnitWeek == biggerUnit) {
513 range.location = ucal_get(calendar->_cal, smallField, &status);
514 if (kCFCalendarUnitMonth == smallerUnit) range.location++;
515 } else {
516 range.location = (kCFCalendarUnitHour == smallerUnit || kCFCalendarUnitMinute == smallerUnit || kCFCalendarUnitSecond == smallerUnit) ? 0 : 1;
517 }
518 // Set calendar to first instant of next value of big unit
519 if (UCAL_ERA == bigField) {
520 // ICU refuses to do the addition, probably because we are
521 // at the limit of UCAL_ERA. Use alternate strategy.
522 CFIndex limit = ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_MAXIMUM, &status);
523 if (100000 < limit) limit = 100000;
524 ucal_add(calendar->_cal, UCAL_YEAR, limit, &status);
525 } else {
526 ucal_add(calendar->_cal, bigField, 1, &status);
527 }
528 if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitYear == biggerUnit) {
529 ucal_add(calendar->_cal, UCAL_SECOND, -1, &status);
530 range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status);
531 while (1 == range.length) {
532 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, -1, &status);
533 range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status);
534 }
535 range.location = 1;
536 return range;
537 } else if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitMonth == biggerUnit) {
538 ucal_add(calendar->_cal, UCAL_SECOND, -1, &status);
539 range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status);
540 range.location = 1;
541 return range;
542 }
543 UDate goal = ucal_getMillis(calendar->_cal, &status);
544 // Set calendar back to first instant of big unit
545 ucal_setMillis(calendar->_cal, start, &status);
546 if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) {
547 // roll day forward to first 'dow'
548 while (ucal_get(calendar->_cal, (kCFCalendarUnitMonth == biggerUnit) ? UCAL_WEEK_OF_MONTH : UCAL_WEEK_OF_YEAR, &status) != 1) {
549 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status);
550 }
551 while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) {
552 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status);
553 }
554 start = ucal_getMillis(calendar->_cal, &status);
555 goal -= 1000;
556 range.location = 1; // constant here works around ICU -- see 3948293
557 }
558 UDate curr = start;
559 range.length = (kCFCalendarUnitWeekdayOrdinal == smallerUnit) ? 1 : 0;
560 const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
561 int multiple = (1 << multiple_table[flsl(smallerUnit) - 1]);
562 Boolean divide = false, alwaysDivide = false;
563 while (curr < goal) {
564 ucal_add(calendar->_cal, smallField, multiple, &status);
565 UDate newcurr = ucal_getMillis(calendar->_cal, &status);
566 if (curr < newcurr && newcurr <= goal) {
567 range.length += multiple;
568 curr = newcurr;
569 } else {
570 // Either newcurr is going backwards, or not making
571 // progress, or has overshot the goal; reset date
572 // and try smaller multiples.
573 ucal_setMillis(calendar->_cal, curr, &status);
574 divide = true;
575 // once we start overshooting the goal, the add at
576 // smaller multiples will succeed at most once for
577 // each multiple, so we reduce it every time through
578 // the loop.
579 if (goal < newcurr) alwaysDivide = true;
580 }
581 if (divide) {
582 multiple = multiple / 2;
583 if (0 == multiple) break;
584 divide = alwaysDivide;
585 }
586 }
587 }
588 return range;
589 }
590
591 static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) __attribute__((noinline));
592 static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
593 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange, calendar, "_rangeOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at);
594 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
595 CFRange range = {kCFNotFound, kCFNotFound};
596 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
597 if (calendar->_cal) {
598 switch (smallerUnit) {
599 case kCFCalendarUnitSecond:
600 switch (biggerUnit) {
601 case kCFCalendarUnitMinute:
602 case kCFCalendarUnitHour:
603 case kCFCalendarUnitDay:
604 case kCFCalendarUnitWeekday:
605 case kCFCalendarUnitWeek:
606 case kCFCalendarUnitMonth:
607 case kCFCalendarUnitYear:
608 case kCFCalendarUnitEra:
609 // goto calculate;
610 range.location = 0;
611 range.length = 60;
612 break;
613 }
614 break;
615 case kCFCalendarUnitMinute:
616 switch (biggerUnit) {
617 case kCFCalendarUnitHour:
618 case kCFCalendarUnitDay:
619 case kCFCalendarUnitWeekday:
620 case kCFCalendarUnitWeek:
621 case kCFCalendarUnitMonth:
622 case kCFCalendarUnitYear:
623 case kCFCalendarUnitEra:
624 // goto calculate;
625 range.location = 0;
626 range.length = 60;
627 break;
628 }
629 break;
630 case kCFCalendarUnitHour:
631 switch (biggerUnit) {
632 case kCFCalendarUnitDay:
633 case kCFCalendarUnitWeekday:
634 case kCFCalendarUnitWeek:
635 case kCFCalendarUnitMonth:
636 case kCFCalendarUnitYear:
637 case kCFCalendarUnitEra:
638 // goto calculate;
639 range.location = 0;
640 range.length = 24;
641 break;
642 }
643 break;
644 case kCFCalendarUnitDay:
645 switch (biggerUnit) {
646 case kCFCalendarUnitWeek:
647 case kCFCalendarUnitMonth:
648 case kCFCalendarUnitYear:
649 case kCFCalendarUnitEra:
650 goto calculate;
651 break;
652 }
653 break;
654 case kCFCalendarUnitWeekday:
655 switch (biggerUnit) {
656 case kCFCalendarUnitWeek:
657 case kCFCalendarUnitMonth:
658 case kCFCalendarUnitYear:
659 case kCFCalendarUnitEra:
660 goto calculate;
661 break;
662 }
663 break;
664 case kCFCalendarUnitWeekdayOrdinal:
665 switch (biggerUnit) {
666 case kCFCalendarUnitMonth:
667 case kCFCalendarUnitYear:
668 case kCFCalendarUnitEra:
669 goto calculate;
670 break;
671 }
672 break;
673 case kCFCalendarUnitWeek:
674 switch (biggerUnit) {
675 case kCFCalendarUnitMonth:
676 case kCFCalendarUnitYear:
677 case kCFCalendarUnitEra:
678 goto calculate;
679 break;
680 }
681 break;
682 case kCFCalendarUnitMonth:
683 switch (biggerUnit) {
684 case kCFCalendarUnitYear:
685 case kCFCalendarUnitEra:
686 goto calculate;
687 break;
688 }
689 break;
690 case kCFCalendarUnitYear:
691 switch (biggerUnit) {
692 case kCFCalendarUnitEra:
693 goto calculate;
694 break;
695 }
696 break;
697 case kCFCalendarUnitEra:
698 break;
699 }
700 }
701 return range;
702
703 calculate:;
704 ucal_clear(calendar->_cal);
705 UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit);
706 UCalendarDateFields bigField = __CFCalendarGetICUFieldCode(biggerUnit);
707 UCalendarDateFields yearField = __CFCalendarGetICUFieldCode(kCFCalendarUnitYear);
708 UCalendarDateFields fieldToAdd = smallField;
709 if (kCFCalendarUnitWeekday == smallerUnit) {
710 fieldToAdd = __CFCalendarGetICUFieldCode(kCFCalendarUnitDay);
711 }
712 int32_t dow = -1;
713 if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) {
714 UErrorCode status = U_ZERO_ERROR;
715 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
716 ucal_setMillis(calendar->_cal, udate, &status);
717 dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status);
718 fieldToAdd = __CFCalendarGetICUFieldCode(kCFCalendarUnitWeek);
719 }
720 // Set calendar to first instant of big unit
721 __CFCalendarSetToFirstInstant(calendar, biggerUnit, at);
722 if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) {
723 UErrorCode status = U_ZERO_ERROR;
724 // roll day forward to first 'dow'
725 while (ucal_get(calendar->_cal, (kCFCalendarUnitMonth == biggerUnit) ? UCAL_WEEK_OF_MONTH : UCAL_WEEK_OF_YEAR, &status) != 1) {
726 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status);
727 }
728 while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) {
729 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status);
730 }
731 }
732 int32_t minSmallValue = INT32_MAX;
733 int32_t maxSmallValue = INT32_MIN;
734 UErrorCode status = U_ZERO_ERROR;
735 int32_t bigValue = ucal_get(calendar->_cal, bigField, &status);
736 for (;;) {
737 int32_t smallValue = ucal_get(calendar->_cal, smallField, &status);
738 if (smallValue < minSmallValue) minSmallValue = smallValue;
739 if (smallValue > maxSmallValue) maxSmallValue = smallValue;
740 ucal_add(calendar->_cal, fieldToAdd, 1, &status);
741 if (bigValue != ucal_get(calendar->_cal, bigField, &status)) break;
742 if (biggerUnit == kCFCalendarUnitEra && ucal_get(calendar->_cal, yearField, &status) > 10000) break;
743 // we assume an answer for 10000 years can be extrapolated to 100000 years, to save time
744 }
745 status = U_ZERO_ERROR;
746 range.location = minSmallValue;
747 if (smallerUnit == kCFCalendarUnitMonth) range.location = 1;
748 range.length = maxSmallValue - minSmallValue + 1;
749 if (biggerUnit == kCFCalendarUnitEra && ucal_get(calendar->_cal, yearField, &status) > 10000) range.length = 100000;
750
751 return range;
752 }
753
754 CFRange CFCalendarGetRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
755 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) {
756 return __CFCalendarGetRangeOfUnit2(calendar, smallerUnit, biggerUnit, at);
757 } else {
758 return __CFCalendarGetRangeOfUnit1(calendar, smallerUnit, biggerUnit, at);
759 }
760 }
761
762 CFIndex CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
763 CFIndex result = kCFNotFound;
764 if (!__validUnits(smallerUnit, biggerUnit)) return result;
765 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFIndex, calendar, "_ordinalityOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at);
766 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
767 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
768 if (calendar->_cal) {
769 UErrorCode status = U_ZERO_ERROR;
770 ucal_clear(calendar->_cal);
771 if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitYear == biggerUnit) {
772 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
773 ucal_setMillis(calendar->_cal, udate, &status);
774 int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status);
775 return val;
776 } else if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitMonth == biggerUnit) {
777 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
778 ucal_setMillis(calendar->_cal, udate, &status);
779 int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_MONTH, &status);
780 return val;
781 }
782 UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit);
783 // Set calendar to first instant of big unit
784 __CFCalendarSetToFirstInstant(calendar, biggerUnit, at);
785 UDate curr = ucal_getMillis(calendar->_cal, &status);
786 UDate goal = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
787 result = 1;
788 const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
789 int multiple = (1 << multiple_table[flsl(smallerUnit) - 1]);
790 Boolean divide = false, alwaysDivide = false;
791 while (curr < goal) {
792 ucal_add(calendar->_cal, smallField, multiple, &status);
793 UDate newcurr = ucal_getMillis(calendar->_cal, &status);
794 if (curr < newcurr && newcurr <= goal) {
795 result += multiple;
796 curr = newcurr;
797 } else {
798 // Either newcurr is going backwards, or not making
799 // progress, or has overshot the goal; reset date
800 // and try smaller multiples.
801 ucal_setMillis(calendar->_cal, curr, &status);
802 divide = true;
803 // once we start overshooting the goal, the add at
804 // smaller multiples will succeed at most once for
805 // each multiple, so we reduce it every time through
806 // the loop.
807 if (goal < newcurr) alwaysDivide = true;
808 }
809 if (divide) {
810 multiple = multiple / 2;
811 if (0 == multiple) break;
812 divide = alwaysDivide;
813 }
814 }
815 }
816 return result;
817 }
818
819 Boolean _CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, int *vector, int count) {
820 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
821 if (calendar->_cal) {
822 UErrorCode status = U_ZERO_ERROR;
823 ucal_clear(calendar->_cal);
824 ucal_set(calendar->_cal, UCAL_YEAR, 1);
825 ucal_set(calendar->_cal, UCAL_MONTH, 0);
826 ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, 1);
827 ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, 0);
828 ucal_set(calendar->_cal, UCAL_MINUTE, 0);
829 ucal_set(calendar->_cal, UCAL_SECOND, 0);
830 const char *desc = componentDesc;
831 Boolean doWOY = false;
832 char ch = *desc;
833 while (ch) {
834 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
835 if (UCAL_WEEK_OF_YEAR == field) {
836 doWOY = true;
837 }
838 desc++;
839 ch = *desc;
840 }
841 desc = componentDesc;
842 ch = *desc;
843 while (ch) {
844 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
845 int value = *vector;
846 if (UCAL_YEAR == field && doWOY) field = UCAL_YEAR_WOY;
847 if (UCAL_MONTH == field) value--;
848 ucal_set(calendar->_cal, field, value);
849 vector++;
850 desc++;
851 ch = *desc;
852 }
853 UDate udate = ucal_getMillis(calendar->_cal, &status);
854 CFAbsoluteTime at = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970;
855 if (atp) *atp = at;
856 return U_SUCCESS(status) ? true : false;
857 }
858 return false;
859 }
860
861 Boolean _CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, int **vector, int count) {
862 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
863 if (calendar->_cal) {
864 UErrorCode status = U_ZERO_ERROR;
865 ucal_clear(calendar->_cal);
866 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
867 ucal_setMillis(calendar->_cal, udate, &status);
868 char ch = *componentDesc;
869 while (ch) {
870 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
871 int value = ucal_get(calendar->_cal, field, &status);
872 if (UCAL_MONTH == field) value++;
873 *(*vector) = value;
874 vector++;
875 componentDesc++;
876 ch = *componentDesc;
877 }
878 return U_SUCCESS(status) ? true : false;
879 }
880 return false;
881 }
882
883 Boolean _CFCalendarAddComponentsV(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, int *vector, int count) {
884 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
885 if (calendar->_cal) {
886 UErrorCode status = U_ZERO_ERROR;
887 ucal_clear(calendar->_cal);
888 UDate udate = floor((*atp + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
889 ucal_setMillis(calendar->_cal, udate, &status);
890 char ch = *componentDesc;
891 while (ch) {
892 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
893 int amount = *vector;
894 if (options & kCFCalendarComponentsWrap) {
895 ucal_roll(calendar->_cal, field, amount, &status);
896 } else {
897 ucal_add(calendar->_cal, field, amount, &status);
898 }
899 vector++;
900 componentDesc++;
901 ch = *componentDesc;
902 }
903 udate = ucal_getMillis(calendar->_cal, &status);
904 *atp = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970;
905 return U_SUCCESS(status) ? true : false;
906 }
907 return false;
908 }
909
910 Boolean _CFCalendarGetComponentDifferenceV(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, int **vector, int count) {
911 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
912 if (calendar->_cal) {
913 UErrorCode status = U_ZERO_ERROR;
914 ucal_clear(calendar->_cal);
915 UDate curr = floor((startingAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
916 UDate goal = floor((resultAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
917 ucal_setMillis(calendar->_cal, curr, &status);
918 int direction = (startingAT <= resultAT) ? 1 : -1;
919 char ch = *componentDesc;
920 while (ch) {
921 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
922 const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
923 int multiple = direction * (1 << multiple_table[flsl(__CFCalendarGetCalendarUnitFromChar(ch)) - 1]);
924 Boolean divide = false, alwaysDivide = false;
925 int result = 0;
926 while ((direction > 0 && curr < goal) || (direction < 0 && goal < curr)) {
927 ucal_add(calendar->_cal, field, multiple, &status);
928 UDate newcurr = ucal_getMillis(calendar->_cal, &status);
929 if ((direction > 0 && curr < newcurr && newcurr <= goal) || (direction < 0 && newcurr < curr && goal <= newcurr)) {
930 result += multiple;
931 curr = newcurr;
932 } else {
933 // Either newcurr is going backwards, or not making
934 // progress, or has overshot the goal; reset date
935 // and try smaller multiples.
936 ucal_setMillis(calendar->_cal, curr, &status);
937 divide = true;
938 // once we start overshooting the goal, the add at
939 // smaller multiples will succeed at most once for
940 // each multiple, so we reduce it every time through
941 // the loop.
942 if ((direction > 0 && goal < newcurr) || (direction < 0 && newcurr < goal)) alwaysDivide = true;
943 }
944 if (divide) {
945 multiple = multiple / 2;
946 if (0 == multiple) break;
947 divide = alwaysDivide;
948 }
949 }
950 *(*vector) = result;
951 vector++;
952 componentDesc++;
953 ch = *componentDesc;
954 }
955 return U_SUCCESS(status) ? true : false;
956 }
957 return false;
958 }
959
960 Boolean CFCalendarComposeAbsoluteTime(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, ...) {
961 va_list args;
962 va_start(args, componentDesc);
963 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean, calendar, "_composeAbsoluteTime:::", atp, componentDesc, args);
964 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
965 int idx, cnt = strlen((char *)componentDesc);
966 STACK_BUFFER_DECL(int, vector, cnt);
967 for (idx = 0; idx < cnt; idx++) {
968 int arg = va_arg(args, int);
969 vector[idx] = arg;
970 }
971 va_end(args);
972 return _CFCalendarComposeAbsoluteTimeV(calendar, atp, componentDesc, vector, cnt);
973 }
974
975 Boolean CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, ...) {
976 va_list args;
977 va_start(args, componentDesc);
978 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean, calendar, "_decomposeAbsoluteTime:::", at, componentDesc, args);
979 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
980 int idx, cnt = strlen((char *)componentDesc);
981 STACK_BUFFER_DECL(int *, vector, cnt);
982 for (idx = 0; idx < cnt; idx++) {
983 int *arg = va_arg(args, int *);
984 vector[idx] = arg;
985 }
986 va_end(args);
987 return _CFCalendarDecomposeAbsoluteTimeV(calendar, at, componentDesc, vector, cnt);
988 }
989
990 Boolean CFCalendarAddComponents(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, ...) {
991 va_list args;
992 va_start(args, componentDesc);
993 CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean, calendar, "_addComponents::::", atp, options, componentDesc, args);
994 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
995 int idx, cnt = strlen((char *)componentDesc);
996 STACK_BUFFER_DECL(int, vector, cnt);
997 for (idx = 0; idx < cnt; idx++) {
998 int arg = va_arg(args, int);
999 vector[idx] = arg;
1000 }
1001 va_end(args);
1002 return _CFCalendarAddComponentsV(calendar, atp, options, componentDesc, vector, cnt);
1003 }
1004
1005 Boolean CFCalendarGetComponentDifference(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, ...) {
1006 va_list args;
1007 va_start(args, componentDesc);
1008 CF_OBJC_FUNCDISPATCH5(CFCalendarGetTypeID(), Boolean, calendar, "_diffComponents:::::", startingAT, resultAT, options, componentDesc, args);
1009 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
1010 int idx, cnt = strlen((char *)componentDesc);
1011 STACK_BUFFER_DECL(int *, vector, cnt);
1012 for (idx = 0; idx < cnt; idx++) {
1013 int *arg = va_arg(args, int *);
1014 vector[idx] = arg;
1015 }
1016 va_end(args);
1017 Boolean ret = _CFCalendarGetComponentDifferenceV(calendar, startingAT, resultAT, options, componentDesc, vector, cnt);
1018 return ret;
1019 }
1020
1021 Boolean CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at, CFAbsoluteTime *startp, CFTimeInterval *tip) {
1022 CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean, calendar, "_rangeOfUnit:startTime:interval:forAT:", unit, startp, tip, at);
1023 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
1024 if (kCFCalendarUnitWeekdayOrdinal == unit) return false;
1025 if (kCFCalendarUnitWeekday == unit) unit = kCFCalendarUnitDay;
1026 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
1027 if (calendar->_cal) {
1028 ucal_clear(calendar->_cal);
1029 __CFCalendarSetToFirstInstant(calendar, unit, at);
1030 UErrorCode status = U_ZERO_ERROR;
1031 UDate start = ucal_getMillis(calendar->_cal, &status);
1032 UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit);
1033 ucal_add(calendar->_cal, field, 1, &status);
1034 UDate end = ucal_getMillis(calendar->_cal, &status);
1035 if (end == start && kCFCalendarUnitEra == unit) {
1036 // ICU refuses to do the addition, probably because we are
1037 // at the limit of UCAL_ERA. Use alternate strategy.
1038 CFIndex limit = ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_MAXIMUM, &status);
1039 if (100000 < limit) limit = 100000;
1040 ucal_add(calendar->_cal, UCAL_YEAR, limit, &status);
1041 end = ucal_getMillis(calendar->_cal, &status);
1042 }
1043 if (U_SUCCESS(status)) {
1044 if (startp) *startp = (double)start / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
1045 if (tip) *tip = (double)(end - start) / 1000.0;
1046 return true;
1047 }
1048 }
1049
1050 return false;
1051 }
1052
1053 #undef BUFFER_SIZE
1054