/ CoreFoundation / Locale.subproj / CFDateComponents.c
CFDateComponents.c
  1  /*    CFDateComponents.c
  2        Copyright (c) 2004-2019, Apple Inc. and the Swift project authors
  3  
  4        Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors
  5        Licensed under Apache License v2.0 with Runtime Library Exception
  6        See http://swift.org/LICENSE.txt for license information
  7        See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
  8        Responsibility: Itai Ferber
  9   */
 10  
 11  #include <assert.h>
 12  #include <CoreFoundation/CFCalendar.h>
 13  #include <CoreFoundation/CFString.h>
 14  #include "CFDateComponents.h"
 15  #include "CFInternal.h"
 16  #include "CFCalendar_Internal.h"
 17  #include "CFRuntime_Internal.h"
 18  
 19  static Boolean __CFDateComponentsEqual(CFTypeRef cf1, CFTypeRef cf2) {
 20      assert(NULL != cf1);
 21      assert(NULL != cf2);
 22      CFDateComponentsRef dc1 = (CFDateComponentsRef)cf1;
 23      CFDateComponentsRef dc2 = (CFDateComponentsRef)cf2;
 24      if (dc1->_era != dc2->_era) return false;
 25      if (dc1->_year != dc2->_year) return false;
 26      if (dc1->_quarter != dc2->_quarter) return false;
 27      if (dc1->_month != dc2->_month) return false;
 28      if (dc1->_day != dc2->_day) return false;
 29      if (dc1->_hour != dc2->_hour) return false;
 30      if (dc1->_minute != dc2->_minute) return false;
 31      if (dc1->_second != dc2->_second) return false;
 32      if (dc1->_nanosecond != dc2->_nanosecond) return false;
 33      if (dc1->_week != dc2->_week) return false;
 34      if (dc1->_weekOfYear != dc2->_weekOfYear) return false;
 35      if (dc1->_weekOfMonth != dc2->_weekOfMonth) return false;
 36      if (dc1->_yearForWeekOfYear != dc2->_yearForWeekOfYear) return false;
 37      if (dc1->_weekday != dc2->_weekday) return false;
 38      if (dc1->_weekdayOrdinal != dc2->_weekdayOrdinal) return false;
 39      // TODO: NSDateComponents would compare leapMonth, not checking isLeapMonthSet first. 'isLeapMonth' returns NO in the case where 'isLeapMonthSet' returns NO.
 40      // This also manifested as a bug where setting leapMonth -> NO meant that the 'isLeapMonthSet' was false after decoding the archive, because the value was only set on the decoded NSDateComponents if 'leapMonth' was YES.
 41      // For now, we will use the same logic as before, and look into seeing if we can change that behavior for encoding later.
 42      if (!((dc1->_leapMonth == 0 && dc2->_leapMonth == CFDateComponentUndefined) ||
 43            (dc1->_leapMonth == CFDateComponentUndefined && dc2->_leapMonth == 0) ||
 44            (dc1->_leapMonth == dc2->_leapMonth))) {
 45          return false;
 46      }
 47      if ((dc1->_calendar && !dc2->_calendar) || (!dc1->_calendar && dc2->_calendar)) return false;
 48      if (dc1->_calendar && dc2->_calendar && !CFEqual(dc1->_calendar, dc2->_calendar)) return false;
 49      if ((dc1->_timeZone && !dc2->_timeZone) || (!dc1->_timeZone && dc2->_timeZone)) return false;
 50      if (dc1->_timeZone && dc2->_timeZone && !CFEqual(dc1->_timeZone, dc2->_timeZone)) return false;
 51      return true;
 52  }
 53  
 54  static CFHashCode __CFDateComponentsHash(CFTypeRef cf) {
 55      assert(NULL != cf);
 56      CFDateComponentsRef dc = (CFDateComponentsRef)cf;
 57      CFIndex calHash = dc->_calendar ? CFHash(dc->_calendar) : 0;
 58      CFIndex tzHash = dc->_timeZone ? CFHash(dc->_timeZone) : 0;
 59      CFIndex calTzHash = calHash ^ tzHash;
 60      CFIndex y = dc->_year; if (y == CFDateComponentUndefined) y = 0;
 61      CFIndex m = dc->_month; if (m == CFDateComponentUndefined) m = 0;
 62      CFIndex d = dc->_day; if (d == CFDateComponentUndefined) d = 0;
 63      CFIndex h = dc->_hour; if (h == CFDateComponentUndefined) h = 0;
 64      CFIndex mm = dc->_minute; if (mm == CFDateComponentUndefined) mm = 0;
 65      CFIndex s = dc->_second; if (s == CFDateComponentUndefined) s = 0;
 66      CFIndex yy = dc->_yearForWeekOfYear; if (yy == CFDateComponentUndefined) yy = 0;
 67      CFIndex hash = calTzHash + (32832013 * (y + yy) + 2678437 * m + 86413 * d + 3607 * h + 61 * mm + s);
 68      hash = hash + (41 * dc->_weekOfYear + 11 * dc->_weekOfMonth + 7 * dc->_weekday + 3 * dc->_weekdayOrdinal + dc->_quarter) * (1ULL << 5);
 69      return hash;
 70  }
 71  
 72  Boolean CFDateComponentsIsLeapMonthSet(CFDateComponentsRef dc) {
 73      return dc->_leapMonth != CFDateComponentUndefined;
 74  }
 75  
 76  Boolean CFDateComponentsIsLeapMonth(CFDateComponentsRef dc) {
 77      return (CFDateComponentUndefined != dc->_leapMonth && dc->_leapMonth) ? true : false;
 78  }
 79  
 80  CFStringRef _CFDateComponentsCopyDescriptionInner(CFDateComponentsRef dc) {
 81      CFMutableStringRef mstr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
 82      CFStringAppend(mstr, CFSTR("{"));
 83      CFCalendarRef cal = dc->_calendar;
 84      if (cal) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Calendar: %@"), cal);
 85      CFTimeZoneRef tz = dc->_timeZone;
 86      if (tz) CFStringAppendFormat(mstr, NULL, CFSTR("\n    TimeZone: %@"), tz);
 87      CFIndex val = dc->_era;
 88      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Era: %ld"), (long)val);
 89      val = dc->_year;
 90      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Calendar Year: %ld"), (long)val);
 91      val = dc->_month;
 92      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Month: %ld"), (long)val);
 93      val = dc->_leapMonth;
 94      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Leap Month: %ld"), (long)val);
 95      val = dc->_day;
 96      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Day: %ld"), (long)val);
 97      val = dc->_hour;
 98      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Hour: %ld"), (long)val);
 99      val = dc->_minute;
100      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Minute: %ld"), (long)val);
101      val = dc->_second;
102      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Second: %ld"), (long)val);
103      val = dc->_nanosecond;
104      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Nanosecond: %ld"), (long)val);
105      val = dc->_quarter;
106      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Quarter: %ld"), (long)val);
107      val = dc->_yearForWeekOfYear;
108      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Year for Week of Year: %ld"), (long)val);
109      val = dc->_weekOfYear;
110      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Week of Year: %ld"), (long)val);
111      val = dc->_weekOfMonth;
112      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Week of Month: %ld"), (long)val);
113  #pragma GCC diagnostic push
114  #pragma GCC diagnostic ignored "-Wdeprecated"
115      val = dc->_week;
116      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Week (obsolete): %ld"), (long)val);
117  #pragma GCC diagnostic pop
118      val = dc->_weekday;
119      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Weekday: %ld"), (long)val);
120      val = dc->_weekdayOrdinal;
121      if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n    Weekday Ordinal: %ld"), (long)val);
122      return mstr;
123  }
124  
125  static CFStringRef __CFDateComponentsCopyDescription(CFTypeRef cf) {
126      assert(NULL != cf);
127      CFDateComponentsRef dc = (CFDateComponentsRef)cf;
128      CFStringRef interiorDescription = _CFDateComponentsCopyDescriptionInner(dc);
129      CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFDateComponents %p [%p]>%@"), dc, CFGetAllocator(dc), interiorDescription);
130      CFRelease(interiorDescription);
131      return result;
132  }
133  
134  static void __CFDateComponentsDeallocate(CFTypeRef cf) {
135      assert(NULL != cf);
136      CFDateComponentsRef dc = (CFDateComponentsRef)cf;
137      if (dc->_calendar) CFRelease(dc->_calendar);
138      if (dc->_timeZone) CFRelease(dc->_timeZone);
139  }
140  
141  const CFRuntimeClass __CFDateComponentsClass = {
142      0,
143      "CFDateComponents",
144      NULL,   // init
145      NULL,   // copy
146      __CFDateComponentsDeallocate,
147      __CFDateComponentsEqual,
148      __CFDateComponentsHash,
149      NULL,   //
150      __CFDateComponentsCopyDescription
151  };
152  
153  CFTypeID CFDateComponentsGetTypeID(void) {
154      return _kCFRuntimeIDCFDateComponents;
155  }
156  /* End Runtime setup */
157  
158  CFDateComponentsRef CFDateComponentsCreate(CFAllocatorRef allocator) {
159      if (!allocator) allocator = CFAllocatorGetDefault();
160      struct __CFDateComponents *dc = NULL;
161      uint32_t size = sizeof(struct __CFDateComponents) - sizeof(CFRuntimeBase);
162      dc = (struct __CFDateComponents *)_CFRuntimeCreateInstance(allocator, CFDateComponentsGetTypeID(), size, NULL);
163      if (NULL == dc) return NULL;
164      dc->_calendar = NULL;
165      dc->_timeZone = NULL;
166      dc->_era = CFDateComponentUndefined;
167      dc->_year = CFDateComponentUndefined;
168      dc->_month = CFDateComponentUndefined;
169      dc->_leapMonth = CFDateComponentUndefined;
170      dc->_day = CFDateComponentUndefined;
171      dc->_hour = CFDateComponentUndefined;
172      dc->_minute = CFDateComponentUndefined;
173      dc->_second = CFDateComponentUndefined;
174      dc->_week = CFDateComponentUndefined;
175      dc->_weekday = CFDateComponentUndefined;
176      dc->_weekdayOrdinal = CFDateComponentUndefined;
177      dc->_quarter = CFDateComponentUndefined;
178      dc->_weekOfMonth = CFDateComponentUndefined;
179      dc->_weekOfYear = CFDateComponentUndefined;
180      dc->_yearForWeekOfYear = CFDateComponentUndefined;
181      dc->_nanosecond = CFDateComponentUndefined;
182      return dc;
183  }
184  
185  CFDateComponentsRef CFDateComponentsCreateCopy(CFAllocatorRef allocator, CFDateComponentsRef dc) {
186      CFDateComponentsRef result = CFDateComponentsCreate(allocator);
187      if (!result) HALT_MSG("Out of memory");
188      
189      CFCalendarRef cal = CFDateComponentsCopyCalendar(dc);
190      if (cal) {
191          CFDateComponentsSetCalendar(result, cal);
192          CFRelease(cal);
193      }
194      CFTimeZoneRef tz = CFDateComponentsCopyTimeZone(dc);
195      if (tz) {
196          CFDateComponentsSetTimeZone(result, tz);
197          CFRelease(tz);
198      }
199      
200      // Just reach in for the rest
201      result->_era = dc->_era;
202      result->_year = dc->_year;
203      result->_month = dc->_month;
204      result->_leapMonth = dc->_leapMonth;
205      result->_day = dc->_day;
206      result->_hour = dc->_hour;
207      result->_minute = dc->_minute;
208      result->_second = dc->_second;
209      result->_week = dc->_week;
210      result->_weekday = dc->_weekday;
211      result->_weekdayOrdinal = dc->_weekdayOrdinal;
212      result->_quarter = dc->_quarter;
213      result->_weekOfMonth = dc->_weekOfMonth;
214      result->_weekOfYear = dc->_weekOfYear;
215      result->_yearForWeekOfYear = dc->_yearForWeekOfYear;
216      result->_nanosecond = dc->_nanosecond;
217  
218      return result;
219  }
220  
221  CFIndex CFDateComponentsGetValue(CFDateComponentsRef dateComp, CFCalendarUnit unit) {
222      assert(NULL != dateComp);
223      CFIndex val = CFDateComponentUndefined;
224      switch (unit) {
225          case kCFCalendarUnitEra:
226              val = dateComp->_era;
227              break;
228          case kCFCalendarUnitYear:
229              val = dateComp->_year;
230              break;
231          case kCFCalendarUnitMonth:
232              val = dateComp->_month;
233              break;
234          case kCFCalendarUnitLeapMonth:
235              val = dateComp->_leapMonth;
236              break;
237          case kCFCalendarUnitDay:
238              val = dateComp->_day;
239              break;
240          case kCFCalendarUnitHour:
241              val = dateComp->_hour;
242              break;
243          case kCFCalendarUnitMinute:
244              val = dateComp->_minute;
245              break;
246          case kCFCalendarUnitSecond:
247              val = dateComp->_second;
248              break;
249  #pragma GCC diagnostic push
250  #pragma GCC diagnostic ignored "-Wdeprecated"
251          case kCFCalendarUnitWeek:
252              val = dateComp->_week;
253              break;
254  #pragma GCC diagnostic pop
255          case kCFCalendarUnitWeekday:
256              val = dateComp->_weekday;
257              break;
258          case kCFCalendarUnitWeekdayOrdinal:
259              val = dateComp->_weekdayOrdinal;
260              break;
261          case kCFCalendarUnitQuarter:
262              val = dateComp->_quarter;
263              break;
264          case kCFCalendarUnitWeekOfMonth:
265              val = dateComp->_weekOfMonth;
266              break;
267          case kCFCalendarUnitWeekOfYear:
268              val = dateComp->_weekOfYear;
269              break;
270          case kCFCalendarUnitYearForWeekOfYear:
271              val = dateComp->_yearForWeekOfYear;
272              break;
273          case kCFCalendarUnitNanosecond:
274              val = dateComp->_nanosecond;
275              break;
276          default:
277              // Unknown units are ignored for forwards compatibility
278              break;
279      }
280      return val;
281  }
282  
283  void CFDateComponentsSetValue(CFDateComponentsRef dateComp, CFCalendarUnit unit, CFIndex value) {
284      assert(NULL != dateComp);
285      switch (unit) {
286          case kCFCalendarUnitEra:
287              dateComp->_era = value;
288              break;
289          case kCFCalendarUnitYear:
290              dateComp->_year = value;
291              break;
292          case kCFCalendarUnitMonth:
293              dateComp->_month = value;
294              break;
295          case kCFCalendarUnitLeapMonth:
296              dateComp->_leapMonth = value;
297              break;
298          case kCFCalendarUnitDay:
299              dateComp->_day = value;
300              break;
301          case kCFCalendarUnitHour:
302              dateComp->_hour = value;
303              break;
304          case kCFCalendarUnitMinute:
305              dateComp->_minute = value;
306              break;
307          case kCFCalendarUnitSecond:
308              dateComp->_second = value;
309              break;
310  #pragma GCC diagnostic push
311  #pragma GCC diagnostic ignored "-Wdeprecated"
312          case kCFCalendarUnitWeek:
313              dateComp->_week = value;
314              break;
315  #pragma GCC diagnostic pop
316          case kCFCalendarUnitWeekday:
317              dateComp->_weekday = value;
318              break;
319          case kCFCalendarUnitWeekdayOrdinal:
320              dateComp->_weekdayOrdinal = value;
321              break;
322          case kCFCalendarUnitQuarter:
323              dateComp->_quarter = value;
324              break;
325          case kCFCalendarUnitWeekOfMonth:
326              dateComp->_weekOfMonth = value;
327              break;
328          case kCFCalendarUnitWeekOfYear:
329              dateComp->_weekOfYear = value;
330              break;
331          case kCFCalendarUnitYearForWeekOfYear:
332              dateComp->_yearForWeekOfYear = value;
333              break;
334          case kCFCalendarUnitNanosecond:
335              dateComp->_nanosecond = value;
336              break;
337          default:
338              // Unknown units are ignored for forwards compatibility
339              break;
340      }
341  }
342  
343  CFCalendarRef CFDateComponentsCopyCalendar(CFDateComponentsRef dateComp) {
344      assert(NULL != dateComp);
345      if (dateComp->_calendar) {
346          return (CFCalendarRef)CFRetain(dateComp->_calendar);
347      } else {
348          return NULL;
349      }
350  }
351  
352  void CFDateComponentsSetCalendar(CFDateComponentsRef dateComp, CFCalendarRef calendar) {
353      assert(NULL != dateComp);
354      CFCalendarRef currCal = dateComp->_calendar;
355      if (calendar && currCal) {
356          if (CFEqual(currCal, calendar)) return;
357      }
358      if (currCal) {
359          CFRelease(dateComp->_calendar);
360          dateComp->_calendar = NULL;
361      }
362      if (calendar) {
363          // We copy the calendar, and set its time zone to match the one in the current date components if required
364          CFCalendarRef calCopy = _CFCalendarCreateCopy(kCFAllocatorSystemDefault, calendar);
365          
366          if (dateComp->_timeZone) {
367              CFCalendarSetTimeZone(calCopy, dateComp->_timeZone);
368          }
369          dateComp->_calendar = calCopy;
370      }
371  }
372  
373  CFTimeZoneRef CFDateComponentsCopyTimeZone(CFDateComponentsRef dateComp) {
374      assert(NULL != dateComp);
375      if (dateComp->_timeZone) {
376          return (CFTimeZoneRef)CFRetain(dateComp->_timeZone);
377      } else {
378          return NULL;
379      }
380  }
381  
382  void CFDateComponentsSetTimeZone(CFDateComponentsRef dateComp, CFTimeZoneRef timeZone) {
383      assert(NULL != dateComp);
384      CFTimeZoneRef currTZ = dateComp->_timeZone;
385      if (timeZone && currTZ) {
386          if (CFEqual(currTZ, timeZone)) return;
387      }
388      if (currTZ) {
389          CFRelease(dateComp->_timeZone);
390          dateComp->_timeZone = NULL;
391      }
392      if (timeZone) {
393          dateComp->_timeZone = (CFTimeZoneRef)CFRetain(timeZone);
394          // Also set the tz on our calendar
395          CFCalendarRef cal = dateComp->_calendar;
396          if (cal) {
397              CFCalendarSetTimeZone(cal, timeZone);
398          }
399      }
400  }
401  
402  Boolean CFDateComponentsIsValidDate(CFDateComponentsRef dateComp) {
403      CFCalendarRef cal = dateComp->_calendar;
404      if (!cal) {
405          return false;
406      } else {
407          return CFDateComponentsIsValidDateInCalendar(dateComp, cal);
408      }
409  }
410  
411  Boolean CFDateComponentsIsValidDateInCalendar(CFDateComponentsRef dateComp, CFCalendarRef inCalendar) {
412      assert(NULL != dateComp);
413      assert(NULL != inCalendar);
414  
415      CFIndex ns = dateComp->_nanosecond;
416      if (CFDateComponentUndefined != ns && 1000 * 1000 * 1000UL <= ns) {
417          return false;
418      }
419      
420      CFCalendarRef calendar = _CFCalendarCreateCopy(kCFAllocatorSystemDefault, inCalendar);
421      
422      // Clear nanoseconds temporarily
423      if (CFDateComponentUndefined != ns && 0 < ns) {
424          dateComp->_nanosecond = 0;
425      }
426      
427      CFDateRef date = CFCalendarCreateDateFromComponents(kCFAllocatorSystemDefault, calendar, dateComp);
428      
429      // Reset nanoseconds
430      if (CFDateComponentUndefined != ns && 0 < ns) {
431          dateComp->_nanosecond = ns;
432      }
433      
434      Boolean result = true;
435      if (date) {
436          CFCalendarUnit all = kCFCalendarUnitEra | kCFCalendarUnitYear | kCFCalendarUnitMonth | kCFCalendarUnitDay | kCFCalendarUnitHour | kCFCalendarUnitMinute | kCFCalendarUnitSecond | kCFCalendarUnitWeekday | kCFCalendarUnitWeekdayOrdinal | kCFCalendarUnitQuarter | kCFCalendarUnitWeekOfMonth | kCFCalendarUnitWeekOfYear | kCFCalendarUnitYearForWeekOfYear;
437          
438          CFDateComponentsRef newComps = CFCalendarCreateDateComponentsFromDate(kCFAllocatorSystemDefault, calendar, all, date);
439          
440          if (result && CFDateComponentUndefined != dateComp->_era) if (newComps->_era != dateComp->_era) result = false;
441          if (result && CFDateComponentUndefined != dateComp->_year) if (newComps->_year != dateComp->_year) result = false;
442          if (result && CFDateComponentUndefined != dateComp->_month) if (newComps->_month != dateComp->_month) result = false;
443          if (result && CFDateComponentUndefined != dateComp->_leapMonth) if (newComps->_leapMonth != dateComp->_leapMonth) result = false;
444          if (result && CFDateComponentUndefined != dateComp->_day) if (newComps->_day != dateComp->_day) result = false;
445          if (result && CFDateComponentUndefined != dateComp->_hour) if (newComps->_hour != dateComp->_hour) result = false;
446          if (result && CFDateComponentUndefined != dateComp->_minute) if (newComps->_minute != dateComp->_minute) result = false;
447          if (result && CFDateComponentUndefined != dateComp->_second) if (newComps->_second != dateComp->_second) result = false;
448          if (result && CFDateComponentUndefined != dateComp->_weekday) if (newComps->_weekday != dateComp->_weekday) result = false;
449          if (result && CFDateComponentUndefined != dateComp->_weekdayOrdinal) if (newComps->_weekdayOrdinal != dateComp->_weekdayOrdinal) result = false;
450          if (result && CFDateComponentUndefined != dateComp->_quarter) if (newComps->_quarter != dateComp->_quarter) result = false;
451          if (result && CFDateComponentUndefined != dateComp->_weekOfMonth) if (newComps->_weekOfMonth != dateComp->_weekOfMonth) result = false;
452          if (result && CFDateComponentUndefined != dateComp->_weekOfYear) if (newComps->_weekOfYear != dateComp->_weekOfYear) result = false;
453          if (result && CFDateComponentUndefined != dateComp->_yearForWeekOfYear) if (newComps->_yearForWeekOfYear != dateComp->_yearForWeekOfYear) result = false;
454          CFRelease(date);
455          CFRelease(newComps);
456      }
457      
458      CFRelease(calendar);
459      
460      // NSDateComponents has a legacy behavior here to support an unset calendar that means if date is not set we return true
461      return result;
462  }
463  
464  Boolean CFDateComponentsDateMatchesComponents(CFDateComponentsRef dateComp, CFCalendarRef calendar, CFDateRef date) {
465      // TODO
466      return false;
467  }