/ NSCalendar.m
NSCalendar.m
  1  //
  2  //  NSCalendar.m
  3  //  CoreFoundation
  4  //
  5  //  Copyright (c) 2014 Apportable. All rights reserved.
  6  //
  7  
  8  #import <Foundation/NSTimeZone.h>
  9  #import <Foundation/NSLocale.h>
 10  #import "NSCalendarInternal.h"
 11  #import "NSObjectInternal.h"
 12  
 13  extern void CFCalendarSetGregorianStartDate(CFCalendarRef calendar, CFDateRef date);
 14  extern CFDateRef CFCalendarCopyGregorianStartDate(CFCalendarRef calendar);
 15  extern Boolean _CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, int **componentVector, int count);
 16  extern Boolean _CFCalendarGetComponentDifferenceV(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, int **vector, int count);
 17  
 18  const NSCalendarIdentifier NSCalendarIdentifierGregorian = @"NSCalendarIdentifierGregorian";
 19  const NSCalendarIdentifier NSCalendarIdentifierISO8601 = @"NSCalendarIdentifierISO8601";
 20  const NSCalendarIdentifier NSCalendarIdentifierBuddhist = @"NSCalendarIdentifierBuddhist";
 21  const NSCalendarIdentifier NSCalendarIdentifierChinese = @"NSCalendarIdentifierChinese";
 22  const NSCalendarIdentifier NSCalendarIdentifierCoptic = @"NSCalendarIdentifierCoptic";
 23  const NSCalendarIdentifier NSCalendarIdentifierEthiopicAmeteAlem = @"NSCalendarIdentifierEthiopicAmeteAlem";
 24  const NSCalendarIdentifier NSCalendarIdentifierEthiopicAmeteMihret = @"NSCalendarIdentifierEthiopicAmeteMihret";
 25  const NSCalendarIdentifier NSCalendarIdentifierHebrew = @"NSCalendarIdentifierHebrew";
 26  const NSCalendarIdentifier NSCalendarIdentifierIndian = @"NSCalendarIdentifierIndian";
 27  const NSCalendarIdentifier NSCalendarIdentifierIslamic = @"NSCalendarIdentifierIslamic";
 28  const NSCalendarIdentifier NSCalendarIdentifierIslamicCivil = @"NSCalendarIdentifierIslamicCivil";
 29  const NSCalendarIdentifier NSCalendarIdentifierIslamicTabular = @"NSCalendarIdentifierIslamicTabular";
 30  const NSCalendarIdentifier NSCalendarIdentifierIslamicUmmAlQura = @"NSCalendarIdentifierIslamicUmmAlQura";
 31  const NSCalendarIdentifier NSCalendarIdentifierJapanese = @"NSCalendarIdentifierJapanese";
 32  const NSCalendarIdentifier NSCalendarIdentifierPersian = @"NSCalendarIdentifierPersian";
 33  const NSCalendarIdentifier NSCalendarIdentifierRepublicOfChina = @"NSCalendarIdentifierRepublicOfChina";
 34  
 35  
 36  @implementation NSCalendar
 37  
 38  + (id)allocWithZone:(NSZone *)zone
 39  {
 40      if (self == [NSCalendar class])
 41      {
 42          static dispatch_once_t once = 0L;
 43          static __NSCFCalendar *placeholder = nil;
 44          dispatch_once(&once, ^{
 45              placeholder = [__NSCFCalendar allocWithZone:nil];
 46          });
 47          return placeholder;
 48      }
 49      else
 50      {
 51          return [super allocWithZone:zone];
 52      }
 53  }
 54  
 55  + (id)currentCalendar
 56  {
 57      return [(NSCalendar *)CFCalendarCopyCurrent() autorelease];
 58  }
 59  
 60  - (CFTypeID)_cfTypeID
 61  {
 62      return CFCalendarGetTypeID();
 63  }
 64  
 65  
 66  - (BOOL)rangeOfUnit:(NSCalendarUnit)unit startDate:(NSDate **)datep interval:(NSTimeInterval *)tip forDate:(NSDate *)date
 67  {
 68      return NO;
 69  }
 70  
 71  @end
 72  
 73  
 74  @implementation __NSCFCalendar
 75  
 76  + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
 77  {
 78      return NO;
 79  }
 80  
 81  #define MAX_COMPS 17
 82  
 83  static int makeCFCalendarComponentVector(NSDateComponents *comps, int *componentVector, char *format)
 84  {
 85      NSInteger era = [comps era];
 86      NSInteger year = [comps year];
 87      NSInteger month = [comps month];
 88      NSInteger day = [comps day];
 89      NSInteger hour = [comps hour];
 90      NSInteger minute = [comps minute];
 91      NSInteger second = [comps second];
 92      NSInteger nanosecond = [comps nanosecond];
 93      NSInteger weekOfYear = [comps weekOfYear];
 94      NSInteger weekOfMonth = [comps weekOfMonth];
 95      NSInteger yearForWeekOfYear = [comps yearForWeekOfYear];
 96      NSInteger weekday = [comps weekday];
 97      NSInteger weekdayOrdinal = [comps weekdayOrdinal];
 98  
 99      int count = 0;
100      if (era != INT_MAX)
101      {
102          componentVector[count] = era;
103          format[count++] = 'G';
104      }
105      if (year != INT_MAX)
106      {
107          componentVector[count] = year;
108          format[count++] = 'y';
109      }
110      if (month != INT_MAX)
111      {
112          componentVector[count] = month;
113          format[count++] = 'M';
114      }
115      if (day != INT_MAX)
116      {
117          componentVector[count] = day;
118          format[count++] = 'd';
119      }
120      if (hour != INT_MAX)
121      {
122          componentVector[count] = hour;
123          format[count++] = 'H';
124      }
125      if (minute != INT_MAX)
126      {
127          componentVector[count] = minute;
128          format[count++] = 'm';
129      }
130      if (second != INT_MAX)
131      {
132          componentVector[count] = second;
133          format[count++] = 's';
134      }
135      if (nanosecond != INT_MAX)
136      {
137          componentVector[count] = nanosecond / NSEC_PER_MSEC;
138          format[count++] = 'S';
139      }
140      if (weekOfYear != INT_MAX)
141      {
142          componentVector[count] = weekOfYear;
143          format[count++] = 'w';
144      }
145      if (weekOfMonth != INT_MAX)
146      {
147          componentVector[count] = weekOfMonth;
148          format[count++] = 'W';
149      }
150      if (yearForWeekOfYear != INT_MAX)
151      {
152          componentVector[count] = yearForWeekOfYear;
153          format[count++] = 'Y';
154      }
155      if (weekday != INT_MAX)
156      {
157          componentVector[count] = weekday;
158          format[count++] = 'E';
159      }
160      if (weekdayOrdinal != INT_MAX)
161      {
162          componentVector[count] = weekdayOrdinal;
163          format[count++] = 'F';
164      }
165  
166      format[count] = '\0';
167  
168      return count;
169  }
170  
171  static int makeCFCalendarComponentsVector(NSUInteger unitFlags, int *componentVector, int **v, char *format)
172  {
173      /*
174          TODO: figure out how these are populated
175          NSCalendarUnitQuarter
176  
177          G UCAL_ERA NSCalendarUnitEra
178          y UCAL_YEAR NSCalendarUnitYear
179          M UCAL_MONTH NSCalendarUnitMonth
180          d UCAL_DAY_OF_MONTH NSCalendarUnitDay
181          h UCAL_HOUR 
182          H UCAL_HOUR_OF_DAY NSCalendarUnitHour
183          m UCAL_MINUTE NSCalendarUnitMinute
184          s UCAL_SECOND NSCalendarUnitSecond
185          S UCAL_MILLISECOND (NSCalendarUnitNanosecond with conversion)
186          w UCAL_WEEK_OF_YEAR NSCalendarUnitWeekOfYear
187          W UCAL_WEEK_OF_MONTH NSCalendarUnitWeekOfMonth
188          Y UCAL_YEAR_WOY NSCalendarUnitYearForWeekOfYear
189          E UCAL_DAY_OF_WEEK NSCalendarUnitWeekday
190          D UCAL_DAY_OF_YEAR
191          F UCAL_DAY_OF_WEEK_IN_MONTH NSCalendarUnitWeekdayOrdinal
192          a UCAL_AM_PM
193          g UCAL_JULIAN_DAY
194      */
195  
196      int count = 0;
197  
198      if ((unitFlags & NSCalendarUnitEra) != 0)
199      {
200          format[count++] = 'G';
201      }
202      if ((unitFlags & NSCalendarUnitYear) != 0)
203      {
204          format[count++] = 'y';
205      }
206      if ((unitFlags & NSCalendarUnitMonth) != 0 ||
207          (unitFlags & NSCalendarUnitQuarter) != 0)
208      {
209          format[count++] = 'M';
210      }
211      if ((unitFlags & NSCalendarUnitDay) != 0)
212      {
213          format[count++] = 'd';
214      }
215      if ((unitFlags & NSCalendarUnitHour) != 0)
216      {
217          format[count++] = 'H';
218      }
219      if ((unitFlags & NSCalendarUnitMinute) != 0)
220      {
221          format[count++] = 'm';
222      }
223      if ((unitFlags & NSCalendarUnitSecond) != 0)
224      {
225          format[count++] = 's';
226      }
227      if ((unitFlags & NSCalendarUnitNanosecond) != 0)
228      {
229          format[count++] = 'S';
230      }
231      if ((unitFlags & NSCalendarUnitWeekOfYear) != 0)
232      {
233          format[count++] = 'w';
234      }
235      if ((unitFlags & NSCalendarUnitWeekOfMonth) != 0)
236      {
237          format[count++] = 'W';
238      }
239      if ((unitFlags & NSCalendarUnitYearForWeekOfYear) != 0)
240      {
241          format[count++] = 'Y';
242      }
243      if ((unitFlags & NSCalendarUnitWeekday) != 0)
244      {
245          format[count++] = 'E';
246      }
247      if ((unitFlags & NSCalendarUnitWeekdayOrdinal) != 0)
248      {
249          format[count++] = 'F';
250      }
251  
252      format[count] = '\0'; // ensure terminator since _CFCalendarDecomposeAbsoluteTimeV iterates on the formatter
253  
254      for (NSUInteger idx = 0; idx < MAX_COMPS; idx++)
255      {
256          v[idx] = &componentVector[idx];
257      }
258  
259      return count;
260  }
261  
262  static NSDateComponents *componentsFromVector(NSCalendar *self, NSUInteger unitFlags, int *componentVector)
263  {
264      NSDateComponents *comps = [[NSDateComponents alloc] init];
265      NSUInteger count = 0;
266      if ((unitFlags & NSCalendarUnitEra) != 0)
267      {
268          [comps setEra:componentVector[count++]];
269      }
270      if ((unitFlags & NSCalendarUnitYear) != 0)
271      {
272          [comps setYear:componentVector[count++]];
273      }
274      int month = -1;
275      if ((unitFlags & NSCalendarUnitMonth) != 0 ||
276          (unitFlags & NSCalendarUnitQuarter) != 0)
277      {
278          month = componentVector[count++];
279          if ((unitFlags & NSCalendarUnitMonth) != 0)
280          {
281              [comps setMonth:month];
282          }
283      }
284      if ((unitFlags & NSCalendarUnitDay) != 0)
285      {
286          [comps setDay:componentVector[count++]];
287      }
288      if ((unitFlags & NSCalendarUnitQuarter) != 0)
289      {
290  /*
291  US Locale Fiscal Quarters:
292  1st quarter: 1 October 2013 – 31 December 2013
293  2nd quarter: 1 January 2014 – 31 March 2014
294  3rd quarter: 1 April 2014 – 30 June 2014
295  4th quarter: 1 July 2014 – 30 September 2014
296  */
297          enum {
298              january = 1,
299              february,
300              march,
301              april,
302              may,
303              june,
304              july,
305              august,
306              september,
307              october,
308              november,
309              december,
310          };
311          int quarter = -1;
312          if (october <= month && month <= december)
313          {
314              quarter = 0;
315          }
316          else if (january <= month && month <= march)
317          {
318              quarter = 1;
319          }
320          else if (april <= month && month <= june)
321          {
322              quarter = 2;
323          }
324          else /* if (july <= month && month <= september) */
325          {
326              quarter = 3;
327          }
328  
329          if (quarter != -1)
330          {
331              [comps setQuarter:quarter];
332          }
333          else
334          {
335              DEBUG_BREAK();
336          }
337      }
338      if ((unitFlags & NSCalendarUnitHour) != 0)
339      {
340          [comps setHour:componentVector[count++]];
341      }
342      if ((unitFlags & NSCalendarUnitMinute) != 0)
343      {
344          [comps setMinute:componentVector[count++]];
345      }
346      if ((unitFlags & NSCalendarUnitSecond) != 0)
347      {
348          [comps setSecond:componentVector[count++]];
349      }
350      if ((unitFlags & NSCalendarUnitNanosecond) != 0)
351      {
352          [comps setNanosecond:NSEC_PER_MSEC * componentVector[count++]];
353      }
354      if ((unitFlags & NSCalendarUnitWeekOfYear) != 0)
355      {
356          [comps setWeekOfYear:componentVector[count++]];
357      }
358      if ((unitFlags & NSCalendarUnitWeekOfMonth) != 0)
359      {
360          [comps setWeekOfMonth:componentVector[count++]];
361      }
362      if ((unitFlags & NSCalendarUnitYearForWeekOfYear) != 0)
363      {
364          [comps setYearForWeekOfYear:componentVector[count++]];
365      }
366      if ((unitFlags & NSCalendarUnitWeekday) != 0)
367      {
368          [comps setWeekday:componentVector[count++]];
369      }
370      if ((unitFlags & NSCalendarUnitWeekdayOrdinal) != 0)
371      {
372          [comps setWeekdayOrdinal:componentVector[count++]];
373      }
374      if ((unitFlags & NSCalendarUnitCalendar) != 0)
375      {
376          [comps setCalendar:self];
377      }
378      if ((unitFlags & NSCalendarUnitTimeZone) != 0)
379      {
380          [comps setTimeZone:[self timeZone]];
381      }
382      return [comps autorelease];
383  }
384  
385  - (NSDateComponents *)components:(NSUInteger)unitFlags fromDate:(NSDate *)startingDate toDate:(NSDate *)resultDate options:(NSUInteger)opts
386  {
387      if (startingDate == nil)
388      {
389          return nil;
390      }
391  
392      if (resultDate == nil)
393      {
394          return nil;
395      }
396  
397  
398      CFAbsoluteTime startingAT = [startingDate timeIntervalSinceReferenceDate];
399      CFAbsoluteTime resultAT = [resultDate timeIntervalSinceReferenceDate];
400  
401      char format[MAX_COMPS];
402      int componentVector[MAX_COMPS];
403      int *v[MAX_COMPS];
404      int count = makeCFCalendarComponentsVector(unitFlags, componentVector, v, format);
405  
406      if (!_CFCalendarGetComponentDifferenceV((CFCalendarRef)self, startingAT, resultAT, (CFOptionFlags)opts, format, v, count))
407      {
408          return nil;
409      }
410  
411      return componentsFromVector(self, unitFlags, componentVector);
412  }
413  
414  - (NSDate *)dateByAddingComponents:(NSDateComponents *)comps toDate:(NSDate *)date options:(NSUInteger)opts
415  {
416      if (comps == nil)
417      {
418          return nil;
419      }
420      
421      if (date == nil)
422      {
423          return nil;
424      }
425  
426      CFAbsoluteTime absTime = [date timeIntervalSinceReferenceDate];
427      char format[MAX_COMPS];
428      int componentVector[MAX_COMPS];
429  
430      int count = makeCFCalendarComponentVector(comps, componentVector, format);
431  
432      // TODO handle "yMldHms" for day of week - also era, nanoseconds, etc.
433      if (!_CFCalendarAddComponentsV((CFCalendarRef)self, &absTime, opts, format, componentVector, strlen(format)))
434      {
435          return nil;
436      }
437      return [NSDate dateWithTimeIntervalSinceReferenceDate:absTime];
438  }
439  
440  - (NSDateComponents *)components:(NSUInteger)unitFlags fromDate:(NSDate *)date
441  {
442      if (date == nil)
443      {
444          return nil;
445      }
446      
447      CFAbsoluteTime at = [date timeIntervalSinceReferenceDate];
448  
449      char format[MAX_COMPS];
450      int componentVector[MAX_COMPS];
451      int *v[MAX_COMPS];
452      int count = makeCFCalendarComponentsVector(unitFlags, componentVector, v, format);
453  
454      if (!_CFCalendarDecomposeAbsoluteTimeV((CFCalendarRef)self, at, format, v, count))
455      {
456          return nil;
457      }
458  
459      return componentsFromVector(self, unitFlags, componentVector);
460  }
461  
462  
463  
464  - (NSDate *)dateFromComponents:(NSDateComponents *)comps
465  {
466      if (comps == nil)
467      {
468          return nil;
469      }
470  
471      char format[MAX_COMPS];
472      int componentVector[MAX_COMPS];
473  
474      int count = makeCFCalendarComponentVector(comps, componentVector, format);
475  
476      CFAbsoluteTime absTime;
477  
478      // TODO handle "yMldHms" for day of week
479      if (!_CFCalendarComposeAbsoluteTimeV((CFCalendarRef)self, &absTime, format, componentVector, count))
480      {
481          return nil;
482      }
483      return [NSDate dateWithTimeIntervalSinceReferenceDate:absTime];
484  }
485  
486  - (BOOL)rangeOfUnit:(NSCalendarUnit)unit startDate:(NSDate **)datep interval:(NSTimeInterval *)tip forDate:(NSDate *)date
487  {
488      CFTimeInterval at = [date timeIntervalSinceReferenceDate];
489      CFTimeInterval start = at;
490      BOOL success = CFCalendarGetTimeRangeOfUnit((CFCalendarRef)self, (CFCalendarUnit)unit, at, &start, (CFTimeInterval *)tip);
491      if (datep)
492      {
493          *datep = [NSDate dateWithTimeIntervalSinceReferenceDate:start];
494      }
495      return success;
496  }
497  
498  - (NSUInteger)ordinalityOfUnit:(NSCalendarUnit)smaller inUnit:(NSCalendarUnit)larger forDate:(NSDate *)date
499  {
500      return CFCalendarGetOrdinalityOfUnit((CFCalendarRef)self, (CFCalendarUnit)smaller, (CFCalendarUnit)larger, [date timeIntervalSinceReferenceDate]);
501  }
502  
503  - (NSRange)rangeOfUnit:(NSCalendarUnit)smaller inUnit:(NSCalendarUnit)larger forDate:(NSDate *)date
504  {
505      CFRange r = CFCalendarGetRangeOfUnit((CFCalendarRef)self, (CFCalendarUnit)smaller, (CFCalendarUnit)larger, [date timeIntervalSinceReferenceDate]);
506      return NSMakeRange(r.location, r.length);
507  }
508  
509  - (NSRange)minimumRangeOfUnit:(NSCalendarUnit)unit
510  {
511      CFRange r = CFCalendarGetMinimumRangeOfUnit((CFCalendarRef)self, (CFCalendarUnit)unit);
512      return NSMakeRange(r.location, r.length);
513  }
514  
515  - (NSRange)maximumRangeOfUnit:(NSCalendarUnit)unit
516  {
517      CFRange r = CFCalendarGetMaximumRangeOfUnit((CFCalendarRef)self, (CFCalendarUnit)unit);
518      return NSMakeRange(r.location, r.length);
519  }
520  
521  - (NSDate *)gregorianStartDate
522  {
523      return [(NSDate *)CFCalendarCopyGregorianStartDate((CFCalendarRef)self) autorelease];
524  }
525  
526  - (void)setGregorianStartDate:(NSDate *)date
527  {
528      CFCalendarSetGregorianStartDate((CFCalendarRef)self, (CFDateRef)self);
529  }
530  
531  - (void)setMinimumDaysInFirstWeek:(NSUInteger)mdw
532  {
533      CFCalendarSetMinimumDaysInFirstWeek((CFCalendarRef)self, mdw);
534  }
535  
536  - (NSUInteger)minimumDaysInFirstWeek
537  {
538      return CFCalendarGetMinimumDaysInFirstWeek((CFCalendarRef)self);
539  }
540  
541  - (void)setFirstWeekday:(NSUInteger)weekday
542  {
543      CFCalendarSetFirstWeekday((CFCalendarRef)self, weekday);
544  }
545  
546  - (NSUInteger)firstWeekday
547  {
548      return CFCalendarGetFirstWeekday((CFCalendarRef)self);
549  }
550  
551  - (void)setTimeZone:(NSTimeZone *)tz
552  {
553      CFCalendarSetTimeZone((CFCalendarRef)self, (CFTimeZoneRef)tz);
554  }
555  
556  - (NSTimeZone *)timeZone
557  {
558      return [(NSTimeZone *)CFCalendarCopyTimeZone((CFCalendarRef)self) autorelease];
559  }
560  
561  - (void)setLocale:(NSLocale *)locale
562  {
563      CFCalendarSetLocale((CFCalendarRef)self, (CFLocaleRef)locale);
564  }
565  
566  - (NSLocale *)locale
567  {
568      return [(NSLocale *)CFCalendarCopyLocale((CFCalendarRef)self) autorelease];
569  }
570  
571  - (id)initWithCalendarIdentifier:(NSString *)ident
572  {
573      return (id)CFCalendarCreateWithIdentifier(kCFAllocatorDefault, (CFStringRef)ident);
574  }
575  
576  - (NSString *)calendarIdentifier
577  {
578      return (NSString *)CFCalendarGetIdentifier((CFCalendarRef)self);
579  }
580  
581  - (id)copyWithZone:(NSZone *)zone
582  {
583      // this seems incomplete
584      return (id)CFCalendarCreateWithIdentifier(kCFAllocatorDefault, CFCalendarGetIdentifier((CFCalendarRef)self));
585  }
586  
587  - (NSUInteger)retainCount
588  {
589      return CFGetRetainCount((CFTypeRef)self);
590  }
591  
592  - (BOOL)_isDeallocating
593  {
594      return _CFIsDeallocating((CFTypeRef)self);
595  }
596  
597  - (BOOL)_tryRetain
598  {
599      return _CFTryRetain((CFTypeRef)self) != NULL;
600  }
601  
602  - (oneway void)release
603  {
604      CFRelease((CFTypeRef)self);
605  }
606  
607  - (id)retain
608  {
609      return (id)CFRetain((CFTypeRef)self);
610  }
611  - (NSUInteger)hash
612  {
613      return CFHash((CFTypeRef)self);
614  }
615  
616  - (BOOL)isEqual:(id)other
617  {
618      return CFEqual((CFTypeRef)self, (CFTypeRef)other);
619  }
620  
621  @end
622