]> git.saurik.com Git - apple/cf.git/blob - NumberDate.subproj/CFDate.c
CF-368.tar.gz
[apple/cf.git] / NumberDate.subproj / CFDate.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /* CFDate.c
24 Copyright 1998-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
26 */
27
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"
35 #include <math.h>
36 #if defined(__MACH__) || defined(__LINUX__)
37 #include <sys/time.h>
38 #endif
39 #if defined(__WIN32__)
40 #include <windows.h>
41 #endif
42
43 const CFTimeInterval kCFAbsoluteTimeIntervalSince1970 = 978307200.0L;
44 const CFTimeInterval kCFAbsoluteTimeIntervalSince1904 = 3061152000.0L;
45
46 /* cjk: The Julian Date for the reference date is 2451910.5,
47 I think, in case that's ever useful. */
48
49 struct __CFDate {
50 CFRuntimeBase _base;
51 CFAbsoluteTime _time; /* immutable */
52 };
53
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 */
61 return ret;
62 }
63 #endif
64
65 #if defined(__MACH__) || defined(__WIN32__)
66 static double __CFTSRRate = 0.0;
67 static double __CF1_TSRRate = 0.0;
68
69 __private_extern__ int64_t __CFTimeIntervalToTSR(CFTimeInterval ti) {
70 return (int64_t)(ti * __CFTSRRate);
71 }
72
73 __private_extern__ CFTimeInterval __CFTSRToTimeInterval(int64_t tsr) {
74 return (CFTimeInterval)((double)tsr * __CF1_TSRRate);
75 }
76 #endif
77
78 CFAbsoluteTime CFAbsoluteTimeGetCurrent(void) {
79 CFAbsoluteTime ret;
80 #if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__)
81 struct timeval tv;
82 gettimeofday(&tv, NULL);
83 ret = (CFTimeInterval)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970;
84 ret += (1.0E-6 * (CFTimeInterval)tv.tv_usec);
85 #elif defined(__WIN32__)
86 FILETIME ft;
87 GetSystemTimeAsFileTime(&ft);
88 ret = _CFAbsoluteTimeFromFileTime(&ft);
89 #else
90 #error CFAbsoluteTimeGetCurrent unimplemented for this platform
91 #endif
92 return ret;
93 }
94
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;
99 return true;
100 }
101
102 static CFHashCode __CFDateHash(CFTypeRef cf) {
103 CFDateRef date = (CFDateRef)cf;
104 return (CFHashCode)(float)floor(date->_time);
105 }
106
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);
110 }
111
112 static CFTypeID __kCFDateTypeID = _kCFRuntimeNotATypeID;
113
114 static const CFRuntimeClass __CFDateClass = {
115 0,
116 "CFDate",
117 NULL, // init
118 NULL, // copy
119 NULL, // dealloc
120 __CFDateEqual,
121 __CFDateHash,
122 NULL, //
123 __CFDateCopyDescription
124 };
125
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;
132 #endif
133 #if defined(__WIN32__)
134 LARGE_INTEGER freq;
135 if (!QueryPerformanceFrequency(&freq)) {
136 HALT;
137 }
138 __CFTSRRate = freq.QuadPart;
139 __CF1_TSRRate = 1.0 / __CFTSRRate;
140 #endif
141 __kCFDateTypeID = _CFRuntimeRegisterClass(&__CFDateClass);
142 }
143
144 CFTypeID CFDateGetTypeID(void) {
145 return __kCFDateTypeID;
146 }
147
148 CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at) {
149 CFDateRef memory;
150 uint32_t size;
151 size = sizeof(struct __CFDate) - sizeof(CFRuntimeBase);
152 memory = _CFRuntimeCreateInstance(allocator, __kCFDateTypeID, size, NULL);
153 if (NULL == memory) {
154 return NULL;
155 }
156 ((struct __CFDate *)memory)->_time = at;
157 return memory;
158 }
159
160 CFTimeInterval CFDateGetAbsoluteTime(CFDateRef date) {
161 CF_OBJC_FUNCDISPATCH0(__kCFDateTypeID, CFTimeInterval, date, "timeIntervalSinceReferenceDate");
162 __CFGenericValidateType(date, CFDateGetTypeID());
163 return date->_time;
164 }
165
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;
171 }
172
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;
180 }
181
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;
185 return result;
186 }
187
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;
191 return result;
192 }
193
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};
197
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 */
200 if (y < 0) y = -y;
201 return (0 == (y & 3) && 100 != y && 200 != y && 300 != y);
202 }
203
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);
207 }
208
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);
212 }
213
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);
217 }
218
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
222 int32_t y = b * 400;
223 uint16_t ydays;
224 absolute -= b * 146097;
225 while (absolute < 0) {
226 y -= 1;
227 absolute += __CFDaysAfterMonth(0, y, isleap(y));
228 }
229 /* Now absolute is non-negative days to add to year */
230 ydays = __CFDaysAfterMonth(0, y, isleap(y));
231 while (ydays <= absolute) {
232 y += 1;
233 absolute -= ydays;
234 ydays = __CFDaysAfterMonth(0, y, isleap(y));
235 }
236 /* Now we have year and days-into-year */
237 if (year) *year = y;
238 if (month || day) {
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;
244 }
245 }
246
247 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
248 static int32_t __CFAbsoluteFromYMD(int32_t year, int8_t month, int8_t day) {
249 int32_t absolute = 0, idx;
250 if (year < 0) {
251 for (idx = year; idx < 0; idx++)
252 absolute -= __CFDaysAfterMonth(0, idx, isleap(idx));
253 } else {
254 for (idx = year - 1; 0 <= idx; idx--)
255 absolute += __CFDaysAfterMonth(0, idx, isleap(idx));
256 }
257 /* Now add the days into the original year */
258 absolute += __CFDaysBeforeMonth(month, year, isleap(year)) + day - 1;
259 return absolute;
260 }
261
262 Boolean CFGregorianDateIsValid(CFGregorianDate gdate, CFOptionFlags unitFlags) {
263 if ((unitFlags & kCFGregorianUnitsYears) && (gdate.year <= 0)) return false;
264 if ((unitFlags & kCFGregorianUnitsMonths) && (gdate.month < 1 || 12 < gdate.month)) return false;
265 if ((unitFlags & kCFGregorianUnitsDays) && (gdate.day < 1 || 31 < gdate.day)) return false;
266 if ((unitFlags & kCFGregorianUnitsHours) && (gdate.hour < 0 || 23 < gdate.hour)) return false;
267 if ((unitFlags & kCFGregorianUnitsMinutes) && (gdate.minute < 0 || 59 < gdate.minute)) return false;
268 if ((unitFlags & kCFGregorianUnitsSeconds) && (gdate.second < 0.0 || 60.0 <= gdate.second)) return false;
269 if ((unitFlags & kCFGregorianUnitsDays) && (__CFDaysInMonth(gdate.month, gdate.year - 2001, isleap(gdate.year - 2001)) < gdate.day)) return false;
270 return true;
271 }
272
273 CFAbsoluteTime CFGregorianDateGetAbsoluteTime(CFGregorianDate gdate, CFTimeZoneRef tz) {
274 CFAbsoluteTime at;
275 CFTimeInterval offset0, offset1;
276 if (NULL != tz) {
277 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
278 }
279 at = 86400.0 * __CFAbsoluteFromYMD(gdate.year - 2001, gdate.month, gdate.day);
280 at += 3600.0 * gdate.hour + 60.0 * gdate.minute + gdate.second;
281 if (NULL != tz) {
282 offset0 = CFTimeZoneGetSecondsFromGMT(tz, at);
283 offset1 = CFTimeZoneGetSecondsFromGMT(tz, at - offset0);
284 at -= offset1;
285 }
286 return at;
287 }
288
289 CFGregorianDate CFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at, CFTimeZoneRef tz) {
290 CFGregorianDate gdate;
291 int32_t absolute, year;
292 int8_t month, day;
293 CFAbsoluteTime fixedat;
294 if (NULL != tz) {
295 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
296 }
297 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
298 absolute = (int32_t)(float)floor(fixedat / 86400.0);
299 __CFYMDFromAbsolute(absolute, &year, &month, &day);
300 gdate.year = year + 2001;
301 gdate.month = month;
302 gdate.day = day;
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 return gdate;
307 }
308
309 /* Note that the units of years and months are not equal length, but are treated as such. */
310 CFAbsoluteTime CFAbsoluteTimeAddGregorianUnits(CFAbsoluteTime at, CFTimeZoneRef tz, CFGregorianUnits units) {
311 CFGregorianDate gdate;
312 CFGregorianUnits working;
313 CFAbsoluteTime candidate_at0, candidate_at1;
314 uint8_t monthdays;
315
316 if (NULL != tz) {
317 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
318 }
319
320 /* Most people seem to expect years, then months, then days, etc.
321 to be added in that order. Thus, 27 April + (4 days, 1 month)
322 = 31 May, and not 1 June. This is also relatively predictable.
323
324 On another issue, months not being equal length, people also
325 seem to expect late day-of-month clamping (don't clamp as you
326 go through months), but clamp before adding in the days. Late
327 clamping is also more predictable given random starting points
328 and random numbers of months added (ie Jan 31 + 2 months could
329 be March 28 or March 29 in different years with aggressive
330 clamping). Proportionality (28 Feb + 1 month = 31 March) is
331 also not expected.
332
333 Also, people don't expect time zone transitions to have any
334 effect when adding years and/or months and/or days, only.
335 Hours, minutes, and seconds, though, are added in as humans
336 would experience the passing of that time. What this means
337 is that if the date, after adding years, months, and days
338 lands on some date, and then adding hours, minutes, and
339 seconds crosses a time zone transition, the time zone
340 transition is accounted for. If adding years, months, and
341 days gets the date into a different time zone offset period,
342 that transition is not taken into account.
343 */
344 gdate = CFAbsoluteTimeGetGregorianDate(at, tz);
345 /* We must work in a CFGregorianUnits, because the fields in the CFGregorianDate can easily overflow */
346 working.years = gdate.year;
347 working.months = gdate.month;
348 working.days = gdate.day;
349 working.years += units.years;
350 working.months += units.months;
351 while (12 < working.months) {
352 working.months -= 12;
353 working.years += 1;
354 }
355 while (working.months < 1) {
356 working.months += 12;
357 working.years -= 1;
358 }
359 monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001));
360 if (monthdays < working.days) { /* Clamp day to new month */
361 working.days = monthdays;
362 }
363 working.days += units.days;
364 while (monthdays < working.days) {
365 working.months += 1;
366 if (12 < working.months) {
367 working.months -= 12;
368 working.years += 1;
369 }
370 working.days -= monthdays;
371 monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001));
372 }
373 while (working.days < 1) {
374 working.months -= 1;
375 if (working.months < 1) {
376 working.months += 12;
377 working.years -= 1;
378 }
379 monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001));
380 working.days += monthdays;
381 }
382 gdate.year = working.years;
383 gdate.month = working.months;
384 gdate.day = working.days;
385 /* Roll in hours, minutes, and seconds */
386 candidate_at0 = CFGregorianDateGetAbsoluteTime(gdate, tz);
387 candidate_at1 = candidate_at0 + 3600.0 * units.hours + 60.0 * units.minutes + units.seconds;
388 /* If summing in the hours, minutes, and seconds delta pushes us
389 * into a new time zone offset, that will automatically be taken
390 * care of by the fact that we just add the raw time above. To
391 * undo that effect, we'd have to get the time zone offsets for
392 * candidate_at0 and candidate_at1 here, and subtract the
393 * difference (offset1 - offset0) from candidate_at1. */
394 return candidate_at1;
395 }
396
397 /* at1 - at2. The only constraint here is that this needs to be the inverse
398 of CFAbsoluteTimeByAddingGregorianUnits(), but that's a very rigid constraint.
399 Unfortunately, due to the nonuniformity of the year and month units, this
400 inversion essentially has to approximate until it finds the answer. */
401 CFGregorianUnits CFAbsoluteTimeGetDifferenceAsGregorianUnits(CFAbsoluteTime at1, CFAbsoluteTime at2, CFTimeZoneRef tz, CFOptionFlags unitFlags) {
402 const int32_t seconds[5] = {366 * 24 * 3600, 31 * 24 * 3600, 24 * 3600, 3600, 60};
403 CFGregorianUnits units = {0, 0, 0, 0, 0, 0.0};
404 CFAbsoluteTime atold, atnew = at2;
405 int32_t idx, incr;
406 incr = (at2 < at1) ? 1 : -1;
407 /* Successive approximation: years, then months, then days, then hours, then minutes. */
408 for (idx = 0; idx < 5; idx++) {
409 if (unitFlags & (1 << idx)) {
410 ((int32_t *)&units)[idx] = -3 * incr + (at1 - atnew) / seconds[idx];
411 do {
412 atold = atnew;
413 ((int32_t *)&units)[idx] += incr;
414 atnew = CFAbsoluteTimeAddGregorianUnits(at2, tz, units);
415 } while ((1 == incr && atnew <= at1) || (-1 == incr && at1 <= atnew));
416 ((int32_t *)&units)[idx] -= incr;
417 atnew = atold;
418 }
419 }
420 if (unitFlags & kCFGregorianUnitsSeconds) {
421 units.seconds = at1 - atnew;
422 }
423 return units;
424 }
425
426 SInt32 CFAbsoluteTimeGetDayOfWeek(CFAbsoluteTime at, CFTimeZoneRef tz) {
427 int32_t absolute;
428 CFAbsoluteTime fixedat;
429 if (NULL != tz) {
430 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
431 }
432 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
433 absolute = (int32_t)(float)floor(fixedat / 86400.0);
434 return (absolute < 0) ? ((absolute + 1) % 7 + 7) : (absolute % 7 + 1); /* Monday = 1, etc. */
435 }
436
437 SInt32 CFAbsoluteTimeGetDayOfYear(CFAbsoluteTime at, CFTimeZoneRef tz) {
438 CFAbsoluteTime fixedat;
439 int32_t absolute, year;
440 int8_t month, day;
441 if (NULL != tz) {
442 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
443 }
444 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
445 absolute = (int32_t)(float)floor(fixedat / 86400.0);
446 __CFYMDFromAbsolute(absolute, &year, &month, &day);
447 return __CFDaysBeforeMonth(month, year, isleap(year)) + day;
448 }
449
450 /* "the first week of a year is the one which includes the first Thursday" (ISO 8601) */
451 SInt32 CFAbsoluteTimeGetWeekOfYear(CFAbsoluteTime at, CFTimeZoneRef tz) {
452 int32_t absolute, year;
453 int8_t month, day;
454 int32_t absolute0101, dow0101;
455 CFAbsoluteTime fixedat;
456 if (NULL != tz) {
457 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
458 }
459 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
460 absolute = (int32_t)(float)floor(fixedat / 86400.0);
461 __CFYMDFromAbsolute(absolute, &year, &month, &day);
462 absolute0101 = __CFAbsoluteFromYMD(year, 1, 1);
463 dow0101 = (absolute0101 < 0) ? ((absolute0101 + 1) % 7 + 7) : (absolute0101 % 7 + 1);
464 /* First three and last three days of a year can end up in a week of a different year */
465 if (1 == month && day < 4) {
466 if ((day < 4 && 5 == dow0101) || (day < 3 && 6 == dow0101) || (day < 2 && 7 == dow0101)) {
467 return 53;
468 }
469 }
470 if (12 == month && 28 < day) {
471 int32_t absolute20101, dow20101;
472 absolute20101 = __CFAbsoluteFromYMD(year + 1, 1, 1);
473 dow20101 = (absolute20101 < 0) ? ((absolute20101 + 1) % 7 + 7) : (absolute20101 % 7 + 1);
474 if ((28 < day && 4 == dow20101) || (29 < day && 3 == dow20101) || (30 < day && 2 == dow20101)) {
475 return 1;
476 }
477 }
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;
480 }
481
482