views:

1438

answers:

6

Is there a better way to do this?

-(NSDate *)getMidnightTommorow {
    NSCalendarDate *now = [NSCalendarDate date];
    NSCalendarDate *tomorrow = [now dateByAddingYears:0 months:0 days:1 hours:0 minutes:0 seconds:0];
    return [NSCalendarDate dateWithYear:[tomorrow yearOfCommonEra]
                                  month:[tomorrow monthOfYear]
                                    day:[tomorrow dayOfMonth]
                                   hour:0
                                 minute:0
                                 second:0
                               timeZone:[tomorrow timeZone]];
}

Note that I always want the next midnight, even if it happens to be midnight when I make that call, however if it happens to be 23:59:59, I of course want the midnight that is coming in one second.

The natural language functions seem flaky, and I'm not sure what Cocoa would do if I pass 32 in the "day" field. (If that'd work I could drop the [now dateByAddingYears:...] call)

+11  A: 

From the documentation:

Use of NSCalendarDate strongly discouraged. It is not deprecated yet, however it may be in the next major OS release after Mac OS X v10.5. For calendrical calculations, you should use suitable combinations of NSCalendar, NSDate, and NSDateComponents, as described in Calendars in Dates and Times Programming Topics for Cocoa.

Following that advice:

NSDate *today = [NSDate date];

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSDateComponents *components = [[NSDateComponents alloc] init];
components.day = 1;
NSDate *tomorrow = [gregorian dateByAddingComponents:components toDate:today options:0];
[components release];

NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
components = [gregorian components:unitFlags fromDate:tomorrow];
components.hour = 0;
components.minute = 0;

NSDate *tomorrowMidnight = [gregorian dateFromComponents:components];

[gregorian release];

(I'm not sure offhand if this is the most efficient implementation, but it should serve as a pointer in the right direction.)

Note: In theory you can reduce the amount of code here by allowing a date components object with values greater than the range of normal values for the component (e.g. simply adding 1 to the day component, which might result in its having a value of 32). However, although dateFromComponents: may tolerate out-of-bounds values, it's not guaranteed to. You're strongly encouraged not to rely on it.

mmalc
And following from that pointer, it looks like I can cut out most of that as dateFromComponents *does* seem to tolerate days past the end of the month.
Dre
dateFromComponents *may* tolerate out-of-bounds values, but it's not guaranteed to. You're strongly encouraged not to rely on that.
mmalc
A: 

Convert your current date and time to a Unix date (seconds since 1970) or DOS style (since 1980), then add 24 hours and convert it back. Then reset the hours, minutes and seconds to zero to get to midnight.

selwyn
Doesn't work right for some daylight savings edge cases.
Andrew
+5  A: 

Nope - it'll be the same way you use to find midnight today.

loudej
awww c'mon. that was funny!
loudej
I'm afraid people here have very defective irony/humor detectors. If you don't mark your funny comments as such, you will get downvoted. BTDT.
JesperE
I won't downvote your answer, but even assuming it was funny, what's the joke? A flippant short answer that ignores the details of the question? You could use your "joke" for every single technical question on SO. THAT is why it's not funny.
Yar
It's not saying the formula is the same as today plus one. It's intentionally misunderstanding the sentence structure of the question, as if I believed Dre was asking if a better formula not currently available might present itself if he waited a day. "Is there a better way to find midnight tomorrow?" See? To which you could quite rightly reply, "If you have to explain it, it ain't funny."
loudej
A: 

sounds like a good title for a pop song:

"Is there a better way to find midnight tomorrow?"

louism
A: 

[NSDate dateWithNaturalLanguageString:@"midnight tomorrow"];

Interesting approach (and I upvoted it for that), but note the caution in the documentation: “It may give unexpected results, and its use is strongly discouraged.”
Peter Hosey
Thanks, i was looking for a better solution too since the iPhone sdk doesn't like when I use it, but it still works for now :)
A: 

NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];

NSDate *tomorrow = [NSDate dateWithTimeIntervalSinceNow:(24 * 60 * 60)];

NSDateComponents *components = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:tomorrow];

NSDate *midnight = [gregorian dateFromComponents:components];