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