2 * Copyright (c) 2009 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@
24 Copyright (c) 1998-2009, Apple Inc. All rights reserved.
25 Responsibility: Christopher Kane
28 #include <CoreFoundation/CFDate.h>
29 #include <CoreFoundation/CFTimeZone.h>
30 #include <CoreFoundation/CFDictionary.h>
31 #include <CoreFoundation/CFArray.h>
32 #include <CoreFoundation/CFString.h>
33 #include <CoreFoundation/CFNumber.h>
34 #include "CFInternal.h"
36 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
38 #elif DEPLOYMENT_TARGET_WINDOWS
40 #error Unknown or unspecified DEPLOYMENT_TARGET
44 /* cjk: The Julian Date for the reference date is 2451910.5,
45 I think, in case that's ever useful. */
48 const CFTimeInterval kCFAbsoluteTimeIntervalSince1970
= 978307200.0L;
49 const CFTimeInterval kCFAbsoluteTimeIntervalSince1904
= 3061152000.0L;
51 __private_extern__
double __CFTSRRate
= 0.0;
52 static double __CF1_TSRRate
= 0.0;
54 __private_extern__
int64_t __CFTimeIntervalToTSR(CFTimeInterval ti
) {
55 if ((ti
* __CFTSRRate
) > INT64_MAX
/ 2) return (INT64_MAX
/ 2);
56 return (int64_t)(ti
* __CFTSRRate
);
59 __private_extern__ CFTimeInterval
__CFTSRToTimeInterval(int64_t tsr
) {
60 return (CFTimeInterval
)((double)tsr
* __CF1_TSRRate
);
63 CFAbsoluteTime
CFAbsoluteTimeGetCurrent(void) {
65 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_IPHONE
67 gettimeofday(&tv
, NULL
);
68 ret
= (CFTimeInterval
)tv
.tv_sec
- kCFAbsoluteTimeIntervalSince1970
;
69 ret
+= (1.0E-6 * (CFTimeInterval
)tv
.tv_usec
);
70 #elif DEPLOYMENT_TARGET_WINDOWS_SYNC || DEPLOYMENT_TARGET_CODE011
72 GetSystemTimeAsFileTime(&ft
);
73 ret
= _CFAbsoluteTimeFromFileTime(&ft
);
75 #error Unknown or unspecified DEPLOYMENT_TARGET
80 __private_extern__
void __CFDateInitialize(void) {
81 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_IPHONE
82 struct mach_timebase_info info
;
83 mach_timebase_info(&info
);
84 __CFTSRRate
= (1.0E9
/ (double)info
.numer
) * (double)info
.denom
;
85 __CF1_TSRRate
= 1.0 / __CFTSRRate
;
86 #elif DEPLOYMENT_TARGET_WINDOWS_SYNC || DEPLOYMENT_TARGET_CODE011
88 if (!QueryPerformanceFrequency(&freq
)) {
91 __CFTSRRate
= (double)freq
.QuadPart
;
92 __CF1_TSRRate
= 1.0 / __CFTSRRate
;
94 #error Unknown or unspecified DEPLOYMENT_TARGET
96 CFDateGetTypeID(); // cause side-effects
102 CFAbsoluteTime _time
; /* immutable */
105 static Boolean
__CFDateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
106 CFDateRef date1
= (CFDateRef
)cf1
;
107 CFDateRef date2
= (CFDateRef
)cf2
;
108 if (date1
->_time
!= date2
->_time
) return false;
112 static CFHashCode
__CFDateHash(CFTypeRef cf
) {
113 CFDateRef date
= (CFDateRef
)cf
;
114 return (CFHashCode
)(float)floor(date
->_time
);
117 static CFStringRef
__CFDateCopyDescription(CFTypeRef cf
) {
118 CFDateRef date
= (CFDateRef
)cf
;
119 return CFStringCreateWithFormat(CFGetAllocator(date
), NULL
, CFSTR("<CFDate %p [%p]>{time = %0.09g}"), cf
, CFGetAllocator(date
), date
->_time
);
122 static CFTypeID __kCFDateTypeID
= _kCFRuntimeNotATypeID
;
124 static const CFRuntimeClass __CFDateClass
= {
133 __CFDateCopyDescription
136 CFTypeID
CFDateGetTypeID(void) {
137 if (_kCFRuntimeNotATypeID
== __kCFDateTypeID
) __kCFDateTypeID
= _CFRuntimeRegisterClass(&__CFDateClass
);
138 return __kCFDateTypeID
;
141 CFDateRef
CFDateCreate(CFAllocatorRef allocator
, CFAbsoluteTime at
) {
144 size
= sizeof(struct __CFDate
) - sizeof(CFRuntimeBase
);
145 memory
= (CFDateRef
)_CFRuntimeCreateInstance(allocator
, CFDateGetTypeID(), size
, NULL
);
146 if (NULL
== memory
) {
149 ((struct __CFDate
*)memory
)->_time
= at
;
153 CFTimeInterval
CFDateGetAbsoluteTime(CFDateRef date
) {
154 CF_OBJC_FUNCDISPATCH0(CFDateGetTypeID(), CFTimeInterval
, date
, "timeIntervalSinceReferenceDate");
155 __CFGenericValidateType(date
, CFDateGetTypeID());
159 CFTimeInterval
CFDateGetTimeIntervalSinceDate(CFDateRef date
, CFDateRef otherDate
) {
160 CF_OBJC_FUNCDISPATCH1(CFDateGetTypeID(), CFTimeInterval
, date
, "timeIntervalSinceDate:", otherDate
);
161 __CFGenericValidateType(date
, CFDateGetTypeID());
162 __CFGenericValidateType(otherDate
, CFDateGetTypeID());
163 return date
->_time
- otherDate
->_time
;
166 CFComparisonResult
CFDateCompare(CFDateRef date
, CFDateRef otherDate
, void *context
) {
167 CF_OBJC_FUNCDISPATCH1(CFDateGetTypeID(), CFComparisonResult
, date
, "compare:", otherDate
);
168 __CFGenericValidateType(date
, CFDateGetTypeID());
169 __CFGenericValidateType(otherDate
, CFDateGetTypeID());
170 if (date
->_time
< otherDate
->_time
) return kCFCompareLessThan
;
171 if (date
->_time
> otherDate
->_time
) return kCFCompareGreaterThan
;
172 return kCFCompareEqualTo
;
177 CF_INLINE
int32_t __CFDoubleModToInt(double d
, int32_t modulus
) {
178 int32_t result
= (int32_t)(float)floor(d
- floor(d
/ modulus
) * modulus
);
179 if (result
< 0) result
+= modulus
;
183 CF_INLINE
double __CFDoubleMod(double d
, int32_t modulus
) {
184 double result
= d
- floor(d
/ modulus
) * modulus
;
185 if (result
< 0.0) result
+= (double)modulus
;
189 static const uint8_t daysInMonth
[16] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0, 0, 0};
190 static const uint16_t daysBeforeMonth
[16] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0};
191 static const uint16_t daysAfterMonth
[16] = {365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0, 0, 0, 0};
193 CF_INLINE
bool isleap(int64_t year
) {
194 int64_t y
= (year
+ 1) % 400; /* correct to nearest multiple-of-400 year, then find the remainder */
196 return (0 == (y
& 3) && 100 != y
&& 200 != y
&& 300 != y
);
199 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
200 CF_INLINE
uint8_t __CFDaysInMonth(int8_t month
, int64_t year
, bool leap
) {
201 return daysInMonth
[month
] + (2 == month
&& leap
);
204 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
205 CF_INLINE
uint16_t __CFDaysBeforeMonth(int8_t month
, int64_t year
, bool leap
) {
206 return daysBeforeMonth
[month
] + (2 < month
&& leap
);
209 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
210 CF_INLINE
uint16_t __CFDaysAfterMonth(int8_t month
, int64_t year
, bool leap
) {
211 return daysAfterMonth
[month
] + (month
< 2 && leap
);
214 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
215 static void __CFYMDFromAbsolute(int64_t absolute
, int64_t *year
, int8_t *month
, int8_t *day
) {
216 int64_t b
= absolute
/ 146097; // take care of as many multiples of 400 years as possible
219 absolute
-= b
* 146097;
220 while (absolute
< 0) {
222 absolute
+= __CFDaysAfterMonth(0, y
, isleap(y
));
224 /* Now absolute is non-negative days to add to year */
225 ydays
= __CFDaysAfterMonth(0, y
, isleap(y
));
226 while (ydays
<= absolute
) {
229 ydays
= __CFDaysAfterMonth(0, y
, isleap(y
));
231 /* Now we have year and days-into-year */
234 int8_t m
= absolute
/ 33 + 1; /* search from the approximation */
235 bool leap
= isleap(y
);
236 while (__CFDaysBeforeMonth(m
+ 1, y
, leap
) <= absolute
) m
++;
237 if (month
) *month
= m
;
238 if (day
) *day
= absolute
- __CFDaysBeforeMonth(m
, y
, leap
) + 1;
242 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
243 static double __CFAbsoluteFromYMD(int64_t year
, int8_t month
, int8_t day
) {
244 double absolute
= 0.0;
246 int64_t b
= year
/ 400; // take care of as many multiples of 400 years as possible
247 absolute
+= b
* 146097.0;
250 for (idx
= year
; idx
< 0; idx
++)
251 absolute
-= __CFDaysAfterMonth(0, idx
, isleap(idx
));
253 for (idx
= 0; idx
< year
; idx
++)
254 absolute
+= __CFDaysAfterMonth(0, idx
, isleap(idx
));
256 /* Now add the days into the original year */
257 absolute
+= __CFDaysBeforeMonth(month
, year
, isleap(year
)) + day
- 1;
261 Boolean
CFGregorianDateIsValid(CFGregorianDate gdate
, CFOptionFlags unitFlags
) {
262 if ((unitFlags
& kCFGregorianUnitsYears
) && (gdate
.year
<= 0)) return false;
263 if ((unitFlags
& kCFGregorianUnitsMonths
) && (gdate
.month
< 1 || 12 < gdate
.month
)) return false;
264 if ((unitFlags
& kCFGregorianUnitsDays
) && (gdate
.day
< 1 || 31 < gdate
.day
)) return false;
265 if ((unitFlags
& kCFGregorianUnitsHours
) && (gdate
.hour
< 0 || 23 < gdate
.hour
)) return false;
266 if ((unitFlags
& kCFGregorianUnitsMinutes
) && (gdate
.minute
< 0 || 59 < gdate
.minute
)) return false;
267 if ((unitFlags
& kCFGregorianUnitsSeconds
) && (gdate
.second
< 0.0 || 60.0 <= gdate
.second
)) return false;
268 if ((unitFlags
& kCFGregorianUnitsDays
) && (unitFlags
& kCFGregorianUnitsMonths
) && (unitFlags
& kCFGregorianUnitsYears
) && (__CFDaysInMonth(gdate
.month
, gdate
.year
- 2001, isleap(gdate
.year
- 2001)) < gdate
.day
)) return false;
272 CFAbsoluteTime
CFGregorianDateGetAbsoluteTime(CFGregorianDate gdate
, CFTimeZoneRef tz
) {
274 CFTimeInterval offset0
, offset1
;
276 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
278 at
= 86400.0 * __CFAbsoluteFromYMD(gdate
.year
- 2001, gdate
.month
, gdate
.day
);
279 at
+= 3600.0 * gdate
.hour
+ 60.0 * gdate
.minute
+ gdate
.second
;
281 offset0
= CFTimeZoneGetSecondsFromGMT(tz
, at
);
282 offset1
= CFTimeZoneGetSecondsFromGMT(tz
, at
- offset0
);
288 CFGregorianDate
CFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
289 CFGregorianDate gdate
;
290 int64_t absolute
, year
;
292 CFAbsoluteTime fixedat
;
294 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
296 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
297 absolute
= (int64_t)floor(fixedat
/ 86400.0);
298 __CFYMDFromAbsolute(absolute
, &year
, &month
, &day
);
299 if (INT32_MAX
- 2001 < year
) year
= INT32_MAX
- 2001;
300 gdate
.year
= year
+ 2001;
303 gdate
.hour
= __CFDoubleModToInt(floor(fixedat
/ 3600.0), 24);
304 gdate
.minute
= __CFDoubleModToInt(floor(fixedat
/ 60.0), 60);
305 gdate
.second
= __CFDoubleMod(fixedat
, 60);
306 if (0.0 == gdate
.second
) gdate
.second
= 0.0; // stomp out possible -0.0
310 /* Note that the units of years and months are not equal length, but are treated as such. */
311 CFAbsoluteTime
CFAbsoluteTimeAddGregorianUnits(CFAbsoluteTime at
, CFTimeZoneRef tz
, CFGregorianUnits units
) {
312 CFGregorianDate gdate
;
313 CFGregorianUnits working
;
314 CFAbsoluteTime candidate_at0
, candidate_at1
;
318 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
321 /* Most people seem to expect years, then months, then days, etc.
322 to be added in that order. Thus, 27 April + (4 days, 1 month)
323 = 31 May, and not 1 June. This is also relatively predictable.
325 On another issue, months not being equal length, people also
326 seem to expect late day-of-month clamping (don't clamp as you
327 go through months), but clamp before adding in the days. Late
328 clamping is also more predictable given random starting points
329 and random numbers of months added (ie Jan 31 + 2 months could
330 be March 28 or March 29 in different years with aggressive
331 clamping). Proportionality (28 Feb + 1 month = 31 March) is
334 Also, people don't expect time zone transitions to have any
335 effect when adding years and/or months and/or days, only.
336 Hours, minutes, and seconds, though, are added in as humans
337 would experience the passing of that time. What this means
338 is that if the date, after adding years, months, and days
339 lands on some date, and then adding hours, minutes, and
340 seconds crosses a time zone transition, the time zone
341 transition is accounted for. If adding years, months, and
342 days gets the date into a different time zone offset period,
343 that transition is not taken into account.
345 gdate
= CFAbsoluteTimeGetGregorianDate(at
, tz
);
346 /* We must work in a CFGregorianUnits, because the fields in the CFGregorianDate can easily overflow */
347 working
.years
= gdate
.year
;
348 working
.months
= gdate
.month
;
349 working
.days
= gdate
.day
;
350 working
.years
+= units
.years
;
351 working
.months
+= units
.months
;
352 while (12 < working
.months
) {
353 working
.months
-= 12;
356 while (working
.months
< 1) {
357 working
.months
+= 12;
360 monthdays
= __CFDaysInMonth(working
.months
, working
.years
- 2001, isleap(working
.years
- 2001));
361 if (monthdays
< working
.days
) { /* Clamp day to new month */
362 working
.days
= monthdays
;
364 working
.days
+= units
.days
;
365 while (monthdays
< working
.days
) {
367 if (12 < working
.months
) {
368 working
.months
-= 12;
371 working
.days
-= monthdays
;
372 monthdays
= __CFDaysInMonth(working
.months
, working
.years
- 2001, isleap(working
.years
- 2001));
374 while (working
.days
< 1) {
376 if (working
.months
< 1) {
377 working
.months
+= 12;
380 monthdays
= __CFDaysInMonth(working
.months
, working
.years
- 2001, isleap(working
.years
- 2001));
381 working
.days
+= monthdays
;
383 gdate
.year
= working
.years
;
384 gdate
.month
= working
.months
;
385 gdate
.day
= working
.days
;
386 /* Roll in hours, minutes, and seconds */
387 candidate_at0
= CFGregorianDateGetAbsoluteTime(gdate
, tz
);
388 candidate_at1
= candidate_at0
+ 3600.0 * units
.hours
+ 60.0 * units
.minutes
+ units
.seconds
;
389 /* If summing in the hours, minutes, and seconds delta pushes us
390 * into a new time zone offset, that will automatically be taken
391 * care of by the fact that we just add the raw time above. To
392 * undo that effect, we'd have to get the time zone offsets for
393 * candidate_at0 and candidate_at1 here, and subtract the
394 * difference (offset1 - offset0) from candidate_at1. */
395 return candidate_at1
;
398 /* at1 - at2. The only constraint here is that this needs to be the inverse
399 of CFAbsoluteTimeByAddingGregorianUnits(), but that's a very rigid constraint.
400 Unfortunately, due to the nonuniformity of the year and month units, this
401 inversion essentially has to approximate until it finds the answer. */
402 CFGregorianUnits
CFAbsoluteTimeGetDifferenceAsGregorianUnits(CFAbsoluteTime at1
, CFAbsoluteTime at2
, CFTimeZoneRef tz
, CFOptionFlags unitFlags
) {
403 const int32_t seconds
[5] = {366 * 24 * 3600, 31 * 24 * 3600, 24 * 3600, 3600, 60};
404 CFGregorianUnits units
= {0, 0, 0, 0, 0, 0.0};
405 CFAbsoluteTime atold
, atnew
= at2
;
407 incr
= (at2
< at1
) ? 1 : -1;
408 /* Successive approximation: years, then months, then days, then hours, then minutes. */
409 for (idx
= 0; idx
< 5; idx
++) {
410 if (unitFlags
& (1 << idx
)) {
411 ((int32_t *)&units
)[idx
] = -3 * incr
+ (int32_t)((at1
- atnew
) / seconds
[idx
]);
414 ((int32_t *)&units
)[idx
] += incr
;
415 atnew
= CFAbsoluteTimeAddGregorianUnits(at2
, tz
, units
);
416 } while ((1 == incr
&& atnew
<= at1
) || (-1 == incr
&& at1
<= atnew
));
417 ((int32_t *)&units
)[idx
] -= incr
;
421 if (unitFlags
& kCFGregorianUnitsSeconds
) {
422 units
.seconds
= at1
- atnew
;
424 if (0.0 == units
.seconds
) units
.seconds
= 0.0; // stomp out possible -0.0
428 SInt32
CFAbsoluteTimeGetDayOfWeek(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
430 CFAbsoluteTime fixedat
;
432 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
434 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
435 absolute
= (int64_t)floor(fixedat
/ 86400.0);
436 return (absolute
< 0) ? ((absolute
+ 1) % 7 + 7) : (absolute
% 7 + 1); /* Monday = 1, etc. */
439 SInt32
CFAbsoluteTimeGetDayOfYear(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
440 CFAbsoluteTime fixedat
;
441 int64_t absolute
, year
;
444 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
446 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
447 absolute
= (int64_t)floor(fixedat
/ 86400.0);
448 __CFYMDFromAbsolute(absolute
, &year
, &month
, &day
);
449 return __CFDaysBeforeMonth(month
, year
, isleap(year
)) + day
;
452 /* "the first week of a year is the one which includes the first Thursday" (ISO 8601) */
453 SInt32
CFAbsoluteTimeGetWeekOfYear(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
454 int64_t absolute
, year
;
456 CFAbsoluteTime fixedat
;
458 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
460 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
461 absolute
= (int64_t)floor(fixedat
/ 86400.0);
462 __CFYMDFromAbsolute(absolute
, &year
, &month
, &day
);
463 double absolute0101
= __CFAbsoluteFromYMD(year
, 1, 1);
464 int64_t dow0101
= __CFDoubleModToInt(absolute0101
, 7) + 1;
465 /* First three and last three days of a year can end up in a week of a different year */
466 if (1 == month
&& day
< 4) {
467 if ((day
< 4 && 5 == dow0101
) || (day
< 3 && 6 == dow0101
) || (day
< 2 && 7 == dow0101
)) {
471 if (12 == month
&& 28 < day
) {
472 double absolute20101
= __CFAbsoluteFromYMD(year
+ 1, 1, 1);
473 int64_t dow20101
= __CFDoubleModToInt(absolute20101
, 7) + 1;
474 if ((28 < day
&& 4 == dow20101
) || (29 < day
&& 3 == dow20101
) || (30 < day
&& 2 == dow20101
)) {
478 /* Days into year, plus a week-shifting correction, divided by 7. First week is 1. */
479 return (__CFDaysBeforeMonth(month
, year
, isleap(year
)) + day
+ (dow0101
- 11) % 7 + 2) / 7 + 1;