/ 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