diff --git a/Source/CFCalendar.c b/Source/CFCalendar.c index 3c44716..3c7507c 100644 --- a/Source/CFCalendar.c +++ b/Source/CFCalendar.c @@ -795,18 +795,79 @@ CFCalendarGetTimeRangeOfUnit (CFCalendarRef cal, CFCalendarUnit unit, return true; } +/* The ICU field that represents smallerUnit measured within biggerUnit. */ +static UCalendarDateFields +CFCalendarCompoundField (CFCalendarUnit smaller, CFCalendarUnit bigger) +{ + switch (smaller) + { + case kCFCalendarUnitDay: + case kCFCalendarUnitWeekday: + if (bigger == kCFCalendarUnitYear) + return UCAL_DAY_OF_YEAR; + if (bigger == kCFCalendarUnitWeek) + return UCAL_DAY_OF_WEEK; + return UCAL_DATE; + case kCFCalendarUnitMonth: + return UCAL_MONTH; + case kCFCalendarUnitWeek: + if (bigger == kCFCalendarUnitMonth) + return UCAL_WEEK_OF_MONTH; + return UCAL_WEEK_OF_YEAR; + case kCFCalendarUnitHour: + return UCAL_HOUR_OF_DAY; + case kCFCalendarUnitMinute: + return UCAL_MINUTE; + case kCFCalendarUnitSecond: + return UCAL_SECOND; + default: + return (UCalendarDateFields) -1; + } +} + CFRange CFCalendarGetRangeOfUnit (CFCalendarRef cal, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { - return CFRangeMake (kCFNotFound, kCFNotFound); + UCalendarDateFields field; + UErrorCode err = U_ZERO_ERROR; + int32_t min, max; + + field = CFCalendarCompoundField (smallerUnit, biggerUnit); + if (field == (UCalendarDateFields) -1) + return CFRangeMake (kCFNotFound, kCFNotFound); + + CFCalendarOpenUCalendar (cal); + ucal_setMillis (cal->_ucal, ABSOLUTETIME_TO_UDATE (at), &err); + min = ucal_getLimit (cal->_ucal, field, UCAL_ACTUAL_MINIMUM, &err); + max = ucal_getLimit (cal->_ucal, field, UCAL_ACTUAL_MAXIMUM, &err); + if (U_FAILURE (err)) + return CFRangeMake (kCFNotFound, kCFNotFound); + + /* ICU numbers months from zero. */ + return CFRangeMake (field == UCAL_MONTH ? min + 1 : min, max - min + 1); } CFIndex CFCalendarGetOrdinalityOfUnit (CFCalendarRef cal, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { - return kCFNotFound; + UCalendarDateFields field; + UErrorCode err = U_ZERO_ERROR; + int32_t value; + + field = CFCalendarCompoundField (smallerUnit, biggerUnit); + if (field == (UCalendarDateFields) -1) + return kCFNotFound; + + CFCalendarOpenUCalendar (cal); + ucal_setMillis (cal->_ucal, ABSOLUTETIME_TO_UDATE (at), &err); + value = ucal_get (cal->_ucal, field, &err); + if (U_FAILURE (err)) + return kCFNotFound; + + /* ICU numbers months from zero. */ + return field == UCAL_MONTH ? value + 1 : value; } static CFRange diff --git a/Tests/CFCalendar/ordinality.m b/Tests/CFCalendar/ordinality.m new file mode 100644 index 0000000..1e2ca5a --- /dev/null +++ b/Tests/CFCalendar/ordinality.m @@ -0,0 +1,41 @@ +#include "CoreFoundation/CFCalendar.h" +#include "CoreFoundation/CFTimeZone.h" +#include "../CFTesting.h" + +/* CFCalendarGetOrdinalityOfUnit and CFCalendarGetRangeOfUnit. Times are + built directly as offsets from the reference date. */ + +int main (void) +{ + double day = 86400.0; + CFTimeZoneRef gmt = CFTimeZoneCreateWithTimeIntervalFromGMT (NULL, 0.0); + CFCalendarRef cal = CFCalendarCreateWithIdentifier (NULL, + kCFGregorianCalendar); + CFRange r; + + CFCalendarSetTimeZone (cal, gmt); + + r = CFCalendarGetRangeOfUnit (cal, kCFCalendarUnitDay, kCFCalendarUnitMonth, + 45 * day); /* 2001-02-15 */ + PASS_CF (r.location == 1 && r.length == 28, + "The days of February 2001 range from 1 to 28."); + + r = CFCalendarGetRangeOfUnit (cal, kCFCalendarUnitDay, kCFCalendarUnitMonth, + 63 * day); /* 2001-03-05 */ + PASS_CF (r.location == 1 && r.length == 31, + "The days of March 2001 range from 1 to 31."); + + PASS_CF (CFCalendarGetOrdinalityOfUnit (cal, kCFCalendarUnitDay, + kCFCalendarUnitMonth, 45 * day) == 15, + "2001-02-15 is the 15th day of the month."); + PASS_CF (CFCalendarGetOrdinalityOfUnit (cal, kCFCalendarUnitDay, + kCFCalendarUnitYear, 45 * day) == 46, + "2001-02-15 is the 46th day of the year."); + PASS_CF (CFCalendarGetOrdinalityOfUnit (cal, kCFCalendarUnitMonth, + kCFCalendarUnitYear, 45 * day) == 2, + "2001-02-15 is in the 2nd month of the year."); + + CFRelease (cal); + CFRelease (gmt); + return 0; +}