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