2 * Copyright (c) 2005 Apple Computer, 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 1998-2002, 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 defined(__MACH__) || defined(__LINUX__)
39 #if defined(__WIN32__)
43 const CFTimeInterval kCFAbsoluteTimeIntervalSince1970
= 978307200.0L;
44 const CFTimeInterval kCFAbsoluteTimeIntervalSince1904
= 3061152000.0L;
46 /* cjk: The Julian Date for the reference date is 2451910.5,
47 I think, in case that's ever useful. */
51 CFAbsoluteTime _time
; /* immutable */
54 #if defined(__WIN32__)
55 // We should export this as SPI or API to clients - 3514284
56 CFAbsoluteTime
_CFAbsoluteTimeFromFileTime(const FILETIME
*ft
) {
57 CFAbsoluteTime ret
= (CFTimeInterval
)ft
->dwHighDateTime
* 429.49672960;
58 ret
+= (CFTimeInterval
)ft
->dwLowDateTime
/ 10000000.0;
59 ret
-= (11644473600.0 + kCFAbsoluteTimeIntervalSince1970
);
60 /* seconds between 1601 and 1970, 1970 and 2001 */
65 #if defined(__MACH__) || defined(__WIN32__)
66 static double __CFTSRRate
= 0.0;
67 static double __CF1_TSRRate
= 0.0;
69 __private_extern__
int64_t __CFTimeIntervalToTSR(CFTimeInterval ti
) {
70 return (int64_t)(ti
* __CFTSRRate
);
73 __private_extern__ CFTimeInterval
__CFTSRToTimeInterval(int64_t tsr
) {
74 return (CFTimeInterval
)((double)tsr
* __CF1_TSRRate
);
78 CFAbsoluteTime
CFAbsoluteTimeGetCurrent(void) {
80 #if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__)
82 gettimeofday(&tv
, NULL
);
83 ret
= (CFTimeInterval
)tv
.tv_sec
- kCFAbsoluteTimeIntervalSince1970
;
84 ret
+= (1.0E-6 * (CFTimeInterval
)tv
.tv_usec
);
85 #elif defined(__WIN32__)
87 GetSystemTimeAsFileTime(&ft
);
88 ret
= _CFAbsoluteTimeFromFileTime(&ft
);
90 #error CFAbsoluteTimeGetCurrent unimplemented for this platform
95 static Boolean
__CFDateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
96 CFDateRef date1
= (CFDateRef
)cf1
;
97 CFDateRef date2
= (CFDateRef
)cf2
;
98 if (date1
->_time
!= date2
->_time
) return false;
102 static CFHashCode
__CFDateHash(CFTypeRef cf
) {
103 CFDateRef date
= (CFDateRef
)cf
;
104 return (CFHashCode
)(float)floor(date
->_time
);
107 static CFStringRef
__CFDateCopyDescription(CFTypeRef cf
) {
108 CFDateRef date
= (CFDateRef
)cf
;
109 return CFStringCreateWithFormat(CFGetAllocator(date
), NULL
, CFSTR("<CFDate %p [%p]>{time = %0.09g}"), cf
, CFGetAllocator(date
), date
->_time
);
112 static CFTypeID __kCFDateTypeID
= _kCFRuntimeNotATypeID
;
114 static const CFRuntimeClass __CFDateClass
= {
123 __CFDateCopyDescription
126 __private_extern__
void __CFDateInitialize(void) {
127 #if defined(__MACH__)
128 struct mach_timebase_info info
;
129 mach_timebase_info(&info
);
130 __CFTSRRate
= (1.0E9
/ (double)info
.numer
) * (double)info
.denom
;
131 __CF1_TSRRate
= 1.0 / __CFTSRRate
;
133 #if defined(__WIN32__)
135 if (!QueryPerformanceFrequency(&freq
)) {
138 __CFTSRRate
= freq
.QuadPart
;
139 __CF1_TSRRate
= 1.0 / __CFTSRRate
;
141 __kCFDateTypeID
= _CFRuntimeRegisterClass(&__CFDateClass
);
144 CFTypeID
CFDateGetTypeID(void) {
145 return __kCFDateTypeID
;
148 CFDateRef
CFDateCreate(CFAllocatorRef allocator
, CFAbsoluteTime at
) {
151 size
= sizeof(struct __CFDate
) - sizeof(CFRuntimeBase
);
152 memory
= _CFRuntimeCreateInstance(allocator
, __kCFDateTypeID
, size
, NULL
);
153 if (NULL
== memory
) {
156 ((struct __CFDate
*)memory
)->_time
= at
;
160 CFTimeInterval
CFDateGetAbsoluteTime(CFDateRef date
) {
161 CF_OBJC_FUNCDISPATCH0(__kCFDateTypeID
, CFTimeInterval
, date
, "timeIntervalSinceReferenceDate");
162 __CFGenericValidateType(date
, CFDateGetTypeID());
166 CFTimeInterval
CFDateGetTimeIntervalSinceDate(CFDateRef date
, CFDateRef otherDate
) {
167 CF_OBJC_FUNCDISPATCH1(__kCFDateTypeID
, CFTimeInterval
, date
, "timeIntervalSinceDate:", otherDate
);
168 __CFGenericValidateType(date
, CFDateGetTypeID());
169 __CFGenericValidateType(otherDate
, CFDateGetTypeID());
170 return date
->_time
- otherDate
->_time
;
173 CFComparisonResult
CFDateCompare(CFDateRef date
, CFDateRef otherDate
, void *context
) {
174 CF_OBJC_FUNCDISPATCH1(__kCFDateTypeID
, CFComparisonResult
, date
, "compare:", otherDate
);
175 __CFGenericValidateType(date
, CFDateGetTypeID());
176 __CFGenericValidateType(otherDate
, CFDateGetTypeID());
177 if (date
->_time
< otherDate
->_time
) return kCFCompareLessThan
;
178 if (date
->_time
> otherDate
->_time
) return kCFCompareGreaterThan
;
179 return kCFCompareEqualTo
;
182 CF_INLINE
int32_t __CFDoubleModToInt(double d
, int32_t modulus
) {
183 int32_t result
= (int32_t)(float)floor(d
- floor(d
/ modulus
) * modulus
);
184 if (result
< 0) result
+= modulus
;
188 CF_INLINE
double __CFDoubleMod(double d
, int32_t modulus
) {
189 double result
= d
- floor(d
/ modulus
) * modulus
;
190 if (result
< 0.0) result
+= (double)modulus
;
194 static const uint8_t daysInMonth
[16] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0, 0, 0};
195 static const uint16_t daysBeforeMonth
[16] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0};
196 static const uint16_t daysAfterMonth
[16] = {365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0, 0, 0, 0};
198 static inline bool isleap(int32_t year
) {
199 int32_t y
= (year
+ 1) % 400; /* correct to nearest multiple-of-400 year, then find the remainder */
201 return (0 == (y
& 3) && 100 != y
&& 200 != y
&& 300 != y
);
204 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
205 static inline uint8_t __CFDaysInMonth(int8_t month
, int32_t year
, bool leap
) {
206 return daysInMonth
[month
] + (2 == month
&& leap
);
209 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
210 static inline uint16_t __CFDaysBeforeMonth(int8_t month
, int32_t year
, bool leap
) {
211 return daysBeforeMonth
[month
] + (2 < month
&& leap
);
214 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
215 static inline uint16_t __CFDaysAfterMonth(int8_t month
, int32_t year
, bool leap
) {
216 return daysAfterMonth
[month
] + (month
< 2 && leap
);
219 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
220 static void __CFYMDFromAbsolute(int32_t absolute
, int32_t *year
, int8_t *month
, int8_t *day
) {
221 int32_t b
= absolute
/ 146097; // take care of as many multiples of 400 years as possible
224 absolute
-= b
* 146097;
225 while (absolute
< 0) {
227 absolute
+= __CFDaysAfterMonth(0, y
, isleap(y
));
229 /* Now absolute is non-negative days to add to year */
230 ydays
= __CFDaysAfterMonth(0, y
, isleap(y
));
231 while (ydays
<= absolute
) {
234 ydays
= __CFDaysAfterMonth(0, y
, isleap(y
));
236 /* Now we have year and days-into-year */
239 int8_t m
= absolute
/ 33 + 1; /* search from the approximation */
240 bool leap
= isleap(y
);
241 while (__CFDaysBeforeMonth(m
+ 1, y
, leap
) <= absolute
) m
++;
242 if (month
) *month
= m
;
243 if (day
) *day
= absolute
- __CFDaysBeforeMonth(m
, y
, leap
) + 1;
247 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
248 static double __CFAbsoluteFromYMD(int32_t year
, int8_t month
, int8_t day
) {
249 double absolute
= 0.0;
251 int32_t b
= year
/ 400; // take care of as many multiples of 400 years as possible
252 absolute
+= b
* 146097.0;
255 for (idx
= year
; idx
< 0; idx
++)
256 absolute
-= __CFDaysAfterMonth(0, idx
, isleap(idx
));
258 for (idx
= 0; idx
< year
; idx
++)
259 absolute
+= __CFDaysAfterMonth(0, idx
, isleap(idx
));
261 /* Now add the days into the original year */
262 absolute
+= __CFDaysBeforeMonth(month
, year
, isleap(year
)) + day
- 1;
266 Boolean
CFGregorianDateIsValid(CFGregorianDate gdate
, CFOptionFlags unitFlags
) {
267 if ((unitFlags
& kCFGregorianUnitsYears
) && (gdate
.year
<= 0)) return false;
268 if ((unitFlags
& kCFGregorianUnitsMonths
) && (gdate
.month
< 1 || 12 < gdate
.month
)) return false;
269 if ((unitFlags
& kCFGregorianUnitsDays
) && (gdate
.day
< 1 || 31 < gdate
.day
)) return false;
270 if ((unitFlags
& kCFGregorianUnitsHours
) && (gdate
.hour
< 0 || 23 < gdate
.hour
)) return false;
271 if ((unitFlags
& kCFGregorianUnitsMinutes
) && (gdate
.minute
< 0 || 59 < gdate
.minute
)) return false;
272 if ((unitFlags
& kCFGregorianUnitsSeconds
) && (gdate
.second
< 0.0 || 60.0 <= gdate
.second
)) return false;
273 if ((unitFlags
& kCFGregorianUnitsDays
) && (__CFDaysInMonth(gdate
.month
, gdate
.year
- 2001, isleap(gdate
.year
- 2001)) < gdate
.day
)) return false;
277 CFAbsoluteTime
CFGregorianDateGetAbsoluteTime(CFGregorianDate gdate
, CFTimeZoneRef tz
) {
279 CFTimeInterval offset0
, offset1
;
281 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
283 at
= 86400.0 * __CFAbsoluteFromYMD(gdate
.year
- 2001, gdate
.month
, gdate
.day
);
284 at
+= 3600.0 * gdate
.hour
+ 60.0 * gdate
.minute
+ gdate
.second
;
286 offset0
= CFTimeZoneGetSecondsFromGMT(tz
, at
);
287 offset1
= CFTimeZoneGetSecondsFromGMT(tz
, at
- offset0
);
293 CFGregorianDate
CFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
294 CFGregorianDate gdate
;
295 int32_t absolute
, year
;
297 CFAbsoluteTime fixedat
;
299 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
301 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
302 absolute
= (int32_t)(float)floor(fixedat
/ 86400.0);
303 __CFYMDFromAbsolute(absolute
, &year
, &month
, &day
);
304 gdate
.year
= year
+ 2001;
307 gdate
.hour
= __CFDoubleModToInt(floor(fixedat
/ 3600.0), 24);
308 gdate
.minute
= __CFDoubleModToInt(floor(fixedat
/ 60.0), 60);
309 gdate
.second
= __CFDoubleMod(fixedat
, 60);
313 /* Note that the units of years and months are not equal length, but are treated as such. */
314 CFAbsoluteTime
CFAbsoluteTimeAddGregorianUnits(CFAbsoluteTime at
, CFTimeZoneRef tz
, CFGregorianUnits units
) {
315 CFGregorianDate gdate
;
316 CFGregorianUnits working
;
317 CFAbsoluteTime candidate_at0
, candidate_at1
;
321 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
324 /* Most people seem to expect years, then months, then days, etc.
325 to be added in that order. Thus, 27 April + (4 days, 1 month)
326 = 31 May, and not 1 June. This is also relatively predictable.
328 On another issue, months not being equal length, people also
329 seem to expect late day-of-month clamping (don't clamp as you
330 go through months), but clamp before adding in the days. Late
331 clamping is also more predictable given random starting points
332 and random numbers of months added (ie Jan 31 + 2 months could
333 be March 28 or March 29 in different years with aggressive
334 clamping). Proportionality (28 Feb + 1 month = 31 March) is
337 Also, people don't expect time zone transitions to have any
338 effect when adding years and/or months and/or days, only.
339 Hours, minutes, and seconds, though, are added in as humans
340 would experience the passing of that time. What this means
341 is that if the date, after adding years, months, and days
342 lands on some date, and then adding hours, minutes, and
343 seconds crosses a time zone transition, the time zone
344 transition is accounted for. If adding years, months, and
345 days gets the date into a different time zone offset period,
346 that transition is not taken into account.
348 gdate
= CFAbsoluteTimeGetGregorianDate(at
, tz
);
349 /* We must work in a CFGregorianUnits, because the fields in the CFGregorianDate can easily overflow */
350 working
.years
= gdate
.year
;
351 working
.months
= gdate
.month
;
352 working
.days
= gdate
.day
;
353 working
.years
+= units
.years
;
354 working
.months
+= units
.months
;
355 while (12 < working
.months
) {
356 working
.months
-= 12;
359 while (working
.months
< 1) {
360 working
.months
+= 12;
363 monthdays
= __CFDaysInMonth(working
.months
, working
.years
- 2001, isleap(working
.years
- 2001));
364 if (monthdays
< working
.days
) { /* Clamp day to new month */
365 working
.days
= monthdays
;
367 working
.days
+= units
.days
;
368 while (monthdays
< working
.days
) {
370 if (12 < working
.months
) {
371 working
.months
-= 12;
374 working
.days
-= monthdays
;
375 monthdays
= __CFDaysInMonth(working
.months
, working
.years
- 2001, isleap(working
.years
- 2001));
377 while (working
.days
< 1) {
379 if (working
.months
< 1) {
380 working
.months
+= 12;
383 monthdays
= __CFDaysInMonth(working
.months
, working
.years
- 2001, isleap(working
.years
- 2001));
384 working
.days
+= monthdays
;
386 gdate
.year
= working
.years
;
387 gdate
.month
= working
.months
;
388 gdate
.day
= working
.days
;
389 /* Roll in hours, minutes, and seconds */
390 candidate_at0
= CFGregorianDateGetAbsoluteTime(gdate
, tz
);
391 candidate_at1
= candidate_at0
+ 3600.0 * units
.hours
+ 60.0 * units
.minutes
+ units
.seconds
;
392 /* If summing in the hours, minutes, and seconds delta pushes us
393 * into a new time zone offset, that will automatically be taken
394 * care of by the fact that we just add the raw time above. To
395 * undo that effect, we'd have to get the time zone offsets for
396 * candidate_at0 and candidate_at1 here, and subtract the
397 * difference (offset1 - offset0) from candidate_at1. */
398 return candidate_at1
;
401 /* at1 - at2. The only constraint here is that this needs to be the inverse
402 of CFAbsoluteTimeByAddingGregorianUnits(), but that's a very rigid constraint.
403 Unfortunately, due to the nonuniformity of the year and month units, this
404 inversion essentially has to approximate until it finds the answer. */
405 CFGregorianUnits
CFAbsoluteTimeGetDifferenceAsGregorianUnits(CFAbsoluteTime at1
, CFAbsoluteTime at2
, CFTimeZoneRef tz
, CFOptionFlags unitFlags
) {
406 const int32_t seconds
[5] = {366 * 24 * 3600, 31 * 24 * 3600, 24 * 3600, 3600, 60};
407 CFGregorianUnits units
= {0, 0, 0, 0, 0, 0.0};
408 CFAbsoluteTime atold
, atnew
= at2
;
410 incr
= (at2
< at1
) ? 1 : -1;
411 /* Successive approximation: years, then months, then days, then hours, then minutes. */
412 for (idx
= 0; idx
< 5; idx
++) {
413 if (unitFlags
& (1 << idx
)) {
414 ((int32_t *)&units
)[idx
] = -3 * incr
+ (at1
- atnew
) / seconds
[idx
];
417 ((int32_t *)&units
)[idx
] += incr
;
418 atnew
= CFAbsoluteTimeAddGregorianUnits(at2
, tz
, units
);
419 } while ((1 == incr
&& atnew
<= at1
) || (-1 == incr
&& at1
<= atnew
));
420 ((int32_t *)&units
)[idx
] -= incr
;
424 if (unitFlags
& kCFGregorianUnitsSeconds
) {
425 units
.seconds
= at1
- atnew
;
430 SInt32
CFAbsoluteTimeGetDayOfWeek(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
432 CFAbsoluteTime fixedat
;
434 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
436 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
437 absolute
= (int32_t)(float)floor(fixedat
/ 86400.0);
438 return (absolute
< 0) ? ((absolute
+ 1) % 7 + 7) : (absolute
% 7 + 1); /* Monday = 1, etc. */
441 SInt32
CFAbsoluteTimeGetDayOfYear(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
442 CFAbsoluteTime fixedat
;
443 int32_t absolute
, year
;
446 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
448 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
449 absolute
= (int32_t)(float)floor(fixedat
/ 86400.0);
450 __CFYMDFromAbsolute(absolute
, &year
, &month
, &day
);
451 return __CFDaysBeforeMonth(month
, year
, isleap(year
)) + day
;
454 /* "the first week of a year is the one which includes the first Thursday" (ISO 8601) */
455 SInt32
CFAbsoluteTimeGetWeekOfYear(CFAbsoluteTime at
, CFTimeZoneRef tz
) {
456 int32_t absolute
, year
;
458 CFAbsoluteTime fixedat
;
460 __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
462 fixedat
= at
+ (NULL
!= tz
? CFTimeZoneGetSecondsFromGMT(tz
, at
) : 0.0);
463 absolute
= (int32_t)(float)floor(fixedat
/ 86400.0);
464 __CFYMDFromAbsolute(absolute
, &year
, &month
, &day
);
465 double absolute0101
= __CFAbsoluteFromYMD(year
, 1, 1);
466 int32_t dow0101
= __CFDoubleModToInt(absolute0101
, 7) + 1;
467 /* First three and last three days of a year can end up in a week of a different year */
468 if (1 == month
&& day
< 4) {
469 if ((day
< 4 && 5 == dow0101
) || (day
< 3 && 6 == dow0101
) || (day
< 2 && 7 == dow0101
)) {
473 if (12 == month
&& 28 < day
) {
474 double absolute20101
= __CFAbsoluteFromYMD(year
+ 1, 1, 1);
475 int32_t dow20101
= __CFDoubleModToInt(absolute20101
, 7) + 1;
476 if ((28 < day
&& 4 == dow20101
) || (29 < day
&& 3 == dow20101
) || (30 < day
&& 2 == dow20101
)) {
480 /* Days into year, plus a week-shifting correction, divided by 7. First week is #1. */
481 return (__CFDaysBeforeMonth(month
, year
, isleap(year
)) + day
+ (dow0101
- 11) % 7 + 2) / 7 + 1;