views:

60

answers:

4

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!

+1  A: 

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 );
zpasternack
+1  A: 

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.

Thomas Müller
+1  A: 

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.

Dave DeLong
+1  A: 

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.

beeeefy
With iOS 4 and local notifications, alarm clocks can be done.
Thomas Müller
You shouldn't use 24 * 60 * 60 to represent one day, use NSDateComponents and NSCalendar to add 1 day. If you use a hard coded time interval, you end up with a bunch of edge cases, e.g. if daylight savings ends, your "tomorrow" time will be off by an hour.
Nick Forge