| /* |
| * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved. |
| * |
| * This source code is a modified version of the CoreFoundation sources released by Apple Inc. under |
| * the terms of the APSL version 2.0 (see below). |
| * |
| * For information about changes from the original Apple source release can be found by reviewing the |
| * source control system for the project at https://sourceforge.net/svn/?group_id=246198. |
| * |
| * The original license information is as follows: |
| * |
| * Copyright (c) 2008 Apple Inc. All rights reserved. |
| * |
| * @APPLE_LICENSE_HEADER_START@ |
| * |
| * This file contains Original Code and/or Modifications of Original Code |
| * as defined in and that are subject to the Apple Public Source License |
| * Version 2.0 (the 'License'). You may not use this file except in |
| * compliance with the License. Please obtain a copy of the License at |
| * http://www.opensource.apple.com/apsl/ and read it before using this |
| * file. |
| * |
| * The Original Code and all software distributed under the License are |
| * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| * Please see the License for the specific language governing rights and |
| * limitations under the License. |
| * |
| * @APPLE_LICENSE_HEADER_END@ |
| */ |
| /* CFCalendar.c |
| Copyright 2004-2004, Apple Computer, Inc. All rights reserved. |
| Responsibility: Christopher Kane |
| */ |
| |
| #include <CoreFoundation/CFCalendar.h> |
| #include <CoreFoundation/CFRuntime.h> |
| #include "CFInternal.h" |
| #include "CFPriv.h" |
| #include <unicode/ucal.h> |
| |
| #define BUFFER_SIZE 512 |
| |
| struct __CFCalendar { |
| CFRuntimeBase _base; |
| CFStringRef _identifier; // canonical identifier, never NULL |
| CFLocaleRef _locale; |
| CFStringRef _localeID; |
| CFTimeZoneRef _tz; |
| UCalendar *_cal; |
| }; |
| |
| static Boolean __CFCalendarEqual(CFTypeRef cf1, CFTypeRef cf2) { |
| CFCalendarRef calendar1 = (CFCalendarRef)cf1; |
| CFCalendarRef calendar2 = (CFCalendarRef)cf2; |
| return CFEqual(calendar1->_identifier, calendar2->_identifier); |
| } |
| |
| static CFHashCode __CFCalendarHash(CFTypeRef cf) { |
| CFCalendarRef calendar = (CFCalendarRef)cf; |
| return CFHash(calendar->_identifier); |
| } |
| |
| static CFStringRef __CFCalendarCopyDescription(CFTypeRef cf) { |
| CFCalendarRef calendar = (CFCalendarRef)cf; |
| return CFStringCreateWithFormat(CFGetAllocator(calendar), NULL, CFSTR("<CFCalendar %p [%p]>{identifier = '%@'}"), cf, CFGetAllocator(calendar), calendar->_identifier); |
| } |
| |
| static void __CFCalendarDeallocate(CFTypeRef cf) { |
| CFCalendarRef calendar = (CFCalendarRef)cf; |
| CFRelease(calendar->_identifier); |
| if (calendar->_locale) CFRelease(calendar->_locale); |
| if (calendar->_localeID) CFRelease(calendar->_localeID); |
| CFRelease(calendar->_tz); |
| if (calendar->_cal) ucal_close(calendar->_cal); |
| } |
| |
| static CFTypeID __kCFCalendarTypeID = _kCFRuntimeNotATypeID; |
| |
| static const CFRuntimeClass __CFCalendarClass = { |
| 0, |
| "CFCalendar", |
| NULL, // init |
| NULL, // copy |
| __CFCalendarDeallocate, |
| __CFCalendarEqual, |
| __CFCalendarHash, |
| NULL, // |
| __CFCalendarCopyDescription |
| }; |
| |
| static void __CFCalendarInitialize(void) { |
| __kCFCalendarTypeID = _CFRuntimeRegisterClass(&__CFCalendarClass); |
| } |
| |
| CFTypeID CFCalendarGetTypeID(void) { |
| if (_kCFRuntimeNotATypeID == __kCFCalendarTypeID) __CFCalendarInitialize(); |
| return __kCFCalendarTypeID; |
| } |
| |
| __private_extern__ UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz) { |
| if (calendarID) { |
| CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeID); |
| CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components); |
| CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifier, calendarID); |
| localeID = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents); |
| CFRelease(mcomponents); |
| CFRelease(components); |
| } |
| |
| char buffer[BUFFER_SIZE]; |
| const char *cstr = CFStringGetCStringPtr(localeID, kCFStringEncodingASCII); |
| if (NULL == cstr) { |
| if (CFStringGetCString(localeID, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; |
| } |
| if (NULL == cstr) { |
| if (calendarID) CFRelease(localeID); |
| return NULL; |
| } |
| |
| UChar ubuffer[BUFFER_SIZE]; |
| CFStringRef tznam = CFTimeZoneGetName(tz); |
| CFIndex cnt = CFStringGetLength(tznam); |
| if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; |
| CFStringGetCharacters(tznam, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| |
| UErrorCode status = U_ZERO_ERROR; |
| UCalendar *cal = ucal_open(ubuffer, cnt, cstr, UCAL_TRADITIONAL, &status); |
| if (calendarID) CFRelease(localeID); |
| return cal; |
| } |
| |
| static void __CFCalendarSetupCal(CFCalendarRef calendar) { |
| calendar->_cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz); |
| } |
| |
| static void __CFCalendarZapCal(CFCalendarRef calendar) { |
| ucal_close(calendar->_cal); |
| calendar->_cal = NULL; |
| } |
| |
| CFCalendarRef CFCalendarCopyCurrent(void) { |
| CFLocaleRef locale = CFLocaleCopyCurrent(); |
| CFCalendarRef calID = (CFCalendarRef)CFLocaleGetValue(locale, kCFLocaleCalendarIdentifier); |
| if (calID) { |
| CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, (CFStringRef)calID); |
| CFCalendarSetLocale(calendar, locale); |
| CFRelease(locale); |
| return calendar; |
| } |
| return NULL; |
| } |
| |
| CFCalendarRef CFCalendarCreateWithIdentifier(CFAllocatorRef allocator, CFStringRef identifier) { |
| if (allocator == NULL) allocator = __CFGetDefaultAllocator(); |
| __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| __CFGenericValidateType(identifier, CFStringGetTypeID()); |
| // return NULL until Chinese calendar is available |
| if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar) { |
| // if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) { |
| if (CFEqual(kCFGregorianCalendar, identifier)) identifier = kCFGregorianCalendar; |
| else if (CFEqual(kCFBuddhistCalendar, identifier)) identifier = kCFBuddhistCalendar; |
| else if (CFEqual(kCFJapaneseCalendar, identifier)) identifier = kCFJapaneseCalendar; |
| else if (CFEqual(kCFIslamicCalendar, identifier)) identifier = kCFIslamicCalendar; |
| else if (CFEqual(kCFIslamicCivilCalendar, identifier)) identifier = kCFIslamicCivilCalendar; |
| else if (CFEqual(kCFHebrewCalendar, identifier)) identifier = kCFHebrewCalendar; |
| // else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar; |
| else return NULL; |
| } |
| struct __CFCalendar *calendar = NULL; |
| uint32_t size = sizeof(struct __CFCalendar) - sizeof(CFRuntimeBase); |
| calendar = (struct __CFCalendar *)_CFRuntimeCreateInstance(allocator, CFCalendarGetTypeID(), size, NULL); |
| if (NULL == calendar) { |
| return NULL; |
| } |
| calendar->_identifier = (CFStringRef)CFRetain(identifier); |
| calendar->_locale = NULL; |
| calendar->_localeID = CFLocaleGetIdentifier(CFLocaleGetSystem()); |
| calendar->_tz = CFTimeZoneCopyDefault(); |
| calendar->_cal = NULL; |
| return (CFCalendarRef)calendar; |
| } |
| |
| CFStringRef CFCalendarGetIdentifier(CFCalendarRef calendar) { |
| CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFStringRef, calendar, "calendarIdentifier"); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| return calendar->_identifier; |
| } |
| |
| CFLocaleRef CFCalendarCopyLocale(CFCalendarRef calendar) { |
| CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFLocaleRef, calendar, "_copyLocale"); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| return (CFLocaleRef)CFLocaleCreate(kCFAllocatorSystemDefault, calendar->_localeID); |
| } |
| |
| void CFCalendarSetLocale(CFCalendarRef calendar, CFLocaleRef locale) { |
| CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setLocale:", locale); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| __CFGenericValidateType(locale, CFLocaleGetTypeID()); |
| CFStringRef localeID = CFLocaleGetIdentifier(locale); |
| if (localeID != calendar->_localeID) { |
| CFRelease(calendar->_localeID); |
| CFRetain(localeID); |
| calendar->_localeID = localeID; |
| if (calendar->_cal) __CFCalendarZapCal(calendar); |
| } |
| } |
| |
| CFTimeZoneRef CFCalendarCopyTimeZone(CFCalendarRef calendar) { |
| CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFTimeZoneRef, calendar, "_copyTimeZone"); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| return (CFTimeZoneRef)CFRetain(calendar->_tz); |
| } |
| |
| void CFCalendarSetTimeZone(CFCalendarRef calendar, CFTimeZoneRef tz) { |
| CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setTimeZone:", tz); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (tz) __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); |
| if (tz != calendar->_tz) { |
| CFRelease(calendar->_tz); |
| calendar->_tz = tz ? (CFTimeZoneRef)CFRetain(tz) : CFTimeZoneCopyDefault(); |
| if (calendar->_cal) __CFCalendarZapCal(calendar); |
| } |
| } |
| |
| CFIndex CFCalendarGetFirstWeekday(CFCalendarRef calendar) { |
| CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex, calendar, "firstWeekday"); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| return ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK); |
| } |
| return -1; |
| } |
| |
| void CFCalendarSetFirstWeekday(CFCalendarRef calendar, CFIndex wkdy) { |
| CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setFirstWeekday:", wkdy); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| ucal_setAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK, wkdy); |
| } |
| } |
| |
| CFIndex CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar) { |
| CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex, calendar, "minimumDaysInFirstWeek"); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| return calendar->_cal ? ucal_getAttribute(calendar->_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK) : -1; |
| } |
| |
| void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar, CFIndex mwd) { |
| CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setMinimumDaysInFirstWeek:", mwd); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) ucal_setAttribute(calendar->_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, mwd); |
| } |
| |
| CFDateRef CFCalendarCopyGregorianStartDate(CFCalendarRef calendar) { |
| CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFDateRef, calendar, "_gregorianStartDate"); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| UErrorCode status = U_ZERO_ERROR; |
| UDate udate = calendar->_cal ? ucal_getGregorianChange(calendar->_cal, &status) : 0; |
| if (calendar->_cal && U_SUCCESS(status)) { |
| CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; |
| return CFDateCreate(CFGetAllocator(calendar), at); |
| } |
| return NULL; |
| } |
| |
| void CFCalendarSetGregorianStartDate(CFCalendarRef calendar, CFDateRef date) { |
| CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "_setGregorianStartDate:", date); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (date) __CFGenericValidateType(date, CFDateGetTypeID()); |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (!calendar->_cal) return; |
| if (!date) { |
| UErrorCode status = U_ZERO_ERROR; |
| UCalendar *cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz); |
| UDate udate = cal ? ucal_getGregorianChange(cal, &status) : 0; |
| if (cal && U_SUCCESS(status)) { |
| status = U_ZERO_ERROR; |
| if (calendar->_cal) ucal_setGregorianChange(calendar->_cal, udate, &status); |
| } |
| if (cal) ucal_close(cal); |
| } else { |
| CFAbsoluteTime at = CFDateGetAbsoluteTime(date); |
| UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; |
| UErrorCode status = U_ZERO_ERROR; |
| if (calendar->_cal) ucal_setGregorianChange(calendar->_cal, udate, &status); |
| } |
| } |
| |
| |
| static UCalendarDateFields __CFCalendarGetICUFieldCode(CFCalendarUnit unit) { |
| switch (unit) { |
| case kCFCalendarUnitEra: return UCAL_ERA; |
| case kCFCalendarUnitYear: return UCAL_YEAR; |
| case kCFCalendarUnitMonth: return UCAL_MONTH; |
| case kCFCalendarUnitDay: return UCAL_DAY_OF_MONTH; |
| case kCFCalendarUnitHour: return UCAL_HOUR_OF_DAY; |
| case kCFCalendarUnitMinute: return UCAL_MINUTE; |
| case kCFCalendarUnitSecond: return UCAL_SECOND; |
| case kCFCalendarUnitWeek: return UCAL_WEEK_OF_YEAR; |
| case kCFCalendarUnitWeekday: return UCAL_DAY_OF_WEEK; |
| case kCFCalendarUnitWeekdayOrdinal: return UCAL_DAY_OF_WEEK_IN_MONTH; |
| } |
| return (UCalendarDateFields)-1; |
| } |
| |
| static UCalendarDateFields __CFCalendarGetICUFieldCodeFromChar(char ch) { |
| switch (ch) { |
| case 'G': return UCAL_ERA; |
| case 'y': return UCAL_YEAR; |
| case 'M': return UCAL_MONTH; |
| case 'd': return UCAL_DAY_OF_MONTH; |
| case 'h': return UCAL_HOUR; |
| case 'H': return UCAL_HOUR_OF_DAY; |
| case 'm': return UCAL_MINUTE; |
| case 's': return UCAL_SECOND; |
| case 'S': return UCAL_MILLISECOND; |
| case 'w': return UCAL_WEEK_OF_YEAR; |
| case 'W': return UCAL_WEEK_OF_MONTH; |
| case 'E': return UCAL_DAY_OF_WEEK; |
| case 'D': return UCAL_DAY_OF_YEAR; |
| case 'F': return UCAL_DAY_OF_WEEK_IN_MONTH; |
| case 'a': return UCAL_AM_PM; |
| case 'g': return UCAL_JULIAN_DAY; |
| } |
| return (UCalendarDateFields)-1; |
| } |
| |
| static UCalendarDateFields __CFCalendarGetCalendarUnitFromChar(char ch) { |
| switch (ch) { |
| case 'G': return (UCalendarDateFields)kCFCalendarUnitEra; |
| case 'y': return (UCalendarDateFields)kCFCalendarUnitYear; |
| case 'M': return (UCalendarDateFields)kCFCalendarUnitMonth; |
| case 'd': return (UCalendarDateFields)kCFCalendarUnitDay; |
| case 'H': return (UCalendarDateFields)kCFCalendarUnitHour; |
| case 'm': return (UCalendarDateFields)kCFCalendarUnitMinute; |
| case 's': return (UCalendarDateFields)kCFCalendarUnitSecond; |
| case 'w': return (UCalendarDateFields)kCFCalendarUnitWeek; |
| case 'E': return (UCalendarDateFields)kCFCalendarUnitWeekday; |
| case 'F': return (UCalendarDateFields)kCFCalendarUnitWeekdayOrdinal; |
| } |
| return (UCalendarDateFields)-1; |
| } |
| |
| CFRange CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) { |
| CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange, calendar, "_minimumRangeOfUnit:", unit); |
| CFRange range = {kCFNotFound, kCFNotFound}; |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| ucal_clear(calendar->_cal); |
| UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit); |
| UErrorCode status = U_ZERO_ERROR; |
| range.location = ucal_getLimit(calendar->_cal, field, UCAL_GREATEST_MINIMUM, &status); |
| range.length = ucal_getLimit(calendar->_cal, field, UCAL_LEAST_MAXIMUM, &status) - range.location + 1; |
| if (UCAL_MONTH == field) range.location++; |
| if (100000 < range.length) range.length = 100000; |
| } |
| return range; |
| } |
| |
| CFRange CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) { |
| CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange, calendar, "_maximumRangeOfUnit:", unit); |
| CFRange range = {kCFNotFound, kCFNotFound}; |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| ucal_clear(calendar->_cal); |
| UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit); |
| UErrorCode status = U_ZERO_ERROR; |
| range.location = ucal_getLimit(calendar->_cal, field, UCAL_MINIMUM, &status); |
| range.length = ucal_getLimit(calendar->_cal, field, UCAL_MAXIMUM, &status) - range.location + 1; |
| if (UCAL_MONTH == field) range.location++; |
| if (100000 < range.length) range.length = 100000; |
| } |
| return range; |
| } |
| |
| static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at) { |
| // Set UCalendar to first instant of unit prior to 'at' |
| UErrorCode status = U_ZERO_ERROR; |
| UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); |
| ucal_setMillis(calendar->_cal, udate, &status); |
| int target_era = INT_MIN; |
| switch (unit) { // largest to smallest, we set the fields to their minimum value |
| case kCFCalendarUnitWeek: |
| { |
| // reduce to first day of week, then reduce the rest of the day |
| int32_t goal = ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK); |
| int32_t dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); |
| while (dow != goal) { |
| ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, -1, &status); |
| dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); |
| } |
| goto day; |
| } |
| case kCFCalendarUnitEra: |
| { |
| target_era = ucal_get(calendar->_cal, UCAL_ERA, &status); |
| ucal_set(calendar->_cal, UCAL_YEAR, ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_ACTUAL_MINIMUM, &status)); |
| } |
| case kCFCalendarUnitYear: |
| ucal_set(calendar->_cal, UCAL_MONTH, ucal_getLimit(calendar->_cal, UCAL_MONTH, UCAL_ACTUAL_MINIMUM, &status)); |
| case kCFCalendarUnitMonth: |
| ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, ucal_getLimit(calendar->_cal, UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MINIMUM, &status)); |
| case kCFCalendarUnitWeekday: |
| case kCFCalendarUnitDay: |
| day:; |
| ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, ucal_getLimit(calendar->_cal, UCAL_HOUR_OF_DAY, UCAL_ACTUAL_MINIMUM, &status)); |
| case kCFCalendarUnitHour: |
| ucal_set(calendar->_cal, UCAL_MINUTE, ucal_getLimit(calendar->_cal, UCAL_MINUTE, UCAL_ACTUAL_MINIMUM, &status)); |
| case kCFCalendarUnitMinute: |
| ucal_set(calendar->_cal, UCAL_SECOND, ucal_getLimit(calendar->_cal, UCAL_SECOND, UCAL_ACTUAL_MINIMUM, &status)); |
| case kCFCalendarUnitSecond: |
| ucal_set(calendar->_cal, UCAL_MILLISECOND, 0); |
| } |
| if (INT_MIN != target_era && ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) { |
| // In the Japanese calendar, and possibly others, eras don't necessarily |
| // start on the first day of a year, so the previous code may have backed |
| // up into the previous era, and we have to correct forward. |
| UDate bad_udate = ucal_getMillis(calendar->_cal, &status); |
| ucal_add(calendar->_cal, UCAL_MONTH, 1, &status); |
| while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) { |
| bad_udate = ucal_getMillis(calendar->_cal, &status); |
| ucal_add(calendar->_cal, UCAL_MONTH, 1, &status); |
| } |
| udate = ucal_getMillis(calendar->_cal, &status); |
| // target date is between bad_udate and udate |
| for (;;) { |
| UDate test_udate = (udate + bad_udate) / 2; |
| ucal_setMillis(calendar->_cal, test_udate, &status); |
| if (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) { |
| bad_udate = test_udate; |
| } else { |
| udate = test_udate; |
| } |
| if (fabs(udate - bad_udate) < 1000) break; |
| } |
| do { |
| bad_udate = floor((bad_udate + 1000) / 1000) * 1000; |
| ucal_setMillis(calendar->_cal, bad_udate, &status); |
| } while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era); |
| } |
| } |
| |
| static Boolean __validUnits(CFCalendarUnit smaller, CFCalendarUnit bigger) { |
| switch (bigger) { |
| case kCFCalendarUnitEra: |
| if (kCFCalendarUnitEra == smaller) return false; |
| if (kCFCalendarUnitWeekday == smaller) return false; |
| if (kCFCalendarUnitMinute == smaller) return false; // this causes CFIndex overflow in range.length |
| if (kCFCalendarUnitSecond == smaller) return false; // this causes CFIndex overflow in range.length |
| return true; |
| case kCFCalendarUnitYear: |
| if (kCFCalendarUnitEra == smaller) return false; |
| if (kCFCalendarUnitYear == smaller) return false; |
| if (kCFCalendarUnitWeekday == smaller) return false; |
| return true; |
| case kCFCalendarUnitMonth: |
| if (kCFCalendarUnitEra == smaller) return false; |
| if (kCFCalendarUnitYear == smaller) return false; |
| if (kCFCalendarUnitMonth == smaller) return false; |
| if (kCFCalendarUnitWeekday == smaller) return false; |
| return true; |
| case kCFCalendarUnitDay: |
| if (kCFCalendarUnitHour == smaller) return true; |
| if (kCFCalendarUnitMinute == smaller) return true; |
| if (kCFCalendarUnitSecond == smaller) return true; |
| return false; |
| case kCFCalendarUnitHour: |
| if (kCFCalendarUnitMinute == smaller) return true; |
| if (kCFCalendarUnitSecond == smaller) return true; |
| return false; |
| case kCFCalendarUnitMinute: |
| if (kCFCalendarUnitSecond == smaller) return true; |
| return false; |
| case kCFCalendarUnitWeek: |
| if (kCFCalendarUnitWeekday == smaller) return true; |
| if (kCFCalendarUnitDay == smaller) return true; |
| if (kCFCalendarUnitHour == smaller) return true; |
| if (kCFCalendarUnitMinute == smaller) return true; |
| if (kCFCalendarUnitSecond == smaller) return true; |
| return false; |
| case kCFCalendarUnitSecond: |
| case kCFCalendarUnitWeekday: |
| case kCFCalendarUnitWeekdayOrdinal: |
| return false; |
| } |
| return false; |
| }; |
| |
| static CFRange __CFCalendarGetRangeOfUnit1(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { |
| CFRange range = {kCFNotFound, kCFNotFound}; |
| if (!__validUnits(smallerUnit, biggerUnit)) return range; |
| CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange, calendar, "_rangeOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| int32_t dow = -1; |
| ucal_clear(calendar->_cal); |
| UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit); |
| UCalendarDateFields bigField = __CFCalendarGetICUFieldCode(biggerUnit); |
| if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { |
| UErrorCode status = U_ZERO_ERROR; |
| UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); |
| ucal_setMillis(calendar->_cal, udate, &status); |
| dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); |
| } |
| // Set calendar to first instant of big unit |
| __CFCalendarSetToFirstInstant(calendar, biggerUnit, at); |
| UErrorCode status = U_ZERO_ERROR; |
| UDate start = ucal_getMillis(calendar->_cal, &status); |
| if (kCFCalendarUnitWeek == biggerUnit) { |
| range.location = ucal_get(calendar->_cal, smallField, &status); |
| if (kCFCalendarUnitMonth == smallerUnit) range.location++; |
| } else { |
| range.location = (kCFCalendarUnitHour == smallerUnit || kCFCalendarUnitMinute == smallerUnit || kCFCalendarUnitSecond == smallerUnit) ? 0 : 1; |
| } |
| // Set calendar to first instant of next value of big unit |
| if (UCAL_ERA == bigField) { |
| // ICU refuses to do the addition, probably because we are |
| // at the limit of UCAL_ERA. Use alternate strategy. |
| CFIndex limit = ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_MAXIMUM, &status); |
| if (100000 < limit) limit = 100000; |
| ucal_add(calendar->_cal, UCAL_YEAR, limit, &status); |
| } else { |
| ucal_add(calendar->_cal, bigField, 1, &status); |
| } |
| if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitYear == biggerUnit) { |
| ucal_add(calendar->_cal, UCAL_SECOND, -1, &status); |
| range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); |
| while (1 == range.length) { |
| ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, -1, &status); |
| range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); |
| } |
| range.location = 1; |
| return range; |
| } else if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitMonth == biggerUnit) { |
| ucal_add(calendar->_cal, UCAL_SECOND, -1, &status); |
| range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); |
| range.location = 1; |
| return range; |
| } |
| UDate goal = ucal_getMillis(calendar->_cal, &status); |
| // Set calendar back to first instant of big unit |
| ucal_setMillis(calendar->_cal, start, &status); |
| if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { |
| // roll day forward to first 'dow' |
| while (ucal_get(calendar->_cal, (kCFCalendarUnitMonth == biggerUnit) ? UCAL_WEEK_OF_MONTH : UCAL_WEEK_OF_YEAR, &status) != 1) { |
| ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); |
| } |
| while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) { |
| ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); |
| } |
| start = ucal_getMillis(calendar->_cal, &status); |
| goal -= 1000; |
| range.location = 1; // constant here works around ICU -- see 3948293 |
| } |
| UDate curr = start; |
| range.length = (kCFCalendarUnitWeekdayOrdinal == smallerUnit) ? 1 : 0; |
| const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; |
| int multiple = (1 << multiple_table[flsl(smallerUnit) - 1]); |
| Boolean divide = false, alwaysDivide = false; |
| while (curr < goal) { |
| ucal_add(calendar->_cal, smallField, multiple, &status); |
| UDate newcurr = ucal_getMillis(calendar->_cal, &status); |
| if (curr < newcurr && newcurr <= goal) { |
| range.length += multiple; |
| curr = newcurr; |
| } else { |
| // Either newcurr is going backwards, or not making |
| // progress, or has overshot the goal; reset date |
| // and try smaller multiples. |
| ucal_setMillis(calendar->_cal, curr, &status); |
| divide = true; |
| // once we start overshooting the goal, the add at |
| // smaller multiples will succeed at most once for |
| // each multiple, so we reduce it every time through |
| // the loop. |
| if (goal < newcurr) alwaysDivide = true; |
| } |
| if (divide) { |
| multiple = multiple / 2; |
| if (0 == multiple) break; |
| divide = alwaysDivide; |
| } |
| } |
| } |
| return range; |
| } |
| |
| #if __GNUC__ |
| static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) __attribute__((noinline)); |
| #endif |
| static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { |
| CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange, calendar, "_rangeOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| CFRange range = {kCFNotFound, kCFNotFound}; |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| switch (smallerUnit) { |
| case kCFCalendarUnitSecond: |
| switch (biggerUnit) { |
| case kCFCalendarUnitMinute: |
| case kCFCalendarUnitHour: |
| case kCFCalendarUnitDay: |
| case kCFCalendarUnitWeekday: |
| case kCFCalendarUnitWeek: |
| case kCFCalendarUnitMonth: |
| case kCFCalendarUnitYear: |
| case kCFCalendarUnitEra: |
| // goto calculate; |
| range.location = 0; |
| range.length = 60; |
| break; |
| } |
| break; |
| case kCFCalendarUnitMinute: |
| switch (biggerUnit) { |
| case kCFCalendarUnitHour: |
| case kCFCalendarUnitDay: |
| case kCFCalendarUnitWeekday: |
| case kCFCalendarUnitWeek: |
| case kCFCalendarUnitMonth: |
| case kCFCalendarUnitYear: |
| case kCFCalendarUnitEra: |
| // goto calculate; |
| range.location = 0; |
| range.length = 60; |
| break; |
| } |
| break; |
| case kCFCalendarUnitHour: |
| switch (biggerUnit) { |
| case kCFCalendarUnitDay: |
| case kCFCalendarUnitWeekday: |
| case kCFCalendarUnitWeek: |
| case kCFCalendarUnitMonth: |
| case kCFCalendarUnitYear: |
| case kCFCalendarUnitEra: |
| // goto calculate; |
| range.location = 0; |
| range.length = 24; |
| break; |
| } |
| break; |
| case kCFCalendarUnitDay: |
| switch (biggerUnit) { |
| case kCFCalendarUnitWeek: |
| case kCFCalendarUnitMonth: |
| case kCFCalendarUnitYear: |
| case kCFCalendarUnitEra: |
| goto calculate; |
| break; |
| } |
| break; |
| case kCFCalendarUnitWeekday: |
| switch (biggerUnit) { |
| case kCFCalendarUnitWeek: |
| case kCFCalendarUnitMonth: |
| case kCFCalendarUnitYear: |
| case kCFCalendarUnitEra: |
| goto calculate; |
| break; |
| } |
| break; |
| case kCFCalendarUnitWeekdayOrdinal: |
| switch (biggerUnit) { |
| case kCFCalendarUnitMonth: |
| case kCFCalendarUnitYear: |
| case kCFCalendarUnitEra: |
| goto calculate; |
| break; |
| } |
| break; |
| case kCFCalendarUnitWeek: |
| switch (biggerUnit) { |
| case kCFCalendarUnitMonth: |
| case kCFCalendarUnitYear: |
| case kCFCalendarUnitEra: |
| goto calculate; |
| break; |
| } |
| break; |
| case kCFCalendarUnitMonth: |
| switch (biggerUnit) { |
| case kCFCalendarUnitYear: |
| case kCFCalendarUnitEra: |
| goto calculate; |
| break; |
| } |
| break; |
| case kCFCalendarUnitYear: |
| switch (biggerUnit) { |
| case kCFCalendarUnitEra: |
| goto calculate; |
| break; |
| } |
| break; |
| case kCFCalendarUnitEra: |
| break; |
| } |
| } |
| return range; |
| |
| calculate:; |
| ucal_clear(calendar->_cal); |
| UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit); |
| UCalendarDateFields bigField = __CFCalendarGetICUFieldCode(biggerUnit); |
| UCalendarDateFields yearField = __CFCalendarGetICUFieldCode(kCFCalendarUnitYear); |
| UCalendarDateFields fieldToAdd = smallField; |
| if (kCFCalendarUnitWeekday == smallerUnit) { |
| fieldToAdd = __CFCalendarGetICUFieldCode(kCFCalendarUnitDay); |
| } |
| int32_t dow = -1; |
| if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { |
| UErrorCode status = U_ZERO_ERROR; |
| UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); |
| ucal_setMillis(calendar->_cal, udate, &status); |
| dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); |
| fieldToAdd = __CFCalendarGetICUFieldCode(kCFCalendarUnitWeek); |
| } |
| // Set calendar to first instant of big unit |
| __CFCalendarSetToFirstInstant(calendar, biggerUnit, at); |
| if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { |
| UErrorCode status = U_ZERO_ERROR; |
| // roll day forward to first 'dow' |
| while (ucal_get(calendar->_cal, (kCFCalendarUnitMonth == biggerUnit) ? UCAL_WEEK_OF_MONTH : UCAL_WEEK_OF_YEAR, &status) != 1) { |
| ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); |
| } |
| while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) { |
| ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); |
| } |
| } |
| int32_t minSmallValue = INT32_MAX; |
| int32_t maxSmallValue = INT32_MIN; |
| UErrorCode status = U_ZERO_ERROR; |
| int32_t bigValue = ucal_get(calendar->_cal, bigField, &status); |
| for (;;) { |
| int32_t smallValue = ucal_get(calendar->_cal, smallField, &status); |
| if (smallValue < minSmallValue) minSmallValue = smallValue; |
| if (smallValue > maxSmallValue) maxSmallValue = smallValue; |
| ucal_add(calendar->_cal, fieldToAdd, 1, &status); |
| if (bigValue != ucal_get(calendar->_cal, bigField, &status)) break; |
| if (biggerUnit == kCFCalendarUnitEra && ucal_get(calendar->_cal, yearField, &status) > 10000) break; |
| // we assume an answer for 10000 years can be extrapolated to 100000 years, to save time |
| } |
| status = U_ZERO_ERROR; |
| range.location = minSmallValue; |
| if (smallerUnit == kCFCalendarUnitMonth) range.location = 1; |
| range.length = maxSmallValue - minSmallValue + 1; |
| if (biggerUnit == kCFCalendarUnitEra && ucal_get(calendar->_cal, yearField, &status) > 10000) range.length = 100000; |
| |
| return range; |
| } |
| |
| CFRange CFCalendarGetRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { |
| if (_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) { |
| return __CFCalendarGetRangeOfUnit2(calendar, smallerUnit, biggerUnit, at); |
| } else { |
| return __CFCalendarGetRangeOfUnit1(calendar, smallerUnit, biggerUnit, at); |
| } |
| } |
| |
| CFIndex CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { |
| CFIndex result = kCFNotFound; |
| if (!__validUnits(smallerUnit, biggerUnit)) return result; |
| CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFIndex, calendar, "_ordinalityOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| UErrorCode status = U_ZERO_ERROR; |
| ucal_clear(calendar->_cal); |
| if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitYear == biggerUnit) { |
| UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); |
| ucal_setMillis(calendar->_cal, udate, &status); |
| int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); |
| return val; |
| } else if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitMonth == biggerUnit) { |
| UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); |
| ucal_setMillis(calendar->_cal, udate, &status); |
| int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_MONTH, &status); |
| return val; |
| } |
| UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit); |
| // Set calendar to first instant of big unit |
| __CFCalendarSetToFirstInstant(calendar, biggerUnit, at); |
| UDate curr = ucal_getMillis(calendar->_cal, &status); |
| UDate goal = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); |
| result = 1; |
| const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; |
| int multiple = (1 << multiple_table[flsl(smallerUnit) - 1]); |
| Boolean divide = false, alwaysDivide = false; |
| while (curr < goal) { |
| ucal_add(calendar->_cal, smallField, multiple, &status); |
| UDate newcurr = ucal_getMillis(calendar->_cal, &status); |
| if (curr < newcurr && newcurr <= goal) { |
| result += multiple; |
| curr = newcurr; |
| } else { |
| // Either newcurr is going backwards, or not making |
| // progress, or has overshot the goal; reset date |
| // and try smaller multiples. |
| ucal_setMillis(calendar->_cal, curr, &status); |
| divide = true; |
| // once we start overshooting the goal, the add at |
| // smaller multiples will succeed at most once for |
| // each multiple, so we reduce it every time through |
| // the loop. |
| if (goal < newcurr) alwaysDivide = true; |
| } |
| if (divide) { |
| multiple = multiple / 2; |
| if (0 == multiple) break; |
| divide = alwaysDivide; |
| } |
| } |
| } |
| return result; |
| } |
| |
| Boolean _CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, int *vector, int count) { |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| UErrorCode status = U_ZERO_ERROR; |
| ucal_clear(calendar->_cal); |
| ucal_set(calendar->_cal, UCAL_YEAR, 1); |
| ucal_set(calendar->_cal, UCAL_MONTH, 0); |
| ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, 1); |
| ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, 0); |
| ucal_set(calendar->_cal, UCAL_MINUTE, 0); |
| ucal_set(calendar->_cal, UCAL_SECOND, 0); |
| const char *desc = componentDesc; |
| Boolean doWOY = false; |
| char ch = *desc; |
| while (ch) { |
| UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); |
| if (UCAL_WEEK_OF_YEAR == field) { |
| doWOY = true; |
| } |
| desc++; |
| ch = *desc; |
| } |
| desc = componentDesc; |
| ch = *desc; |
| while (ch) { |
| UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); |
| int value = *vector; |
| if (UCAL_YEAR == field && doWOY) field = UCAL_YEAR_WOY; |
| if (UCAL_MONTH == field) value--; |
| ucal_set(calendar->_cal, field, value); |
| vector++; |
| desc++; |
| ch = *desc; |
| } |
| UDate udate = ucal_getMillis(calendar->_cal, &status); |
| CFAbsoluteTime at = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970; |
| if (atp) *atp = at; |
| return U_SUCCESS(status) ? true : false; |
| } |
| return false; |
| } |
| |
| Boolean _CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, int **vector, int count) { |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| UErrorCode status = U_ZERO_ERROR; |
| ucal_clear(calendar->_cal); |
| UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); |
| ucal_setMillis(calendar->_cal, udate, &status); |
| char ch = *componentDesc; |
| while (ch) { |
| UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); |
| int value = ucal_get(calendar->_cal, field, &status); |
| if (UCAL_MONTH == field) value++; |
| *(*vector) = value; |
| vector++; |
| componentDesc++; |
| ch = *componentDesc; |
| } |
| return U_SUCCESS(status) ? true : false; |
| } |
| return false; |
| } |
| |
| Boolean _CFCalendarAddComponentsV(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, int *vector, int count) { |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| UErrorCode status = U_ZERO_ERROR; |
| ucal_clear(calendar->_cal); |
| UDate udate = floor((*atp + kCFAbsoluteTimeIntervalSince1970) * 1000.0); |
| ucal_setMillis(calendar->_cal, udate, &status); |
| char ch = *componentDesc; |
| while (ch) { |
| UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); |
| int amount = *vector; |
| if (options & kCFCalendarComponentsWrap) { |
| ucal_roll(calendar->_cal, field, amount, &status); |
| } else { |
| ucal_add(calendar->_cal, field, amount, &status); |
| } |
| vector++; |
| componentDesc++; |
| ch = *componentDesc; |
| } |
| udate = ucal_getMillis(calendar->_cal, &status); |
| *atp = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970; |
| return U_SUCCESS(status) ? true : false; |
| } |
| return false; |
| } |
| |
| Boolean _CFCalendarGetComponentDifferenceV(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, int **vector, int count) { |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| UErrorCode status = U_ZERO_ERROR; |
| ucal_clear(calendar->_cal); |
| UDate curr = floor((startingAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0); |
| UDate goal = floor((resultAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0); |
| ucal_setMillis(calendar->_cal, curr, &status); |
| int direction = (startingAT <= resultAT) ? 1 : -1; |
| char ch = *componentDesc; |
| while (ch) { |
| UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); |
| const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; |
| int multiple = direction * (1 << multiple_table[flsl(__CFCalendarGetCalendarUnitFromChar(ch)) - 1]); |
| Boolean divide = false, alwaysDivide = false; |
| int result = 0; |
| while ((direction > 0 && curr < goal) || (direction < 0 && goal < curr)) { |
| ucal_add(calendar->_cal, field, multiple, &status); |
| UDate newcurr = ucal_getMillis(calendar->_cal, &status); |
| if ((direction > 0 && curr < newcurr && newcurr <= goal) || (direction < 0 && newcurr < curr && goal <= newcurr)) { |
| result += multiple; |
| curr = newcurr; |
| } else { |
| // Either newcurr is going backwards, or not making |
| // progress, or has overshot the goal; reset date |
| // and try smaller multiples. |
| ucal_setMillis(calendar->_cal, curr, &status); |
| divide = true; |
| // once we start overshooting the goal, the add at |
| // smaller multiples will succeed at most once for |
| // each multiple, so we reduce it every time through |
| // the loop. |
| if ((direction > 0 && goal < newcurr) || (direction < 0 && newcurr < goal)) alwaysDivide = true; |
| } |
| if (divide) { |
| multiple = multiple / 2; |
| if (0 == multiple) break; |
| divide = alwaysDivide; |
| } |
| } |
| *(*vector) = result; |
| vector++; |
| componentDesc++; |
| ch = *componentDesc; |
| } |
| return U_SUCCESS(status) ? true : false; |
| } |
| return false; |
| } |
| |
| Boolean CFCalendarComposeAbsoluteTime(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, ...) { |
| va_list args; |
| va_start(args, componentDesc); |
| CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean, calendar, "_composeAbsoluteTime:::", atp, componentDesc, args); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| int idx, cnt = (int)strlen((char *)componentDesc); |
| #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
| STACK_BUFFER_DECL(int, vector, cnt); |
| #else |
| int vector[256]; // Dynamic stack allocation is GNU specific |
| #endif |
| for (idx = 0; idx < cnt; idx++) { |
| int arg = va_arg(args, int); |
| vector[idx] = arg; |
| } |
| va_end(args); |
| return _CFCalendarComposeAbsoluteTimeV(calendar, atp, componentDesc, vector, cnt); |
| } |
| |
| Boolean CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, ...) { |
| va_list args; |
| va_start(args, componentDesc); |
| CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean, calendar, "_decomposeAbsoluteTime:::", at, componentDesc, args); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| int idx, cnt = (int)strlen((char *)componentDesc); |
| #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
| STACK_BUFFER_DECL(int *, vector, cnt); |
| #else |
| int* vector[256]; // Dynamic stack allocation is GNU specific |
| #endif |
| for (idx = 0; idx < cnt; idx++) { |
| int *arg = va_arg(args, int *); |
| vector[idx] = arg; |
| } |
| va_end(args); |
| return _CFCalendarDecomposeAbsoluteTimeV(calendar, at, componentDesc, vector, cnt); |
| } |
| |
| Boolean CFCalendarAddComponents(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, ...) { |
| va_list args; |
| va_start(args, componentDesc); |
| CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean, calendar, "_addComponents::::", atp, options, componentDesc, args); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| int idx, cnt = (int)strlen((char *)componentDesc); |
| #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
| STACK_BUFFER_DECL(int, vector, cnt); |
| #else |
| int vector[256]; // Dynamic stack allocation is GNU specific |
| #endif |
| for (idx = 0; idx < cnt; idx++) { |
| int arg = va_arg(args, int); |
| vector[idx] = arg; |
| } |
| va_end(args); |
| return _CFCalendarAddComponentsV(calendar, atp, options, componentDesc, vector, cnt); |
| } |
| |
| Boolean CFCalendarGetComponentDifference(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, ...) { |
| va_list args; |
| va_start(args, componentDesc); |
| CF_OBJC_FUNCDISPATCH5(CFCalendarGetTypeID(), Boolean, calendar, "_diffComponents:::::", startingAT, resultAT, options, componentDesc, args); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| int idx, cnt = (int)strlen((char *)componentDesc); |
| #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
| STACK_BUFFER_DECL(int *, vector, cnt); |
| #else |
| int* vector[256]; // Dynamic stack allocation is GNU specific |
| #endif |
| for (idx = 0; idx < cnt; idx++) { |
| int *arg = va_arg(args, int *); |
| vector[idx] = arg; |
| } |
| va_end(args); |
| Boolean ret = _CFCalendarGetComponentDifferenceV(calendar, startingAT, resultAT, options, componentDesc, vector, cnt); |
| return ret; |
| } |
| |
| Boolean CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at, CFAbsoluteTime *startp, CFTimeInterval *tip) { |
| CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean, calendar, "_rangeOfUnit:startTime:interval:forAT:", unit, startp, tip, at); |
| __CFGenericValidateType(calendar, CFCalendarGetTypeID()); |
| if (kCFCalendarUnitWeekdayOrdinal == unit) return false; |
| if (kCFCalendarUnitWeekday == unit) unit = kCFCalendarUnitDay; |
| if (!calendar->_cal) __CFCalendarSetupCal(calendar); |
| if (calendar->_cal) { |
| ucal_clear(calendar->_cal); |
| __CFCalendarSetToFirstInstant(calendar, unit, at); |
| UErrorCode status = U_ZERO_ERROR; |
| UDate start = ucal_getMillis(calendar->_cal, &status); |
| UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit); |
| ucal_add(calendar->_cal, field, 1, &status); |
| UDate end = ucal_getMillis(calendar->_cal, &status); |
| if (end == start && kCFCalendarUnitEra == unit) { |
| // ICU refuses to do the addition, probably because we are |
| // at the limit of UCAL_ERA. Use alternate strategy. |
| CFIndex limit = ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_MAXIMUM, &status); |
| if (100000 < limit) limit = 100000; |
| ucal_add(calendar->_cal, UCAL_YEAR, limit, &status); |
| end = ucal_getMillis(calendar->_cal, &status); |
| } |
| if (U_SUCCESS(status)) { |
| if (startp) *startp = (double)start / 1000.0 - kCFAbsoluteTimeIntervalSince1970; |
| if (tip) *tip = (double)(end - start) / 1000.0; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| #undef BUFFER_SIZE |
| |