/ CFDate.c
CFDate.c
  1  /*
  2   * Copyright (c) 2015 Apple 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  
 24  /*	CFDate.c
 25  	Copyright (c) 1998-2014, Apple Inc. All rights reserved.
 26  	Responsibility: Christopher Kane
 27  */
 28  
 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"
 36  #include <math.h>
 37  #include <dispatch/dispatch.h>
 38  
 39  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
 40  #include <sys/time.h>
 41  #endif
 42  
 43  #define DEFINE_CFDATE_FUNCTIONS 1
 44  
 45  /* cjk: The Julian Date for the reference date is 2451910.5,
 46          I think, in case that's ever useful. */
 47  
 48  #if DEFINE_CFDATE_FUNCTIONS
 49  
 50  const CFTimeInterval kCFAbsoluteTimeIntervalSince1970 = 978307200.0L;
 51  const CFTimeInterval kCFAbsoluteTimeIntervalSince1904 = 3061152000.0L;
 52  
 53  CF_PRIVATE double __CFTSRRate = 0.0;
 54  static double __CF1_TSRRate = 0.0;
 55  
 56  CF_PRIVATE uint64_t __CFTimeIntervalToTSR(CFTimeInterval ti) {
 57      if ((ti * __CFTSRRate) > INT64_MAX / 2) return (INT64_MAX / 2);
 58      return (uint64_t)(ti * __CFTSRRate);
 59  }
 60  
 61  CF_PRIVATE CFTimeInterval __CFTSRToTimeInterval(uint64_t tsr) {
 62      return (CFTimeInterval)((double)tsr * __CF1_TSRRate);
 63  }
 64  
 65  CF_PRIVATE CFTimeInterval __CFTimeIntervalUntilTSR(uint64_t tsr) {
 66      CFDateGetTypeID();
 67      uint64_t now = mach_absolute_time();
 68      if (tsr >= now) {
 69          return __CFTSRToTimeInterval(tsr - now);
 70      } else {
 71          return -__CFTSRToTimeInterval(now - tsr);
 72      }
 73  }
 74  
 75  // Technically this is 'TSR units' not a strict 'TSR' absolute time
 76  CF_PRIVATE uint64_t __CFTSRToNanoseconds(uint64_t tsr) {
 77      double tsrInNanoseconds = floor(tsr * __CF1_TSRRate * NSEC_PER_SEC);
 78      uint64_t ns = (uint64_t)tsrInNanoseconds;
 79      return ns;
 80  }
 81  
 82  CF_PRIVATE dispatch_time_t __CFTSRToDispatchTime(uint64_t tsr) {
 83      uint64_t tsrInNanoseconds = __CFTSRToNanoseconds(tsr);
 84      
 85      // It's important to clamp this value to INT64_MAX or it will become interpreted by dispatch_time as a relative value instead of absolute time
 86      if (tsrInNanoseconds > INT64_MAX - 1) tsrInNanoseconds = INT64_MAX - 1;
 87      
 88      // 2nd argument of dispatch_time is a value in nanoseconds, but tsr does not equal nanoseconds on all platforms.
 89      return dispatch_time(1, (int64_t)tsrInNanoseconds);
 90  }
 91  
 92  CFAbsoluteTime CFAbsoluteTimeGetCurrent(void) {
 93      CFAbsoluteTime ret;
 94      struct timeval tv;
 95      gettimeofday(&tv, NULL);
 96      ret = (CFTimeInterval)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970;
 97      ret += (1.0E-6 * (CFTimeInterval)tv.tv_usec);
 98      return ret;
 99  }
100  
101  struct __CFDate {
102      CFRuntimeBase _base;
103      CFAbsoluteTime _time;       /* immutable */
104  };
105  
106  static Boolean __CFDateEqual(CFTypeRef cf1, CFTypeRef cf2) {
107      CFDateRef date1 = (CFDateRef)cf1;
108      CFDateRef date2 = (CFDateRef)cf2;
109      if (date1->_time != date2->_time) return false;
110      return true;
111  }
112  
113  static CFHashCode __CFDateHash(CFTypeRef cf) {
114      CFDateRef date = (CFDateRef)cf;
115      return (CFHashCode)(float)floor(date->_time);
116  }
117  
118  static CFStringRef __CFDateCopyDescription(CFTypeRef cf) {
119      CFDateRef date = (CFDateRef)cf;
120      return CFStringCreateWithFormat(CFGetAllocator(date), NULL, CFSTR("<CFDate %p [%p]>{time = %0.09g}"), cf, CFGetAllocator(date), date->_time);
121  }
122  
123  static CFTypeID __kCFDateTypeID = _kCFRuntimeNotATypeID;
124  
125  static const CFRuntimeClass __CFDateClass = {
126      0,
127      "CFDate",
128      NULL,       // init
129      NULL,       // copy
130      NULL,       // dealloc
131      __CFDateEqual,
132      __CFDateHash,
133      NULL,       //
134      __CFDateCopyDescription
135  };
136  
137  CFTypeID CFDateGetTypeID(void) {
138      static dispatch_once_t initOnce;
139      dispatch_once(&initOnce, ^{
140          __kCFDateTypeID = _CFRuntimeRegisterClass(&__CFDateClass); 
141  
142          // HACK: this effectively reserves the type ID for the date class.
143          //       this ensures that date objects are treated like ObjC objects and not CF ones.
144          //       Apple's newer CF returns a static value for the CFDate type ID and never actually registers a CFDate class.
145          //       instead, they just use the ObjC class for everything.
146          if (__kCFDateTypeID != _kCFRuntimeNotATypeID) {
147              _CFRuntimeUnregisterClassWithTypeID(__kCFDateTypeID);
148          }
149  
150  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
151      struct mach_timebase_info info;
152      mach_timebase_info(&info);
153      __CFTSRRate = (1.0E9 / (double)info.numer) * (double)info.denom;
154      __CF1_TSRRate = 1.0 / __CFTSRRate;
155  #elif DEPLOYMENT_TARGET_WINDOWS
156      LARGE_INTEGER freq;
157      if (!QueryPerformanceFrequency(&freq)) {
158          HALT;
159      }
160      __CFTSRRate = (double)freq.QuadPart;
161      __CF1_TSRRate = 1.0 / __CFTSRRate;
162  #elif DEPLOYMENT_TARGET_LINUX
163      struct timespec res;
164      if (clock_getres(CLOCK_MONOTONIC, &res) != 0) {
165          HALT;
166      }
167      __CFTSRRate = res.tv_sec + (1000000000 * res.tv_nsec);
168      __CF1_TSRRate = 1.0 / __CFTSRRate;
169  #else
170  #error Unable to initialize date
171  #endif
172      });
173      return __kCFDateTypeID;
174  }
175  
176  #endif
177  
178  CF_INLINE int32_t __CFDoubleModToInt(double d, int32_t modulus) {
179      int32_t result = (int32_t)(float)floor(d - floor(d / modulus) * modulus);
180      if (result < 0) result += modulus;
181      return result;
182  }
183  
184  CF_INLINE double __CFDoubleMod(double d, int32_t modulus) {
185      double result = d - floor(d / modulus) * modulus;
186      if (result < 0.0) result += (double)modulus;
187      return result;
188  }
189  
190  static const uint8_t daysInMonth[16] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0, 0, 0};
191  static const uint16_t daysBeforeMonth[16] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0};
192  static const uint16_t daysAfterMonth[16] = {365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0, 0, 0, 0};
193  
194  CF_INLINE bool isleap(int64_t year) {
195      int64_t y = (year + 1) % 400;	/* correct to nearest multiple-of-400 year, then find the remainder */
196      if (y < 0) y = -y;
197      return (0 == (y & 3) && 100 != y && 200 != y && 300 != y);
198  }
199  
200  /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
201  CF_INLINE uint8_t __CFDaysInMonth(int8_t month, int64_t year, bool leap) {
202      return daysInMonth[month] + (2 == month && leap);
203  }
204  
205  /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
206  CF_INLINE uint16_t __CFDaysBeforeMonth(int8_t month, int64_t year, bool leap) {
207      return daysBeforeMonth[month] + (2 < month && leap);
208  }
209  
210  /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
211  CF_INLINE uint16_t __CFDaysAfterMonth(int8_t month, int64_t year, bool leap) {
212      return daysAfterMonth[month] + (month < 2 && leap);
213  }
214  
215  /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
216  static void __CFYMDFromAbsolute(int64_t absolute, int64_t *year, int8_t *month, int8_t *day) {
217      int64_t b = absolute / 146097; // take care of as many multiples of 400 years as possible
218      int64_t y = b * 400;
219      uint16_t ydays;
220      absolute -= b * 146097;
221      while (absolute < 0) {
222  	y -= 1;
223  	absolute += __CFDaysAfterMonth(0, y, isleap(y));
224      }
225      /* Now absolute is non-negative days to add to year */
226      ydays = __CFDaysAfterMonth(0, y, isleap(y));
227      while (ydays <= absolute) {
228  	y += 1;
229  	absolute -= ydays;
230  	ydays = __CFDaysAfterMonth(0, y, isleap(y));
231      }
232      /* Now we have year and days-into-year */
233      if (year) *year = y;
234      if (month || day) {
235  	int8_t m = absolute / 33 + 1; /* search from the approximation */
236  	bool leap = isleap(y);
237  	while (__CFDaysBeforeMonth(m + 1, y, leap) <= absolute) m++;
238  	if (month) *month = m;
239  	if (day) *day = absolute - __CFDaysBeforeMonth(m, y, leap) + 1;
240      }
241  }
242  
243  /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
244  static double __CFAbsoluteFromYMD(int64_t year, int8_t month, int8_t day) {
245      double absolute = 0.0;
246      int64_t idx;
247      int64_t b = year / 400; // take care of as many multiples of 400 years as possible
248      absolute += b * 146097.0;
249      year -= b * 400;
250      if (year < 0) {
251  	for (idx = year; idx < 0; idx++)
252  	    absolute -= __CFDaysAfterMonth(0, idx, isleap(idx));
253      } else {
254  	for (idx = 0; idx < year; 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) && (unitFlags & kCFGregorianUnitsMonths) && (unitFlags & kCFGregorianUnitsYears) && (__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      at = 86400.0 * __CFAbsoluteFromYMD(gdate.year - 2001, gdate.month, gdate.day);
276      at += 3600.0 * gdate.hour + 60.0 * gdate.minute + gdate.second;
277  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
278      if (NULL != tz) {
279  	__CFGenericValidateType(tz, CFTimeZoneGetTypeID());
280      }
281      CFTimeInterval offset0, offset1;
282      if (NULL != tz) {
283  	offset0 = CFTimeZoneGetSecondsFromGMT(tz, at);
284  	offset1 = CFTimeZoneGetSecondsFromGMT(tz, at - offset0);
285  	at -= offset1;
286      }
287  #endif
288      return at;
289  }
290  
291  CFGregorianDate CFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at, CFTimeZoneRef tz) {
292      CFGregorianDate gdate;
293      int64_t absolute, year;
294      int8_t month, day;
295      CFAbsoluteTime fixedat;
296  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
297      if (NULL != tz) {
298  	__CFGenericValidateType(tz, CFTimeZoneGetTypeID());
299      }
300      fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
301  #else
302      fixedat = at;
303  #endif
304      absolute = (int64_t)floor(fixedat / 86400.0);
305      __CFYMDFromAbsolute(absolute, &year, &month, &day);
306      if (INT32_MAX - 2001 < year) year = INT32_MAX - 2001;
307      gdate.year = year + 2001;
308      gdate.month = month;
309      gdate.day = day;
310      gdate.hour = __CFDoubleModToInt(floor(fixedat / 3600.0), 24);
311      gdate.minute = __CFDoubleModToInt(floor(fixedat / 60.0), 60);
312      gdate.second = __CFDoubleMod(fixedat, 60);
313      if (0.0 == gdate.second) gdate.second = 0.0;	// stomp out possible -0.0
314      return gdate;
315  }
316  
317  /* Note that the units of years and months are not equal length, but are treated as such. */
318  CFAbsoluteTime CFAbsoluteTimeAddGregorianUnits(CFAbsoluteTime at, CFTimeZoneRef tz, CFGregorianUnits units) {
319      CFGregorianDate gdate;
320      CFGregorianUnits working;
321      CFAbsoluteTime candidate_at0, candidate_at1;
322      uint8_t monthdays;
323  
324  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
325      if (NULL != tz) {
326  	__CFGenericValidateType(tz, CFTimeZoneGetTypeID());
327      }
328  #endif
329      
330      /* Most people seem to expect years, then months, then days, etc.
331  	to be added in that order.  Thus, 27 April + (4 days, 1 month)
332  	= 31 May, and not 1 June. This is also relatively predictable.
333  
334  	On another issue, months not being equal length, people also
335  	seem to expect late day-of-month clamping (don't clamp as you
336  	go through months), but clamp before adding in the days. Late
337  	clamping is also more predictable given random starting points
338  	and random numbers of months added (ie Jan 31 + 2 months could
339  	be March 28 or March 29 in different years with aggressive
340  	clamping). Proportionality (28 Feb + 1 month = 31 March) is
341  	also not expected.
342  
343  	Also, people don't expect time zone transitions to have any
344  	effect when adding years and/or months and/or days, only.
345  	Hours, minutes, and seconds, though, are added in as humans
346  	would experience the passing of that time. What this means
347  	is that if the date, after adding years, months, and days
348  	lands on some date, and then adding hours, minutes, and
349  	seconds crosses a time zone transition, the time zone
350  	transition is accounted for. If adding years, months, and
351  	days gets the date into a different time zone offset period,
352  	that transition is not taken into account.
353      */
354      gdate = CFAbsoluteTimeGetGregorianDate(at, tz);
355      /* We must work in a CFGregorianUnits, because the fields in the CFGregorianDate can easily overflow */
356      working.years = gdate.year;
357      working.months = gdate.month;
358      working.days = gdate.day;
359      working.years += units.years;
360      working.months += units.months;
361      while (12 < working.months) {
362  	working.months -= 12;
363  	working.years += 1;
364      }
365      while (working.months < 1) {
366  	working.months += 12;
367  	working.years -= 1;
368      }
369      monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001));
370      if (monthdays < working.days) {	/* Clamp day to new month */
371  	working.days = monthdays;
372      }
373      working.days += units.days;
374      while (monthdays < working.days) {
375  	working.months += 1;
376  	if (12 < working.months) {
377  	    working.months -= 12;
378  	    working.years += 1;
379  	}
380  	working.days -= monthdays;
381  	monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001));
382      }
383      while (working.days < 1) {
384  	working.months -= 1;
385  	if (working.months < 1) {
386  	    working.months += 12;
387  	    working.years -= 1;
388  	}
389  	monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001));
390  	working.days += monthdays;
391      }
392      gdate.year = working.years;
393      gdate.month = working.months;
394      gdate.day = working.days;
395      /* Roll in hours, minutes, and seconds */
396      candidate_at0 = CFGregorianDateGetAbsoluteTime(gdate, tz);
397      candidate_at1 = candidate_at0 + 3600.0 * units.hours + 60.0 * units.minutes + units.seconds;
398      /* If summing in the hours, minutes, and seconds delta pushes us
399       * into a new time zone offset, that will automatically be taken
400       * care of by the fact that we just add the raw time above. To
401       * undo that effect, we'd have to get the time zone offsets for
402       * candidate_at0 and candidate_at1 here, and subtract the
403       * difference (offset1 - offset0) from candidate_at1. */
404      return candidate_at1;
405  }
406  
407  /* at1 - at2.  The only constraint here is that this needs to be the inverse
408  of CFAbsoluteTimeByAddingGregorianUnits(), but that's a very rigid constraint.
409  Unfortunately, due to the nonuniformity of the year and month units, this
410  inversion essentially has to approximate until it finds the answer. */
411  CFGregorianUnits CFAbsoluteTimeGetDifferenceAsGregorianUnits(CFAbsoluteTime at1, CFAbsoluteTime at2, CFTimeZoneRef tz, CFOptionFlags unitFlags) {
412      const int32_t seconds[5] = {366 * 24 * 3600, 31 * 24 * 3600, 24 * 3600, 3600, 60};
413      CFGregorianUnits units = {0, 0, 0, 0, 0, 0.0};
414      CFAbsoluteTime atold, atnew = at2;
415      int32_t idx, incr;
416      incr = (at2 < at1) ? 1 : -1;
417      /* Successive approximation: years, then months, then days, then hours, then minutes. */
418      for (idx = 0; idx < 5; idx++) {
419  	if (unitFlags & (1 << idx)) {
420  	    ((int32_t *)&units)[idx] = -3 * incr + (int32_t)((at1 - atnew) / seconds[idx]);
421  	    do {
422  		atold = atnew;
423  		((int32_t *)&units)[idx] += incr;
424  		atnew = CFAbsoluteTimeAddGregorianUnits(at2, tz, units);
425  	    } while ((1 == incr && atnew <= at1) || (-1 == incr && at1 <= atnew));
426  	    ((int32_t *)&units)[idx] -= incr;
427  	    atnew = atold;
428  	}
429      }
430      if (unitFlags & kCFGregorianUnitsSeconds) {
431  	units.seconds = at1 - atnew;
432      }
433      if (0.0 == units.seconds) units.seconds = 0.0;	// stomp out possible -0.0
434      return units;
435  }
436  
437  SInt32 CFAbsoluteTimeGetDayOfWeek(CFAbsoluteTime at, CFTimeZoneRef tz) {
438      int64_t absolute;
439      CFAbsoluteTime fixedat;
440  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
441      if (NULL != tz) {
442  	__CFGenericValidateType(tz, CFTimeZoneGetTypeID());
443      }
444      fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
445  #else
446      fixedat = at;
447  #endif
448      absolute = (int64_t)floor(fixedat / 86400.0);
449      return (absolute < 0) ? ((absolute + 1) % 7 + 7) : (absolute % 7 + 1); /* Monday = 1, etc. */
450  }
451  
452  SInt32 CFAbsoluteTimeGetDayOfYear(CFAbsoluteTime at, CFTimeZoneRef tz) {
453      CFAbsoluteTime fixedat;
454      int64_t absolute, year;
455      int8_t month, day;
456  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
457      if (NULL != tz) {
458  	__CFGenericValidateType(tz, CFTimeZoneGetTypeID());
459      }
460      fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
461  #else
462      fixedat = at;
463  #endif
464      absolute = (int64_t)floor(fixedat / 86400.0);
465      __CFYMDFromAbsolute(absolute, &year, &month, &day);
466      return __CFDaysBeforeMonth(month, year, isleap(year)) + day;
467  }
468  
469  /* "the first week of a year is the one which includes the first Thursday" (ISO 8601) */
470  SInt32 CFAbsoluteTimeGetWeekOfYear(CFAbsoluteTime at, CFTimeZoneRef tz) {
471      int64_t absolute, year;
472      int8_t month, day;
473      CFAbsoluteTime fixedat;
474  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
475      if (NULL != tz) {
476  	__CFGenericValidateType(tz, CFTimeZoneGetTypeID());
477      }
478      fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
479  #else
480      fixedat = at;
481  #endif
482      absolute = (int64_t)floor(fixedat / 86400.0);
483      __CFYMDFromAbsolute(absolute, &year, &month, &day);
484      double absolute0101 = __CFAbsoluteFromYMD(year, 1, 1);
485      int64_t dow0101 = __CFDoubleModToInt(absolute0101, 7) + 1;
486      /* First three and last three days of a year can end up in a week of a different year */
487      if (1 == month && day < 4) {
488  	if ((day < 4 && 5 == dow0101) || (day < 3 && 6 == dow0101) || (day < 2 && 7 == dow0101)) {
489  	    return 53;
490  	}
491      }
492      if (12 == month && 28 < day) {
493  	double absolute20101 = __CFAbsoluteFromYMD(year + 1, 1, 1);
494  	int64_t dow20101 = __CFDoubleModToInt(absolute20101, 7) + 1;
495  	if ((28 < day && 4 == dow20101) || (29 < day && 3 == dow20101) || (30 < day && 2 == dow20101)) {
496  	    return 1;
497  	}
498      }
499      /* Days into year, plus a week-shifting correction, divided by 7. First week is 1. */
500      return (__CFDaysBeforeMonth(month, year, isleap(year)) + day + (dow0101 - 11) % 7 + 2) / 7 + 1;
501  }
502  
503