2 * Copyright (c) 2012 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1998-2011, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
29 #include <CoreFoundation/CFDate.h>
30 #include <CoreFoundation/CFTimeZone.h>
31 #include <CoreFoundation/CFDictionary.h>
32 #include <CoreFoundation/CFArray.h>
33 #include <CoreFoundation/CFString.h>
34 #include <CoreFoundation/CFNumber.h>
35 #include "CFInternal.h"
37 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX
39 #elif DEPLOYMENT_TARGET_WINDOWS
41 #error Unknown or unspecified DEPLOYMENT_TARGET
45 /* cjk: The Julian Date for the reference date is 2451910.5,
46 I think, in case that's ever useful. */
48 #define DEFINE_CFDATE_FUNCTIONS 1
50 #if DEFINE_CFDATE_FUNCTIONS
52 const CFTimeInterval kCFAbsoluteTimeIntervalSince1970
= 978307200.0L;
53 const CFTimeInterval kCFAbsoluteTimeIntervalSince1904
= 3061152000.0L;
55 __private_extern__
double __CFTSRRate
= 0.0;
56 static double __CF1_TSRRate
= 0.0;
58 __private_extern__
int64_t __CFTimeIntervalToTSR(CFTimeInterval ti
) {
59 if ((ti
* __CFTSRRate
) > INT64_MAX
/ 2) return (INT64_MAX
/ 2);
60 return (int64_t)(ti
* __CFTSRRate
);
63 __private_extern__ CFTimeInterval
__CFTSRToTimeInterval(int64_t tsr
) {
64 return (CFTimeInterval
)((double)tsr
* __CF1_TSRRate
);
67 CFAbsoluteTime
CFAbsoluteTimeGetCurrent(void) {
69 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX
71 gettimeofday(&tv
, NULL
);
72 ret
= (CFTimeInterval
)tv
.tv_sec
- kCFAbsoluteTimeIntervalSince1970
;
73 ret
+= (1.0E-6 * (CFTimeInterval
)tv
.tv_usec
);
74 #elif DEPLOYMENT_TARGET_WINDOWS
76 GetSystemTimeAsFileTime(&ft
);
77 ret
= _CFAbsoluteTimeFromFileTime(&ft
);
79 #error Unknown or unspecified DEPLOYMENT_TARGET
84 __private_extern__
void __CFDateInitialize(void) {
85 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
86 struct mach_timebase_info info
;
87 mach_timebase_info(&info
);
88 __CFTSRRate
= (1.0E9
/ (double)info
.numer
) * (double)info
.denom
;
89 __CF1_TSRRate
= 1.0 / __CFTSRRate
;
90 #elif DEPLOYMENT_TARGET_WINDOWS
92 if (!QueryPerformanceFrequency(&freq
)) {
95 __CFTSRRate
= (double)freq
.QuadPart
;
96 __CF1_TSRRate
= 1.0 / __CFTSRRate
;
97 #elif DEPLOYMENT_TARGET_LINUX
99 if (!clock_getres(CLOCK_MONOTONIC
, &res
)) {
102 __CFTSRRate
= res
.tv_sec
+ (1000000000 * res
.tv_nsec
);
103 __CF1_TSRRate
= 1.0 / __CFTSRRate
;
105 #error Unknown or unspecified DEPLOYMENT_TARGET
107 CFDateGetTypeID(); // cause side-effects
113 CFAbsoluteTime _time
; /* immutable */
116 static Boolean
__CFDateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
117 CFDateRef date1
= (CFDateRef
)cf1
;
118 CFDateRef date2
= (CFDateRef
)cf2
;
119 if (date1
->_time
!= date2
->_time
) return false;
123 static CFHashCode
__CFDateHash(CFTypeRef cf
) {
124 CFDateRef date
= (CFDateRef
)cf
;
125 return (CFHashCode
)(float)floor(date
->_time
);
128 static CFStringRef
__CFDateCopyDescription(CFTypeRef cf
) {
129 CFDateRef date
= (CFDateRef
)cf
;
130 return CFStringCreateWithFormat(CFGetAllocator(date
), NULL
, CFSTR("<CFDate %p [%p]>{time = %0.09g}"), cf
, CFGetAllocator(date
), date
->_time
);
133 static CFTypeID __kCFDateTypeID
= _kCFRuntimeNotATypeID
;
135 static const CFRuntimeClass __CFDateClass
= {
144 __CFDateCopyDescription
147 CFTypeID
CFDateGetTypeID(void) {
148 if (_kCFRuntimeNotATypeID
== __kCFDateTypeID
) __kCFDateTypeID
= _CFRuntimeRegisterClass(&__CFDateClass
);
149 return __kCFDateTypeID
;
152 CFDateRef
CFDateCreate(CFAllocatorRef allocator
, CFAbsoluteTime at
) {
155 size
= sizeof(struct __CFDate
) - sizeof(CFRuntimeBase
);
156 memory
= (CFDateRef
)_CFRuntimeCreateInstance(allocator
, CFDateGetTypeID(), size
, NULL
);
157 if (NULL
== memory
) {
160 ((struct __CFDate
*)memory
)->_time
= at
;
164 CFTimeInterval
CFDateGetAbsoluteTime(CFDateRef date
) {
165 CF_OBJC_FUNCDISPATCH0(CFDateGetTypeID(), CFTimeInterval
, date
, "timeIntervalSinceReferenceDate");
166 __CFGenericValidateType(date
, CFDateGetTypeID());
170 CFTimeInterval
CFDateGetTimeIntervalSinceDate(CFDateRef date
, CFDateRef otherDate
) {
171 CF_OBJC_FUNCDISPATCH1(CFDateGetTypeID(), CFTimeInterval
, date
, "timeIntervalSinceDate:", otherDate
);
172 __CFGenericValidateType(date
, CFDateGetTypeID());
173 __CFGenericValidateType(otherDate
, CFDateGetTypeID());
174 return date
->_time
- otherDate
->_time
;
177 CFComparisonResult
CFDateCompare(CFDateRef date
, CFDateRef otherDate
, void *context
) {
178 CF_OBJC_FUNCDISPATCH1(CFDateGetTypeID(), CFComparisonResult
, date
, "compare:", otherDate
);
179 __CFGenericValidateType(date
, CFDateGetTypeID());
180 __CFGenericValidateType(otherDate
, CFDateGetTypeID());
181 if (date
->_time
< otherDate
->_time
) return kCFCompareLessThan
;
182 if (date
->_time
> otherDate
->_time
) return kCFCompareGreaterThan
;
183 return kCFCompareEqualTo
;
189 CF_INLINE
int32_t __CFDoubleModToInt(double d
, int32_t modulus
) {
190 int32_t result
= (int32_t)(float)floor(d
- floor(d
/ modulus
) * modulus
);
191 if (result
< 0) result
+= modulus
;
195 CF_INLINE
double __CFDoubleMod(double d
, int32_t modulus
) {
196 double result
= d
- floor(d
/ modulus
) * modulus
;
197 if (result
< 0.0) result
+= (double)modulus
;
201 static const uint8_t daysInMonth
[16] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0, 0, 0};
202 static const uint16_t daysBeforeMonth
[16] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0};
203 static const uint16_t daysAfterMonth
[16] = {365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0, 0, 0, 0};
205 CF_INLINE
bool isleap(int64_t year
) {
206 int64_t y
= (year
+ 1) % 400; /* correct to nearest multiple-of-400 year, then find the remainder */
208 return (0 == (y
& 3) && 100 != y
&& 200 != y
&& 300 != y
);
211 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
212 CF_INLINE
uint8_t __CFDaysInMonth(int8_t month
, int64_t year
, bool leap
) {
213 return daysInMonth
[month
] + (2 == month
&& leap
);
216 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
217 CF_INLINE
uint16_t __CFDaysBeforeMonth(int8_t month
, int64_t year
, bool leap
) {
218 return daysBeforeMonth
[month
] + (2 < month
&& leap
);
221 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
222 CF_INLINE
uint16_t __CFDaysAfterMonth(int8_t month
, int64_t year
, bool leap
) {
223 return daysAfterMonth
[month
] + (month
< 2 && leap
);
226 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
227 static void __CFYMDFromAbsolute(int64_t absolute
, int64_t *year
, int8_t *month
, int8_t *day
) {
228 int64_t b
= absolute
/ 146097; // take care of as many multiples of 400 years as possible
231 absolute
-= b
* 146097;
232 while (absolute
< 0) {
234 absolute
+= __CFDaysAfterMonth(0, y
, isleap(y
));
236 /* Now absolute is non-negative days to add to year */
237 ydays
= __CFDaysAfterMonth(0, y
, isleap(y
));
238 while (ydays
<= absolute
) {
241 ydays
= __CFDaysAfterMonth(0, y
, isleap(y
));
243 /* Now we have year and days-into-year */
246 int8_t m
= absolute
/ 33 + 1; /* search from the approximation */
247 bool leap
= isleap(y
);
248 while (__CFDaysBeforeMonth(m
+ 1, y
, leap
) <= absolute
) m
++;
249 if (month
) *month
= m
;
250 if (day
) *day
= absolute
- __CFDaysBeforeMonth(m
, y
, leap
) + 1;
254 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
255 static double __CFAbsoluteFromYMD(int64_t year
, int8_t month
, int8_t day
) {
256 double absolute
= 0.0;
258 int64_t b
= year
/ 400; // take care of as many multiples of 400 years as possible
259 absolute
+= b
* 146097.0;
262 for (idx
= year
; idx
< 0; idx
++)
263 absolute
-= __CFDaysAfterMonth(0, idx
, isleap(idx
));
265 for (idx
= 0; idx
< year
; idx
++)
266 absolute
+= __CFDaysAfterMonth(0, idx
, isleap(idx
));
268 /* Now add the days into the original year */
269 absolute
+= __CFDaysBeforeMonth(month
, year
, isleap(year
)) + day
- 1;
273 Boolean
CFGregorianDateIsValid(CFGregorianDate gdate
, CFOptionFlags unitFlags
) {
274 if ((unitFlags
& kCFGregorianUnitsYears
) && (gdate
.year
<= 0)) return false;
275 if ((unitFlags
& kCFGregorianUnitsMonths
) && (gdate
.month
< 1 || 12 < gdate
.month
)) return false;
276 if ((unitFlags
& kCFGregorianUnitsDays
) && (gdate
.day
< 1 || 31 < gdate
.day
)) return false;
277 if ((unitFlags
& kCFGregorianUnitsHours
) && (gdate
.hour
< 0 || 23 < gdate
.hour
)) return false;
278 if ((unitFlags
& kCFGregorianUnitsMinutes
) && (gdate
.minute
< 0 || 59 < gdate
.minute
)) return false;
279 if ((unitFlags
& kCFGregorianUnitsSeconds
) && (gdate
.second
< 0.0 || 60.0 <= gdate
.second
)) return false;
280 if ((unitFlags
& kCFGregorianUnitsDays
) && (unitFlags
& kCFGregorianUnitsMonths
) && (unitFlags
& kCFGregorianUnitsYears
) && (__CFDaysInMonth(gdate
.month
, gdate
.year
- 2001, isleap(gdate
.year
- 2001)) < gdate
.day
)) return false;
284 CFAbsoluteTime
CFGregorianDateGetAbsoluteTime(CFGregorianDate gdate
, CFTimeZoneRef tz
) {
286 CFTimeInterval offset0
, offset1
;
288 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
290 at
= 86400.0 * __CFAbsoluteFromYMD(gdate
.year
- 2001, gdate
.month
, gdate
.day
);
291 at
+= 3600.0 * gdate
.hour
+ 60.0 * gdate
.minute
+ gdate
.second
;
293 offset0
= CFTimeZoneGetSecondsFromGMT(tz
, at
);
294 offset1
= CFTimeZoneGetSecondsFromGMT(tz
, at
- offset0
);
300 CFGregorianDate
CFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
301 CFGregorianDate gdate
;
302 int64_t absolute
, year
;
304 CFAbsoluteTime fixedat
;
306 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
308 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
309 absolute
= (int64_t)floor(fixedat
/ 86400.0);
310 __CFYMDFromAbsolute(absolute
, &year
, &month
, &day
);
311 if (INT32_MAX
- 2001 < year
) year
= INT32_MAX
- 2001;
312 gdate
.year
= year
+ 2001;
315 gdate
.hour
= __CFDoubleModToInt(floor(fixedat
/ 3600.0), 24);
316 gdate
.minute
= __CFDoubleModToInt(floor(fixedat
/ 60.0), 60);
317 gdate
.second
= __CFDoubleMod(fixedat
, 60);
318 if (0.0 == gdate
.second
) gdate
.second
= 0.0; // stomp out possible -0.0
322 /* Note that the units of years and months are not equal length, but are treated as such. */
323 CFAbsoluteTime
CFAbsoluteTimeAddGregorianUnits(CFAbsoluteTime at
, CFTimeZoneRef tz
, CFGregorianUnits units
) {
324 CFGregorianDate gdate
;
325 CFGregorianUnits working
;
326 CFAbsoluteTime candidate_at0
, candidate_at1
;
330 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
333 /* Most people seem to expect years, then months, then days, etc.
334 to be added in that order. Thus, 27 April + (4 days, 1 month)
335 = 31 May, and not 1 June. This is also relatively predictable.
337 On another issue, months not being equal length, people also
338 seem to expect late day-of-month clamping (don't clamp as you
339 go through months), but clamp before adding in the days. Late
340 clamping is also more predictable given random starting points
341 and random numbers of months added (ie Jan 31 + 2 months could
342 be March 28 or March 29 in different years with aggressive
343 clamping). Proportionality (28 Feb + 1 month = 31 March) is
346 Also, people don't expect time zone transitions to have any
347 effect when adding years and/or months and/or days, only.
348 Hours, minutes, and seconds, though, are added in as humans
349 would experience the passing of that time. What this means
350 is that if the date, after adding years, months, and days
351 lands on some date, and then adding hours, minutes, and
352 seconds crosses a time zone transition, the time zone
353 transition is accounted for. If adding years, months, and
354 days gets the date into a different time zone offset period,
355 that transition is not taken into account.
357 gdate
= CFAbsoluteTimeGetGregorianDate(at
, tz
);
358 /* We must work in a CFGregorianUnits, because the fields in the CFGregorianDate can easily overflow */
359 working
.years
= gdate
.year
;
360 working
.months
= gdate
.month
;
361 working
.days
= gdate
.day
;
362 working
.years
+= units
.years
;
363 working
.months
+= units
.months
;
364 while (12 < working
.months
) {
365 working
.months
-= 12;
368 while (working
.months
< 1) {
369 working
.months
+= 12;
372 monthdays
= __CFDaysInMonth(working
.months
, working
.years
- 2001, isleap(working
.years
- 2001));
373 if (monthdays
< working
.days
) { /* Clamp day to new month */
374 working
.days
= monthdays
;
376 working
.days
+= units
.days
;
377 while (monthdays
< working
.days
) {
379 if (12 < working
.months
) {
380 working
.months
-= 12;
383 working
.days
-= monthdays
;
384 monthdays
= __CFDaysInMonth(working
.months
, working
.years
- 2001, isleap(working
.years
- 2001));
386 while (working
.days
< 1) {
388 if (working
.months
< 1) {
389 working
.months
+= 12;
392 monthdays
= __CFDaysInMonth(working
.months
, working
.years
- 2001, isleap(working
.years
- 2001));
393 working
.days
+= monthdays
;
395 gdate
.year
= working
.years
;
396 gdate
.month
= working
.months
;
397 gdate
.day
= working
.days
;
398 /* Roll in hours, minutes, and seconds */
399 candidate_at0
= CFGregorianDateGetAbsoluteTime(gdate
, tz
);
400 candidate_at1
= candidate_at0
+ 3600.0 * units
.hours
+ 60.0 * units
.minutes
+ units
.seconds
;
401 /* If summing in the hours, minutes, and seconds delta pushes us
402 * into a new time zone offset, that will automatically be taken
403 * care of by the fact that we just add the raw time above. To
404 * undo that effect, we'd have to get the time zone offsets for
405 * candidate_at0 and candidate_at1 here, and subtract the
406 * difference (offset1 - offset0) from candidate_at1. */
407 return candidate_at1
;
410 /* at1 - at2. The only constraint here is that this needs to be the inverse
411 of CFAbsoluteTimeByAddingGregorianUnits(), but that's a very rigid constraint.
412 Unfortunately, due to the nonuniformity of the year and month units, this
413 inversion essentially has to approximate until it finds the answer. */
414 CFGregorianUnits
CFAbsoluteTimeGetDifferenceAsGregorianUnits(CFAbsoluteTime at1
, CFAbsoluteTime at2
, CFTimeZoneRef tz
, CFOptionFlags unitFlags
) {
415 const int32_t seconds
[5] = {366 * 24 * 3600, 31 * 24 * 3600, 24 * 3600, 3600, 60};
416 CFGregorianUnits units
= {0, 0, 0, 0, 0, 0.0};
417 CFAbsoluteTime atold
, atnew
= at2
;
419 incr
= (at2
< at1
) ? 1 : -1;
420 /* Successive approximation: years, then months, then days, then hours, then minutes. */
421 for (idx
= 0; idx
< 5; idx
++) {
422 if (unitFlags
& (1 << idx
)) {
423 ((int32_t *)&units
)[idx
] = -3 * incr
+ (int32_t)((at1
- atnew
) / seconds
[idx
]);
426 ((int32_t *)&units
)[idx
] += incr
;
427 atnew
= CFAbsoluteTimeAddGregorianUnits(at2
, tz
, units
);
428 } while ((1 == incr
&& atnew
<= at1
) || (-1 == incr
&& at1
<= atnew
));
429 ((int32_t *)&units
)[idx
] -= incr
;
433 if (unitFlags
& kCFGregorianUnitsSeconds
) {
434 units
.seconds
= at1
- atnew
;
436 if (0.0 == units
.seconds
) units
.seconds
= 0.0; // stomp out possible -0.0
440 SInt32
CFAbsoluteTimeGetDayOfWeek(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
442 CFAbsoluteTime fixedat
;
444 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
446 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
447 absolute
= (int64_t)floor(fixedat
/ 86400.0);
448 return (absolute
< 0) ? ((absolute
+ 1) % 7 + 7) : (absolute
% 7 + 1); /* Monday = 1, etc. */
451 SInt32
CFAbsoluteTimeGetDayOfYear(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
452 CFAbsoluteTime fixedat
;
453 int64_t absolute
, year
;
456 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
458 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
459 absolute
= (int64_t)floor(fixedat
/ 86400.0);
460 __CFYMDFromAbsolute(absolute
, &year
, &month
, &day
);
461 return __CFDaysBeforeMonth(month
, year
, isleap(year
)) + day
;
464 /* "the first week of a year is the one which includes the first Thursday" (ISO 8601) */
465 SInt32
CFAbsoluteTimeGetWeekOfYear(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
466 int64_t absolute
, year
;
468 CFAbsoluteTime fixedat
;
470 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
472 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
473 absolute
= (int64_t)floor(fixedat
/ 86400.0);
474 __CFYMDFromAbsolute(absolute
, &year
, &month
, &day
);
475 double absolute0101
= __CFAbsoluteFromYMD(year
, 1, 1);
476 int64_t dow0101
= __CFDoubleModToInt(absolute0101
, 7) + 1;
477 /* First three and last three days of a year can end up in a week of a different year */
478 if (1 == month
&& day
< 4) {
479 if ((day
< 4 && 5 == dow0101
) || (day
< 3 && 6 == dow0101
) || (day
< 2 && 7 == dow0101
)) {
483 if (12 == month
&& 28 < day
) {
484 double absolute20101
= __CFAbsoluteFromYMD(year
+ 1, 1, 1);
485 int64_t dow20101
= __CFDoubleModToInt(absolute20101
, 7) + 1;
486 if ((28 < day
&& 4 == dow20101
) || (29 < day
&& 3 == dow20101
) || (30 < day
&& 2 == dow20101
)) {
490 /* Days into year, plus a week-shifting correction, divided by 7. First week is 1. */
491 return (__CFDaysBeforeMonth(month
, year
, isleap(year
)) + day
+ (dow0101
- 11) % 7 + 2) / 7 + 1;