in CoreFoundation/Locale.subproj/CFCalendar.c [1497:1757]
Boolean CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at, CFAbsoluteTime *startp, CFTimeInterval *tip) {
__CFCalendarValidateAndCapTimeRange(at);
// Note: We do not toll-free bridge for Swift
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, (NSCalendar *)calendar, _rangeOfUnit:(NSCalendarUnit)unit startTime:startp interval:tip forAT:at);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
const CFTimeInterval inf_ti = 4398046511104.0;
CFStringRef ident = CFCalendarGetIdentifier(calendar);
switch (unit) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
case kCFCalendarUnitCalendar: return false;
case kCFCalendarUnitTimeZone: return false;
#pragma GCC diagnostic pop
case kCFCalendarUnitEra:;
if (kCFCalendarIdentifierGregorian == ident || kCFCalendarIdentifierISO8601 == ident) {
if (at < -63113904000.0) {
if (startp) *startp = -63113904000.0 - inf_ti;
} else {
if (startp) *startp = -63113904000.0;
}
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierRepublicOfChina == ident) {
if (at < -2808691200.0) {
if (startp) *startp = -2808691200.0 - inf_ti;
} else {
if (startp) *startp = -2808691200.0;
}
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierCoptic == ident) {
if (at < -54162518400.0) {
if (startp) *startp = -54162518400.0 - inf_ti;
} else {
if (startp) *startp = -54162518400.0;
}
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierBuddhist == ident) {
if (at < -80249875200.0) {
return false;
}
if (startp) *startp = -80249875200.0;
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierIslamic == ident) {
if (at < -43499980800.0) {
return false;
}
if (startp) *startp = -43499980800.0;
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierIslamicCivil == ident) {
if (at < -43499894400.0) {
return false;
}
if (startp) *startp = -43499894400.0;
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierIslamicTabular == ident) {
if (at < -43499980800.0) {
return false;
}
if (startp) *startp = -43499980800.0;
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierIslamicUmmAlQura == ident) {
if (at < -43499980800.0) {
return false;
}
if (startp) *startp = -43499980800.0;
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierHebrew == ident) {
if (at < -181778083200.0) {
return false;
}
if (startp) *startp = -181778083200.0;
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierPersian == ident) {
if (at < -43510176000.0) {
return false;
}
if (startp) *startp = -43510176000.0;
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierIndian == ident) {
if (at < -60645542400.0) {
return false;
}
if (startp) *startp = -60645542400.0;
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierEthiopicAmeteAlem == ident) {
if (at < -236439216000.0) {
return false;
}
if (startp) *startp = -236439216000.0;
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierEthiopicAmeteMihret == ident) {
if (at < -236439216000.0) {
return false;
}
if (at < -62872416000.0) {
if (startp) *startp = -236439216000.0;
if (tip) *tip = -62872416000.0 - -236439216000.0;
return true;
}
if (startp) *startp = -62872416000.0;
if (tip) *tip = inf_ti;
return true;
} else if (kCFCalendarIdentifierJapanese == ident) {
if (at < -42790982400.0) {
return false;
}
break;
} else if (kCFCalendarIdentifierChinese == ident) {
if (at < -146325744000.0) {
return false;
}
break;
}
break;
case kCFCalendarUnitYear: break;
case kCFCalendarUnitYearForWeekOfYear: break;
case kCFCalendarUnitQuarter: break;
case kCFCalendarUnitMonth: break;
case kCFCalendarUnitDay: break;
case kCFCalendarUnitHour:
{
CFTimeZoneRef timezone = CFCalendarCopyTimeZone(calendar);
CFTimeInterval ti = CFTimeZoneGetSecondsFromGMT(timezone, at);
CFRelease(timezone);
#if 1
CFTimeInterval fixedAT = at + ti; // compute local time
fixedAT = floor(fixedAT / 3600.0) * 3600.0;
fixedAT -= ti; // compute GMT
if (startp) *startp = fixedAT;
#else
NSTimeInterval secondsToSubtract = fmod(fmod(at + ti, 3600.0) + 3600.0, 3600.0); // amount of seconds into the current local time's hour
if (startp) *startp = at - secondsToSubtract;
#endif
if (tip) *tip = 3600.0;
return true;
}
case kCFCalendarUnitMinute:
if (startp) *startp = floor(at / 60.0) * 60.0;
if (tip) *tip = 60.0;
return true;
case kCFCalendarUnitSecond:
if (startp) *startp = floor(at);
if (tip) *tip = 1.0;
return true;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
case kCFCalendarUnitNanosecond:
if (startp) *startp = floor(at * 1.0e+9) * 1.0e-9;
if (tip) *tip = 1.0e-9;
return true;
#pragma GCC diagnostic pop
case kCFCalendarUnitWeek_Deprecated: break;
case kCFCalendarUnitWeekOfMonth: break;
case kCFCalendarUnitWeekOfYear: break;
case kCFCalendarUnitWeekdayOrdinal:
case kCFCalendarUnitWeekday:
unit = kCFCalendarUnitDay; break;
}
if (!calendar->_cal) {
__CFCalendarSetupCal(calendar);
if (!calendar->_cal) {
return false;
}
}
// Set UCalendar to first instant of unit prior to 'at'
__CFCalendarSetToFirstInstant(calendar, unit, at);
UErrorCode status = U_ZERO_ERROR;
UDate end = 0.0, start = __cficu_ucal_getMillis(calendar->_cal, &status);
CFAbsoluteTime start_at = start / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
if (tip) {
#pragma GCC diagnostic push // See 10693376
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch (unit) {
case kCFCalendarUnitEra: {
__cficu_ucal_add(calendar->_cal, UCAL_ERA, 1, &status);
UDate newdate = __cficu_ucal_getMillis(calendar->_cal, &status);
if (newdate == start) {
// ICU refused to do the addition, probably because we are
// at the limit of UCAL_ERA.
if (startp) *startp = start_at;
if (tip) *tip = inf_ti;
return true;
}
break;
}
case kCFCalendarUnitYear:
__cficu_ucal_add(calendar->_cal, UCAL_YEAR, 1, &status);
break;
case kCFCalendarUnitYearForWeekOfYear:
__cficu_ucal_add(calendar->_cal, UCAL_YEAR_WOY, 1, &status);
break;
case kCFCalendarUnitQuarter: {
// #warning adding 3 months and tacking any 13th month in the last quarter is not right for Hebrew
__cficu_ucal_add(calendar->_cal, UCAL_MONTH, 3, &status);
int32_t m = __cficu_ucal_get(calendar->_cal, UCAL_MONTH, &status);
if (12 == m) { // for calendars with 13 months
__cficu_ucal_add(calendar->_cal, UCAL_MONTH, 1, &status);
// workaround ICU bug with Coptic, Ethiopic calendars
int32_t d = __cficu_ucal_get(calendar->_cal, UCAL_DAY_OF_MONTH, &status);
int32_t d1 = __cficu_ucal_getLimit(calendar->_cal, UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MINIMUM, &status);
if (d != d1) {
__cficu_ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, d1);
}
}
break;
}
case kCFCalendarUnitMonth:
__cficu_ucal_add(calendar->_cal, UCAL_MONTH, 1, &status);
break;
case kCFCalendarUnitWeek_Deprecated:
__cficu_ucal_add(calendar->_cal, UCAL_WEEK_OF_YEAR, 1, &status);
break;
case kCFCalendarUnitWeekOfYear:
__cficu_ucal_add(calendar->_cal, UCAL_WEEK_OF_YEAR, 1, &status);
break;
case kCFCalendarUnitWeekOfMonth:
__cficu_ucal_add(calendar->_cal, UCAL_WEEK_OF_MONTH, 1, &status);
break;
case kCFCalendarUnitDay:
__cficu_ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status);
break;
}
#pragma GCC diagnostic pop // See 10693376
// move back to 0h0m0s, in case the start of the unit wasn't at 0h0m0s
__cficu_ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, __cficu_ucal_getLimit(calendar->_cal, UCAL_HOUR_OF_DAY, UCAL_ACTUAL_MINIMUM, &status));
__cficu_ucal_set(calendar->_cal, UCAL_MINUTE, __cficu_ucal_getLimit(calendar->_cal, UCAL_MINUTE, UCAL_ACTUAL_MINIMUM, &status));
__cficu_ucal_set(calendar->_cal, UCAL_SECOND, __cficu_ucal_getLimit(calendar->_cal, UCAL_SECOND, UCAL_ACTUAL_MINIMUM, &status));
__cficu_ucal_set(calendar->_cal, UCAL_MILLISECOND, 0);
status = U_ZERO_ERROR;
end = __cficu_ucal_getMillis(calendar->_cal, &status);
}
CFAbsoluteTime t, end_at;
CFTimeInterval length;
end_at = end / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
if (__CFCalendarGetTimeRangeOfTimeZoneTransition(calendar, end_at, &t, &length)) {
end = (end_at - length + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
}
if (startp) *startp = start_at;
if (tip) *tip = (end - start) / 1000.0;
return true;
}