]> git.saurik.com Git - apple/cf.git/blob - CFCalendar.c
CF-1152.14.tar.gz
[apple/cf.git] / CFCalendar.c
1 /*
2 * Copyright (c) 2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFCalendar.c
25 Copyright (c) 2004-2014, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
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 CFTypeID CFCalendarGetTypeID(void) {
87 static dispatch_once_t initOnce;
88 dispatch_once(&initOnce, ^{ __kCFCalendarTypeID = _CFRuntimeRegisterClass(&__CFCalendarClass); });
89 return __kCFCalendarTypeID;
90 }
91
92 CF_PRIVATE UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz) {
93 if (calendarID) {
94 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeID);
95 CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components);
96 CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifier, calendarID);
97 localeID = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents);
98 CFRelease(mcomponents);
99 CFRelease(components);
100 }
101
102 char buffer[BUFFER_SIZE];
103 const char *cstr = CFStringGetCStringPtr(localeID, kCFStringEncodingASCII);
104 if (NULL == cstr) {
105 if (CFStringGetCString(localeID, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
106 }
107 if (NULL == cstr) {
108 if (calendarID) CFRelease(localeID);
109 return NULL;
110 }
111
112 UChar ubuffer[BUFFER_SIZE];
113 CFStringRef tznam = CFTimeZoneGetName(tz);
114 CFIndex cnt = CFStringGetLength(tznam);
115 if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
116 CFStringGetCharacters(tznam, CFRangeMake(0, cnt), (UniChar *)ubuffer);
117
118 UErrorCode status = U_ZERO_ERROR;
119 UCalendar *cal = ucal_open(ubuffer, cnt, cstr, UCAL_DEFAULT, &status);
120 if (calendarID) CFRelease(localeID);
121 return cal;
122 }
123
124 static void __CFCalendarSetupCal(CFCalendarRef calendar) {
125 calendar->_cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz);
126 }
127
128 static void __CFCalendarZapCal(CFCalendarRef calendar) {
129 ucal_close(calendar->_cal);
130 calendar->_cal = NULL;
131 }
132
133 CFCalendarRef CFCalendarCopyCurrent(void) {
134 CFLocaleRef locale = CFLocaleCopyCurrent();
135 CFCalendarRef calID = (CFCalendarRef)CFLocaleGetValue(locale, kCFLocaleCalendarIdentifier);
136 if (calID) {
137 CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, (CFStringRef)calID);
138 CFCalendarSetLocale(calendar, locale);
139 CFRelease(locale);
140 return calendar;
141 }
142 return NULL;
143 }
144
145 CFCalendarRef CFCalendarCreateWithIdentifier(CFAllocatorRef allocator, CFStringRef identifier) {
146 if (allocator == NULL) allocator = __CFGetDefaultAllocator();
147 __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
148 __CFGenericValidateType(identifier, CFStringGetTypeID());
149 // return NULL until Chinese calendar is available
150 if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar) {
151 // if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) {
152 if (CFEqual(kCFGregorianCalendar, identifier)) identifier = kCFGregorianCalendar;
153 else if (CFEqual(kCFBuddhistCalendar, identifier)) identifier = kCFBuddhistCalendar;
154 else if (CFEqual(kCFJapaneseCalendar, identifier)) identifier = kCFJapaneseCalendar;
155 else if (CFEqual(kCFIslamicCalendar, identifier)) identifier = kCFIslamicCalendar;
156 else if (CFEqual(kCFIslamicCivilCalendar, identifier)) identifier = kCFIslamicCivilCalendar;
157 else if (CFEqual(kCFHebrewCalendar, identifier)) identifier = kCFHebrewCalendar;
158 // else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar;
159 else return NULL;
160 }
161 struct __CFCalendar *calendar = NULL;
162 uint32_t size = sizeof(struct __CFCalendar) - sizeof(CFRuntimeBase);
163 calendar = (struct __CFCalendar *)_CFRuntimeCreateInstance(allocator, CFCalendarGetTypeID(), size, NULL);
164 if (NULL == calendar) {
165 return NULL;
166 }
167 calendar->_identifier = (CFStringRef)CFRetain(identifier);
168 calendar->_locale = NULL;
169 calendar->_localeID = CFLocaleGetIdentifier(CFLocaleGetSystem());
170 calendar->_tz = CFTimeZoneCopyDefault();
171 calendar->_cal = NULL;
172 return (CFCalendarRef)calendar;
173 }
174
175 CFStringRef CFCalendarGetIdentifier(CFCalendarRef calendar) {
176 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFStringRef, calendar, calendarIdentifier);
177 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
178 return calendar->_identifier;
179 }
180
181 CFLocaleRef CFCalendarCopyLocale(CFCalendarRef calendar) {
182 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFLocaleRef, calendar, _copyLocale);
183 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
184 return (CFLocaleRef)CFLocaleCreate(kCFAllocatorSystemDefault, calendar->_localeID);
185 }
186
187 void CFCalendarSetLocale(CFCalendarRef calendar, CFLocaleRef locale) {
188 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar, setLocale:locale);
189 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
190 __CFGenericValidateType(locale, CFLocaleGetTypeID());
191 CFStringRef localeID = CFLocaleGetIdentifier(locale);
192 if (localeID != calendar->_localeID) {
193 CFRelease(calendar->_localeID);
194 CFRetain(localeID);
195 calendar->_localeID = localeID;
196 if (calendar->_cal) __CFCalendarZapCal(calendar);
197 }
198 }
199
200 CFTimeZoneRef CFCalendarCopyTimeZone(CFCalendarRef calendar) {
201 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFTimeZoneRef, calendar_copyTimeZone);
202 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
203 return (CFTimeZoneRef)CFRetain(calendar->_tz);
204 }
205
206 void CFCalendarSetTimeZone(CFCalendarRef calendar, CFTimeZoneRef tz) {
207 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar, setTimeZone:tz);
208 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
209 if (tz) __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
210 if (tz != calendar->_tz) {
211 CFRelease(calendar->_tz);
212 calendar->_tz = tz ? (CFTimeZoneRef)CFRetain(tz) : CFTimeZoneCopyDefault();
213 if (calendar->_cal) __CFCalendarZapCal(calendar);
214 }
215 }
216
217 CFIndex CFCalendarGetFirstWeekday(CFCalendarRef calendar) {
218 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex, calendar, firstWeekday);
219 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
220 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
221 if (calendar->_cal) {
222 return ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK);
223 }
224 return -1;
225 }
226
227 void CFCalendarSetFirstWeekday(CFCalendarRef calendar, CFIndex wkdy) {
228 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar, setFirstWeekday:wkdy);
229 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
230 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
231 if (calendar->_cal) {
232 ucal_setAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK, wkdy);
233 }
234 }
235
236 CFIndex CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar) {
237 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex, calendar, minimumDaysInFirstWeek);
238 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
239 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
240 return calendar->_cal ? ucal_getAttribute(calendar->_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK) : -1;
241 }
242
243 void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar, CFIndex mwd) {
244 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar, setMinimumDaysInFirstWeek:mwd);
245 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
246 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
247 if (calendar->_cal) ucal_setAttribute(calendar->_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, mwd);
248 }
249
250 CFDateRef CFCalendarCopyGregorianStartDate(CFCalendarRef calendar) {
251 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFDateRef, calendar, _gregorianStartDate);
252 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
253 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
254 UErrorCode status = U_ZERO_ERROR;
255 UDate udate = calendar->_cal ? ucal_getGregorianChange(calendar->_cal, &status) : 0;
256 if (calendar->_cal && U_SUCCESS(status)) {
257 CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
258 return CFDateCreate(CFGetAllocator(calendar), at);
259 }
260 return NULL;
261 }
262
263 void CFCalendarSetGregorianStartDate(CFCalendarRef calendar, CFDateRef date) {
264 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar, _setGregorianStartDate:date);
265 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
266 if (date) __CFGenericValidateType(date, CFDateGetTypeID());
267 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
268 if (!calendar->_cal) return;
269 if (!date) {
270 UErrorCode status = U_ZERO_ERROR;
271 UCalendar *cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz);
272 UDate udate = cal ? ucal_getGregorianChange(cal, &status) : 0;
273 if (cal && U_SUCCESS(status)) {
274 status = U_ZERO_ERROR;
275 if (calendar->_cal) ucal_setGregorianChange(calendar->_cal, udate, &status);
276 }
277 if (cal) ucal_close(cal);
278 } else {
279 CFAbsoluteTime at = CFDateGetAbsoluteTime(date);
280 UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
281 UErrorCode status = U_ZERO_ERROR;
282 if (calendar->_cal) ucal_setGregorianChange(calendar->_cal, udate, &status);
283 }
284 }
285
286
287 static UCalendarDateFields __CFCalendarGetICUFieldCode(CFCalendarUnit unit) {
288 switch (unit) {
289 case kCFCalendarUnitEra: return UCAL_ERA;
290 case kCFCalendarUnitYear: return UCAL_YEAR;
291 case kCFCalendarUnitMonth: return UCAL_MONTH;
292 case kCFCalendarUnitDay: return UCAL_DAY_OF_MONTH;
293 case kCFCalendarUnitHour: return UCAL_HOUR_OF_DAY;
294 case kCFCalendarUnitMinute: return UCAL_MINUTE;
295 case kCFCalendarUnitSecond: return UCAL_SECOND;
296 case kCFCalendarUnitWeek: return UCAL_WEEK_OF_YEAR;
297 case kCFCalendarUnitWeekOfYear: return UCAL_WEEK_OF_YEAR;
298 case kCFCalendarUnitWeekOfMonth: return UCAL_WEEK_OF_MONTH;
299 case kCFCalendarUnitYearForWeekOfYear: return UCAL_YEAR_WOY;
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 'Y': return UCAL_YEAR_WOY;
320 case 'E': return UCAL_DAY_OF_WEEK;
321 case 'D': return UCAL_DAY_OF_YEAR;
322 case 'F': return UCAL_DAY_OF_WEEK_IN_MONTH;
323 case 'a': return UCAL_AM_PM;
324 case 'g': return UCAL_JULIAN_DAY;
325 }
326 return (UCalendarDateFields)-1;
327 }
328
329 static CFCalendarUnit __CFCalendarGetCalendarUnitFromChar(char ch) {
330 switch (ch) {
331 case 'G': return kCFCalendarUnitEra;
332 case 'y': return kCFCalendarUnitYear;
333 case 'M': return kCFCalendarUnitMonth;
334 case 'd': return kCFCalendarUnitDay;
335 case 'H': return kCFCalendarUnitHour;
336 case 'm': return kCFCalendarUnitMinute;
337 case 's': return kCFCalendarUnitSecond;
338 case 'w': return kCFCalendarUnitWeekOfYear;
339 case 'W': return kCFCalendarUnitWeekOfMonth;
340 case 'Y': return kCFCalendarUnitYearForWeekOfYear;
341 case 'E': return kCFCalendarUnitWeekday;
342 case 'F': return kCFCalendarUnitWeekdayOrdinal;
343 }
344 return (UCalendarDateFields)-1;
345 }
346
347 CFRange CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) {
348 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange, calendar, _minimumRangeOfUnit:unit);
349 CFRange range = {kCFNotFound, kCFNotFound};
350 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
351 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
352 if (calendar->_cal) {
353 ucal_clear(calendar->_cal);
354 UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit);
355 UErrorCode status = U_ZERO_ERROR;
356 range.location = ucal_getLimit(calendar->_cal, field, UCAL_GREATEST_MINIMUM, &status);
357 range.length = ucal_getLimit(calendar->_cal, field, UCAL_LEAST_MAXIMUM, &status) - range.location + 1;
358 if (UCAL_MONTH == field) range.location++;
359 if (100000 < range.length) range.length = 100000;
360 }
361 return range;
362 }
363
364 CFRange CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) {
365 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange, calendar, _maximumRangeOfUnit:unit);
366 CFRange range = {kCFNotFound, kCFNotFound};
367 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
368 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
369 if (calendar->_cal) {
370 ucal_clear(calendar->_cal);
371 UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit);
372 UErrorCode status = U_ZERO_ERROR;
373 range.location = ucal_getLimit(calendar->_cal, field, UCAL_MINIMUM, &status);
374 range.length = ucal_getLimit(calendar->_cal, field, UCAL_MAXIMUM, &status) - range.location + 1;
375 if (UCAL_MONTH == field) range.location++;
376 if (100000 < range.length) range.length = 100000;
377 }
378 return range;
379 }
380
381 static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at) {
382 // Set UCalendar to first instant of unit prior to 'at'
383 UErrorCode status = U_ZERO_ERROR;
384 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
385 ucal_setMillis(calendar->_cal, udate, &status);
386 int target_era = INT_MIN;
387 switch (unit) { // largest to smallest, we set the fields to their minimum value
388 case kCFCalendarUnitYearForWeekOfYear:;
389 ucal_set(calendar->_cal, UCAL_WEEK_OF_YEAR, ucal_getLimit(calendar->_cal, UCAL_WEEK_OF_YEAR, UCAL_ACTUAL_MINIMUM, &status));
390 case kCFCalendarUnitWeek:
391 case kCFCalendarUnitWeekOfMonth:;
392 case kCFCalendarUnitWeekOfYear:;
393 {
394 // reduce to first day of week, then reduce the rest of the day
395 int32_t goal = ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK);
396 int32_t dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status);
397 while (dow != goal) {
398 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, -1, &status);
399 dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status);
400 }
401 goto day;
402 }
403 case kCFCalendarUnitEra:
404 {
405 target_era = ucal_get(calendar->_cal, UCAL_ERA, &status);
406 ucal_set(calendar->_cal, UCAL_YEAR, ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_ACTUAL_MINIMUM, &status));
407 }
408 case kCFCalendarUnitYear:
409 ucal_set(calendar->_cal, UCAL_MONTH, ucal_getLimit(calendar->_cal, UCAL_MONTH, UCAL_ACTUAL_MINIMUM, &status));
410 case kCFCalendarUnitMonth:
411 ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, ucal_getLimit(calendar->_cal, UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MINIMUM, &status));
412 case kCFCalendarUnitWeekday:
413 case kCFCalendarUnitDay:
414 day:;
415 ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, ucal_getLimit(calendar->_cal, UCAL_HOUR_OF_DAY, UCAL_ACTUAL_MINIMUM, &status));
416 case kCFCalendarUnitHour:
417 ucal_set(calendar->_cal, UCAL_MINUTE, ucal_getLimit(calendar->_cal, UCAL_MINUTE, UCAL_ACTUAL_MINIMUM, &status));
418 case kCFCalendarUnitMinute:
419 ucal_set(calendar->_cal, UCAL_SECOND, ucal_getLimit(calendar->_cal, UCAL_SECOND, UCAL_ACTUAL_MINIMUM, &status));
420 case kCFCalendarUnitSecond:
421 ucal_set(calendar->_cal, UCAL_MILLISECOND, 0);
422 }
423 if (INT_MIN != target_era && ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) {
424 // In the Japanese calendar, and possibly others, eras don't necessarily
425 // start on the first day of a year, so the previous code may have backed
426 // up into the previous era, and we have to correct forward.
427 UDate bad_udate = ucal_getMillis(calendar->_cal, &status);
428 ucal_add(calendar->_cal, UCAL_MONTH, 1, &status);
429 while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) {
430 bad_udate = ucal_getMillis(calendar->_cal, &status);
431 ucal_add(calendar->_cal, UCAL_MONTH, 1, &status);
432 }
433 udate = ucal_getMillis(calendar->_cal, &status);
434 // target date is between bad_udate and udate
435 for (;;) {
436 UDate test_udate = (udate + bad_udate) / 2;
437 ucal_setMillis(calendar->_cal, test_udate, &status);
438 if (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) {
439 bad_udate = test_udate;
440 } else {
441 udate = test_udate;
442 }
443 if (fabs(udate - bad_udate) < 1000) break;
444 }
445 do {
446 bad_udate = floor((bad_udate + 1000) / 1000) * 1000;
447 ucal_setMillis(calendar->_cal, bad_udate, &status);
448 } while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era);
449 }
450 }
451
452 static Boolean __validUnits(CFCalendarUnit smaller, CFCalendarUnit bigger) {
453 switch (bigger) {
454 case kCFCalendarUnitEra:
455 if (kCFCalendarUnitEra == smaller) return false;
456 if (kCFCalendarUnitWeekday == smaller) return false;
457 if (kCFCalendarUnitMinute == smaller) return false; // this causes CFIndex overflow in range.length
458 if (kCFCalendarUnitSecond == smaller) return false; // this causes CFIndex overflow in range.length
459 return true;
460 case kCFCalendarUnitYearForWeekOfYear:
461 case kCFCalendarUnitYear:
462 if (kCFCalendarUnitEra == smaller) return false;
463 if (kCFCalendarUnitYear == smaller) return false;
464 if (kCFCalendarUnitYearForWeekOfYear == smaller) return false;
465 if (kCFCalendarUnitWeekday == smaller) return false;
466 return true;
467 case kCFCalendarUnitMonth:
468 if (kCFCalendarUnitEra == smaller) return false;
469 if (kCFCalendarUnitYear == smaller) return false;
470 if (kCFCalendarUnitMonth == smaller) return false;
471 if (kCFCalendarUnitWeekday == smaller) return false;
472 return true;
473 case kCFCalendarUnitDay:
474 if (kCFCalendarUnitHour == smaller) return true;
475 if (kCFCalendarUnitMinute == smaller) return true;
476 if (kCFCalendarUnitSecond == smaller) return true;
477 return false;
478 case kCFCalendarUnitHour:
479 if (kCFCalendarUnitMinute == smaller) return true;
480 if (kCFCalendarUnitSecond == smaller) return true;
481 return false;
482 case kCFCalendarUnitMinute:
483 if (kCFCalendarUnitSecond == smaller) return true;
484 return false;
485 case kCFCalendarUnitWeek:
486 case kCFCalendarUnitWeekOfMonth:
487 case kCFCalendarUnitWeekOfYear:
488 if (kCFCalendarUnitWeekday == smaller) return true;
489 if (kCFCalendarUnitDay == smaller) return true;
490 if (kCFCalendarUnitHour == smaller) return true;
491 if (kCFCalendarUnitMinute == smaller) return true;
492 if (kCFCalendarUnitSecond == smaller) return true;
493 return false;
494 case kCFCalendarUnitSecond:
495 case kCFCalendarUnitWeekday:
496 case kCFCalendarUnitWeekdayOrdinal:
497 return false;
498 }
499 return false;
500 };
501
502 static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) __attribute__((noinline));
503 static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
504 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange, calendar, _rangeOfUnit:smallerUnit inUnit:biggerUnit forAT:at);
505 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
506 CFRange range = {kCFNotFound, kCFNotFound};
507 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
508 if (calendar->_cal) {
509 switch (smallerUnit) {
510 case kCFCalendarUnitSecond:
511 switch (biggerUnit) {
512 case kCFCalendarUnitMinute:
513 case kCFCalendarUnitHour:
514 case kCFCalendarUnitDay:
515 case kCFCalendarUnitWeekday:
516 case kCFCalendarUnitWeek:
517 case kCFCalendarUnitMonth:
518 case kCFCalendarUnitYear:
519 case kCFCalendarUnitEra:
520 // goto calculate;
521 range.location = 0;
522 range.length = 60;
523 break;
524 }
525 break;
526 case kCFCalendarUnitMinute:
527 switch (biggerUnit) {
528 case kCFCalendarUnitHour:
529 case kCFCalendarUnitDay:
530 case kCFCalendarUnitWeekday:
531 case kCFCalendarUnitWeek:
532 case kCFCalendarUnitMonth:
533 case kCFCalendarUnitYear:
534 case kCFCalendarUnitEra:
535 // goto calculate;
536 range.location = 0;
537 range.length = 60;
538 break;
539 }
540 break;
541 case kCFCalendarUnitHour:
542 switch (biggerUnit) {
543 case kCFCalendarUnitDay:
544 case kCFCalendarUnitWeekday:
545 case kCFCalendarUnitWeek:
546 case kCFCalendarUnitMonth:
547 case kCFCalendarUnitYear:
548 case kCFCalendarUnitEra:
549 // goto calculate;
550 range.location = 0;
551 range.length = 24;
552 break;
553 }
554 break;
555 case kCFCalendarUnitDay:
556 switch (biggerUnit) {
557 case kCFCalendarUnitWeek:
558 case kCFCalendarUnitMonth:
559 case kCFCalendarUnitYear:
560 case kCFCalendarUnitEra:
561 goto calculate;
562 break;
563 }
564 break;
565 case kCFCalendarUnitWeekday:
566 switch (biggerUnit) {
567 case kCFCalendarUnitWeek:
568 case kCFCalendarUnitMonth:
569 case kCFCalendarUnitYear:
570 case kCFCalendarUnitEra:
571 goto calculate;
572 break;
573 }
574 break;
575 case kCFCalendarUnitWeekdayOrdinal:
576 switch (biggerUnit) {
577 case kCFCalendarUnitMonth:
578 case kCFCalendarUnitYear:
579 case kCFCalendarUnitEra:
580 goto calculate;
581 break;
582 }
583 break;
584 case kCFCalendarUnitWeek:
585 switch (biggerUnit) {
586 case kCFCalendarUnitMonth:
587 case kCFCalendarUnitYear:
588 case kCFCalendarUnitEra:
589 goto calculate;
590 break;
591 }
592 break;
593 case kCFCalendarUnitMonth:
594 switch (biggerUnit) {
595 case kCFCalendarUnitYear:
596 case kCFCalendarUnitEra:
597 goto calculate;
598 break;
599 }
600 break;
601 case kCFCalendarUnitYear:
602 switch (biggerUnit) {
603 case kCFCalendarUnitEra:
604 goto calculate;
605 break;
606 }
607 break;
608 case kCFCalendarUnitEra:
609 break;
610 }
611 }
612 return range;
613
614 calculate:;
615 ucal_clear(calendar->_cal);
616 UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit);
617 UCalendarDateFields bigField = __CFCalendarGetICUFieldCode(biggerUnit);
618 UCalendarDateFields yearField = __CFCalendarGetICUFieldCode(kCFCalendarUnitYear);
619 UCalendarDateFields fieldToAdd = smallField;
620 if (kCFCalendarUnitWeekday == smallerUnit) {
621 fieldToAdd = __CFCalendarGetICUFieldCode(kCFCalendarUnitDay);
622 }
623 int32_t dow = -1;
624 if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) {
625 UErrorCode status = U_ZERO_ERROR;
626 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
627 ucal_setMillis(calendar->_cal, udate, &status);
628 dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status);
629 fieldToAdd = __CFCalendarGetICUFieldCode(kCFCalendarUnitWeek);
630 }
631 // Set calendar to first instant of big unit
632 __CFCalendarSetToFirstInstant(calendar, biggerUnit, at);
633 if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) {
634 UErrorCode status = U_ZERO_ERROR;
635 // roll day forward to first 'dow'
636 while (ucal_get(calendar->_cal, (kCFCalendarUnitMonth == biggerUnit) ? UCAL_WEEK_OF_MONTH : UCAL_WEEK_OF_YEAR, &status) != 1) {
637 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status);
638 }
639 while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) {
640 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status);
641 }
642 }
643 int32_t minSmallValue = INT32_MAX;
644 int32_t maxSmallValue = INT32_MIN;
645 UErrorCode status = U_ZERO_ERROR;
646 int32_t bigValue = ucal_get(calendar->_cal, bigField, &status);
647 for (;;) {
648 int32_t smallValue = ucal_get(calendar->_cal, smallField, &status);
649 if (smallValue < minSmallValue) minSmallValue = smallValue;
650 if (smallValue > maxSmallValue) maxSmallValue = smallValue;
651 ucal_add(calendar->_cal, fieldToAdd, 1, &status);
652 if (bigValue != ucal_get(calendar->_cal, bigField, &status)) break;
653 if (biggerUnit == kCFCalendarUnitEra && ucal_get(calendar->_cal, yearField, &status) > 10000) break;
654 // we assume an answer for 10000 years can be extrapolated to 100000 years, to save time
655 }
656 status = U_ZERO_ERROR;
657 range.location = minSmallValue;
658 if (smallerUnit == kCFCalendarUnitMonth) range.location = 1;
659 range.length = maxSmallValue - minSmallValue + 1;
660 if (biggerUnit == kCFCalendarUnitEra && ucal_get(calendar->_cal, yearField, &status) > 10000) range.length = 100000;
661
662 return range;
663 }
664
665 CFRange CFCalendarGetRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
666 return __CFCalendarGetRangeOfUnit2(calendar, smallerUnit, biggerUnit, at);
667 }
668
669 CFIndex CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
670 CFIndex result = kCFNotFound;
671 if (!__validUnits(smallerUnit, biggerUnit)) return result;
672 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex, calendar, _ordinalityOfUnit:smallerUnit inUnit:biggerUnit forAT:at);
673 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
674 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
675 if (calendar->_cal) {
676 UErrorCode status = U_ZERO_ERROR;
677 ucal_clear(calendar->_cal);
678 if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitYear == biggerUnit) {
679 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
680 ucal_setMillis(calendar->_cal, udate, &status);
681 int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status);
682 return val;
683 } else if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitMonth == biggerUnit) {
684 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
685 ucal_setMillis(calendar->_cal, udate, &status);
686 int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_MONTH, &status);
687 return val;
688 }
689 UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit);
690 // Set calendar to first instant of big unit
691 __CFCalendarSetToFirstInstant(calendar, biggerUnit, at);
692 UDate curr = ucal_getMillis(calendar->_cal, &status);
693 UDate goal = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
694 result = 1;
695 const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
696 int multiple = (1 << multiple_table[flsl(smallerUnit) - 1]);
697 Boolean divide = false, alwaysDivide = false;
698 while (curr < goal) {
699 ucal_add(calendar->_cal, smallField, multiple, &status);
700 UDate newcurr = ucal_getMillis(calendar->_cal, &status);
701 if (curr < newcurr && newcurr <= goal) {
702 result += multiple;
703 curr = newcurr;
704 } else {
705 // Either newcurr is going backwards, or not making
706 // progress, or has overshot the goal; reset date
707 // and try smaller multiples.
708 ucal_setMillis(calendar->_cal, curr, &status);
709 divide = true;
710 // once we start overshooting the goal, the add at
711 // smaller multiples will succeed at most once for
712 // each multiple, so we reduce it every time through
713 // the loop.
714 if (goal < newcurr) alwaysDivide = true;
715 }
716 if (divide) {
717 multiple = multiple / 2;
718 if (0 == multiple) break;
719 divide = alwaysDivide;
720 }
721 }
722 }
723 return result;
724 }
725
726 Boolean _CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, int *vector, int count) {
727 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
728 if (calendar->_cal) {
729 UErrorCode status = U_ZERO_ERROR;
730 ucal_clear(calendar->_cal);
731 ucal_set(calendar->_cal, UCAL_YEAR, 1);
732 ucal_set(calendar->_cal, UCAL_MONTH, 0);
733 ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, 1);
734 ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, 0);
735 ucal_set(calendar->_cal, UCAL_MINUTE, 0);
736 ucal_set(calendar->_cal, UCAL_SECOND, 0);
737 const char *desc = componentDesc;
738 Boolean doWOY = false;
739 char ch = *desc;
740 while (ch) {
741 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
742 if (UCAL_WEEK_OF_YEAR == field) {
743 doWOY = true;
744 }
745 desc++;
746 ch = *desc;
747 }
748 desc = componentDesc;
749 ch = *desc;
750 while (ch) {
751 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
752 int value = *vector;
753 if (UCAL_YEAR == field && doWOY) field = UCAL_YEAR_WOY;
754 if (UCAL_MONTH == field) value--;
755 ucal_set(calendar->_cal, field, value);
756 vector++;
757 desc++;
758 ch = *desc;
759 }
760 UDate udate = ucal_getMillis(calendar->_cal, &status);
761 CFAbsoluteTime at = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970;
762 if (atp) *atp = at;
763 return U_SUCCESS(status) ? true : false;
764 }
765 return false;
766 }
767
768 Boolean _CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, int **vector, int count) {
769 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
770 if (calendar->_cal) {
771 UErrorCode status = U_ZERO_ERROR;
772 ucal_clear(calendar->_cal);
773 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
774 ucal_setMillis(calendar->_cal, udate, &status);
775 char ch = *componentDesc;
776 while (ch) {
777 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
778 int value = ucal_get(calendar->_cal, field, &status);
779 if (UCAL_MONTH == field) value++;
780 *(*vector) = value;
781 vector++;
782 componentDesc++;
783 ch = *componentDesc;
784 }
785 return U_SUCCESS(status) ? true : false;
786 }
787 return false;
788 }
789
790 Boolean _CFCalendarAddComponentsV(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, int *vector, int count) {
791 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
792 if (calendar->_cal) {
793 UErrorCode status = U_ZERO_ERROR;
794 ucal_clear(calendar->_cal);
795 UDate udate = floor((*atp + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
796 ucal_setMillis(calendar->_cal, udate, &status);
797 char ch = *componentDesc;
798 while (ch) {
799 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
800 int amount = *vector;
801 if (options & kCFCalendarComponentsWrap) {
802 ucal_roll(calendar->_cal, field, amount, &status);
803 } else {
804 ucal_add(calendar->_cal, field, amount, &status);
805 }
806 vector++;
807 componentDesc++;
808 ch = *componentDesc;
809 }
810 udate = ucal_getMillis(calendar->_cal, &status);
811 *atp = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970;
812 return U_SUCCESS(status) ? true : false;
813 }
814 return false;
815 }
816
817 Boolean _CFCalendarGetComponentDifferenceV(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, 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 UDate curr = floor((startingAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
823 UDate goal = floor((resultAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
824 ucal_setMillis(calendar->_cal, curr, &status);
825 int direction = (startingAT <= resultAT) ? 1 : -1;
826 char ch = *componentDesc;
827 while (ch) {
828 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
829 const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
830 int multiple = direction * (1 << multiple_table[flsl(__CFCalendarGetCalendarUnitFromChar(ch)) - 1]);
831 Boolean divide = false, alwaysDivide = false;
832 int result = 0;
833 while ((direction > 0 && curr < goal) || (direction < 0 && goal < curr)) {
834 ucal_add(calendar->_cal, field, multiple, &status);
835 UDate newcurr = ucal_getMillis(calendar->_cal, &status);
836 if ((direction > 0 && curr < newcurr && newcurr <= goal) || (direction < 0 && newcurr < curr && goal <= newcurr)) {
837 result += multiple;
838 curr = newcurr;
839 } else {
840 // Either newcurr is going backwards, or not making
841 // progress, or has overshot the goal; reset date
842 // and try smaller multiples.
843 ucal_setMillis(calendar->_cal, curr, &status);
844 divide = true;
845 // once we start overshooting the goal, the add at
846 // smaller multiples will succeed at most once for
847 // each multiple, so we reduce it every time through
848 // the loop.
849 if ((direction > 0 && goal < newcurr) || (direction < 0 && newcurr < goal)) alwaysDivide = true;
850 }
851 if (divide) {
852 multiple = multiple / 2;
853 if (0 == multiple) break;
854 divide = alwaysDivide;
855 }
856 }
857 *(*vector) = result;
858 vector++;
859 componentDesc++;
860 ch = *componentDesc;
861 }
862 return U_SUCCESS(status) ? true : false;
863 }
864 return false;
865 }
866
867 Boolean CFCalendarComposeAbsoluteTime(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, ...) {
868 va_list args;
869 va_start(args, componentDesc);
870 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _composeAbsoluteTime:atp :componentDesc :args);
871 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
872 int idx, cnt = strlen((char *)componentDesc);
873 STACK_BUFFER_DECL(int, vector, cnt);
874 for (idx = 0; idx < cnt; idx++) {
875 int arg = va_arg(args, int);
876 vector[idx] = arg;
877 }
878 va_end(args);
879 return _CFCalendarComposeAbsoluteTimeV(calendar, atp, componentDesc, vector, cnt);
880 }
881
882 Boolean CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, ...) {
883 va_list args;
884 va_start(args, componentDesc);
885 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _decomposeAbsoluteTime:at :componentDesc :args);
886 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
887 int idx, cnt = strlen((char *)componentDesc);
888 STACK_BUFFER_DECL(int *, vector, cnt);
889 for (idx = 0; idx < cnt; idx++) {
890 int *arg = va_arg(args, int *);
891 vector[idx] = arg;
892 }
893 va_end(args);
894 return _CFCalendarDecomposeAbsoluteTimeV(calendar, at, componentDesc, vector, cnt);
895 }
896
897 Boolean CFCalendarAddComponents(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, ...) {
898 va_list args;
899 va_start(args, componentDesc);
900 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _addComponents:atp :options :componentDesc :args);
901 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
902 int idx, cnt = strlen((char *)componentDesc);
903 STACK_BUFFER_DECL(int, vector, cnt);
904 for (idx = 0; idx < cnt; idx++) {
905 int arg = va_arg(args, int);
906 vector[idx] = arg;
907 }
908 va_end(args);
909 return _CFCalendarAddComponentsV(calendar, atp, options, componentDesc, vector, cnt);
910 }
911
912 Boolean CFCalendarGetComponentDifference(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, ...) {
913 va_list args;
914 va_start(args, componentDesc);
915 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _diffComponents:startingAT :resultAT :options :componentDesc :args);
916 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
917 int idx, cnt = strlen((char *)componentDesc);
918 STACK_BUFFER_DECL(int *, vector, cnt);
919 for (idx = 0; idx < cnt; idx++) {
920 int *arg = va_arg(args, int *);
921 vector[idx] = arg;
922 }
923 va_end(args);
924 Boolean ret = _CFCalendarGetComponentDifferenceV(calendar, startingAT, resultAT, options, componentDesc, vector, cnt);
925 return ret;
926 }
927
928 Boolean CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at, CFAbsoluteTime *startp, CFTimeInterval *tip) {
929 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _rangeOfUnit:unit startTime:startp interval:tip forAT:at);
930 __CFGenericValidateType(calendar, CFCalendarGetTypeID());
931 if (kCFCalendarUnitWeekdayOrdinal == unit) return false;
932 if (kCFCalendarUnitWeekday == unit) unit = kCFCalendarUnitDay;
933 if (!calendar->_cal) __CFCalendarSetupCal(calendar);
934 if (calendar->_cal) {
935 ucal_clear(calendar->_cal);
936 __CFCalendarSetToFirstInstant(calendar, unit, at);
937 UErrorCode status = U_ZERO_ERROR;
938 UDate start = ucal_getMillis(calendar->_cal, &status);
939 UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit);
940 ucal_add(calendar->_cal, field, 1, &status);
941 UDate end = ucal_getMillis(calendar->_cal, &status);
942 if (end == start && kCFCalendarUnitEra == unit) {
943 // ICU refuses to do the addition, probably because we are
944 // at the limit of UCAL_ERA. Use alternate strategy.
945 CFIndex limit = ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_MAXIMUM, &status);
946 if (100000 < limit) limit = 100000;
947 ucal_add(calendar->_cal, UCAL_YEAR, limit, &status);
948 end = ucal_getMillis(calendar->_cal, &status);
949 }
950 if (U_SUCCESS(status)) {
951 if (startp) *startp = (double)start / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
952 if (tip) *tip = (double)(end - start) / 1000.0;
953 return true;
954 }
955 }
956
957 return false;
958 }
959
960 #undef BUFFER_SIZE
961