BeachRunnerFred
BeachRunnerFred

Reputation: 18558

How can I create a complete NSDate object that represents the next specified day time?

I probably didn't word that title clearly enough, but if the user specifies a time of 2:30pm and it's currently 2:00pm, then I need an NSDate object that represents the current day with a time of 2:30pm. If the user specifies a time of 2:30pm and it's currently 3:00pm, then I need an NSDate object that represents tomorrow with a time of 2:30pm. Similar to how an alarm clock would work.

I already wrote this code, but it's embarrassingly long and it feels really kludgy and I feel like it should be more simple, but I'm new to iOS development and this specific API.

Thanks so much in advance for your help!

Upvotes: 3

Views: 1255

Answers (4)

beeeefy
beeeefy

Reputation: 527

If you get userDate from the user, and nowDate = [NSDate date] , then

NSComparisonResult diff = [userDate compare: nowDate];

If the diff is NSOrderedDescending, then userDate is later than nowDate, and you set the alarm for today. If it's NSOrderedAscending, then you set the alarm for tomorrow. You can test for NSOrderedSame, but it will never be.

You'd like to avoid having to determine what day it is, so it seems to me that adding an NSTimeInterval of the difference plus 24 * 60 * 60 (if the alarm is tomorrow) or just the difference (if the alarm is today) would suffice.

I don't know why everyone is trying to make alarm clocks. It can't be done, as best I can tell.

Upvotes: 0

Dave DeLong
Dave DeLong

Reputation: 243146

Assuming that the user is using a UIDatePicker to select the date, here's what I would do:

NSDate * selectedDate = ...; //2:30pm
NSDate * selectedComponents = [[NSCalendar currentCalendar] components:(NSHourCalendarUnit | NSMinuteCalendarUnit) fromDate:selectedDate];

NSDate * now = [NSDate date];
NSDate * nowComponents = [[NSCalendar currentCalendar] components:NSUIntegerMax fromDate:now];

[nowComponents setHour:[selectedComponents hour]];
[nowComponents setMinute:[selectedComponents minute]];

NSDate * targetDate = [[NSCalendar currentCalendar] dateFromComponents:nowComponents];
if ([[now laterDate:targetDate] isEqual:now]) {
  //in a comparison between now and the target date, the target date has already passed
  [nowComponents setDay:[nowComponents day]+1];
  targetDate = [[NSCalendar currentCalendar] dateFromComponents:nowComponents];
}

//target date is now at the appropriate 2:30pm

Side note: since the NSCalendarUnit is a bitfield typedef for NSUInteger, I pass in NSUIntegerMax to retrieve all possible calendar units. That way I don't have to have a massive bitwise OR statement.

Upvotes: 1

Thomas Müller
Thomas Müller

Reputation: 15625

If you add a time interval that messes up around the daylight savings dates, I believe. Adding seconds for one day will then end up a day after but an hour earlier or later. I'm pretty sure I ran into this before, but you can test that easily, I guess.

Another way would be to use NSDateComponents:

// using while will be inefficient when the targetDate is more than a few days in the past
while ([targetDate compare:[NSDate date]] == NSOrderedAscending) {
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSInteger units = NSYearCalendarUnit |
                      NSMonthCalendarUnit |
                      NSDayCalendarUnit |
                      NSHourCalendarUnit |
                      NSMinuteCalendarUnit |
                      NSSecondCalendarUnit;
    NSDateComponents *comps = [calendar components:units fromDate:targetDate];
    [comps setDay:[comps day] + 1]; // if day is 32 for instance, it'll automatically roll over to the next month
    NSDate *targetDate = [calendar dateFromComponents:comps];
}

The above is by no means an ideal solution (it'll leak targetDate if you retained it, for instance), but is mainly meant to show you how to add 1 day to a NSDate using NSDateComponents.

Upvotes: 1

zpasternack
zpasternack

Reputation: 17898

Do you already have an NSDate representing the date/time the user picked? If so, it's a pretty simple matter to compare it to now (via earlierDate: or laterDate:), and create a new one a day later (via dateWithTimeInterval:sinceDate: or initWithTimeInterval:sinceDate:).

Something like this:

// Assume dateTarget is an NSDate representing the date/time the user picked.
// Is it earlier than now?
if( [dateTarget earlierDate:[NSDate date]] == dateTarget ) {
    // dateTarget is earlier than now.
    // Add 1 day to it.
    NSDate* newDate = [[NSDate alloc]initWithTimeInterval:60*60*24 sinceDate:dateTarget];
    [dateTarget release];
    dateTarget = newDate;
}

// dateTarget is now either the original time, or if that time passed, the same time tomorrow.
NSLog( @"dateTarget = %@", dateTarget );

Upvotes: 1

Related Questions