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