2

Say I have the following code which initializes an NSDateComponents:

NSCalendar *calendar = [NSCalendar currentCalendar];

NSDateComponents *components = [[NSDateComponents alloc] init];
[components setYear:1987];
[components setMonth:3];
[components setDay:17];
[components setHour:14];
[components setMinute:20];
[components setSecond:0];

And now I want to convert the components to an NSDate. It seems like there are two ways:

Method 1:

NSDate *date = [calendar dateFromComponents:components];

Method 2:

NSDate *date = [components date];

Is there a difference between these two methods? If so, which one should be used and why?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Senseful
  • 86,719
  • 67
  • 308
  • 465

2 Answers2

4
NSDate *date = [components date];

is identical to

NSDate *date = [[components calendar] dateFromComponents:comps];

and it returns nil if you don't have assigned a calendar to the date components. You can verify that by setting the environment variable NSObjCMessageLoggingEnabled=YES in the projects scheme, and observe in the generated message trace (/tmp/msgSends-NNNN) that exactly the same methods are called.

That means that you can call

NSDate *date = [calendar dateFromComponents:components];

or

[components setCalendar:calendar];
NSDate *date = [components date];

and there is no difference between the two methods.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • ok so the difference between the two alternatives the OP has is that once he uses a calendar and once he doesn't use any calendar (which would fail. see nickH's answer) – Daij-Djan Mar 01 '14 at 10:07
  • I get that is identical if you assign the calendar to the comps but that isn't what the OP has – Daij-Djan Mar 01 '14 at 10:08
  • 2
    @Daij-Djan: NickH said that *"questions related to a specific calendar (like the Gregorian) are undefined"* if you don't assign a calendar. My point was that the result is `[components date] = nil` generally if you don't assign a calendar. The difference might be small but to me it seemed worth a separate answer. Also the example that NickH gave seems unrelated to this problem to me. – Martin R Mar 01 '14 at 10:18
  • @MartinR I usually end up setting a calendar when I have to use components like that, so I did not know it would be fully nil. And just for record, the example, and the quoted text above were both from Apple's documentation that I linked, so it wasn't directly related, but I thought it would give the full context Apple originally wrote. Your answer is definitely more complete and better researched than mine. – NickH Mar 04 '14 at 01:14
  • I have been profiling my own code surrounding this issue just this morning. Calling [components setCalendar] was taking up 94% of the method time. I removed that call and used [calendar dateFromComponents] and it reduced the time profile significantly. It changed to 34% with [calendar components: fromDate] and 64% [calendar dateFromComponents]. A definite difference. – androider Mar 05 '14 at 11:28
  • @androider: That is interesting. I had used "message tracing" to trace a single invocation of both variants, and observed no difference. I will check that later. Thanks for the feedback! – Martin R Mar 05 '14 at 13:04
  • @MartinR I should clarify the 34% and 64% is the time spent performing those two functions in the same method call. The overall execution time for the method did drop though. – androider Mar 05 '14 at 13:23
1

I believe it is that the NSDateComponents does not have a calendar set by default. Thus questions related to a specific calendar (like the Gregorian) are undefined.

According to Apple's documentation here:

An instance of NSDateComponents is not responsible for answering questions about a date beyond the information with which it was initialized. For example, if you initialize one with May 6, 2004, its weekday is NSUndefinedDateComponent, not Thursday. To get the correct day of the week, you must create a suitable instance of NSCalendar, create an NSDate object using dateFromComponents: and then use components:fromDate: to retrieve the weekday—as illustrated in the following example.

NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:6];
[comps setMonth:5];
[comps setYear:2004];
NSCalendar *gregorian = [[NSCalendar alloc]
    initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [gregorian dateFromComponents:comps];
[comps release];
NSDateComponents *weekdayComponents =
    [gregorian components:NSWeekdayCalendarUnit fromDate:date];
int weekday = [weekdayComponents weekday];

But I believe you can set the NSDateComponent's Calendar property to fix that.

NickH
  • 683
  • 1
  • 5
  • 24