Rachid Finge Jr
Rachid Finge Jr

Reputation: 1171

How to retrieve number of hours past midnight from an NSDate object?

I need to retrieve the number of hours past midnight from a UIDatePicker control in an iPhone project. datePickerMode is set to UIDatePickerModeTime, so the user only can set a time, no date. When the user is done and dismisses the view the UIDatePicker is on, the following date might be retrieved (as an example):

NSDate *returnTime = timePicker.date;
NSLog(@"returnTime: %@", returnTime); // returns for example @"1970-01-01 10:13:00 PM +0000"

As said, I'm looking for the number of hours past midnight. In the example above, that value should be 22. I wanted to achieve this by creating an NSDateFormatter object and have it extract the hour of day in 24 hour clock format, thus using setDateFormat:@"H" (capital H rather than 'h'):

NSDateFormatter *formatHour = [[NSDateFormatter alloc] init];
[formatHour setDateFormat:@"H"];
NSInteger intHoursPastMidnight = [[formatHour stringFromDate:returnTime] integerValue];

This however does not always work as expected. When the user has disabled the 24 hour clock in the system wide Preferences (i.e. the user uses AM/PM), intHoursPastMidnight will contain 10 rather than 22. 10 is indeed the value that's visible in the UIDatePicker, but I had expected NSDateFormatter to convert this to 22 because of @"H".

What's wrong here? Is my convert assumption incorrect? Is this a bug with UIDatePicker? How can I solve the problem, so I can essentially access the number of hours past midnight set in the UIDatePicker, independent from the user's 12 or 24 hour clock preference? Is this the way to go anyway?

The end goal, to make it clear, is to retrieve the number of minutes past midnight (so hours*60+minutes, value always betweet 0 and 1440).

Upvotes: 4

Views: 4030

Answers (2)

memmons
memmons

Reputation: 40502

Dave's answer is correct, however it is not as performant as the example below. In my Instruments profile tests my solution was more than 3x as fast -- the total time taken by his method across all calls in my test app was 844ms, mine 268ms.

Keep in mind that my test app iterates over a collection of objects to calculate the minutes since midnight of each, which is then used as a basis for sorting. So, if your code isn't doing something similar, his more-readable, more-standard answer is probably a better choice.

int const MINUTES_IN_HOUR  = 60;
int const DAY_IN_MINUTES   = 1440;

#define DATE_COMPONENTS (NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit |  NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit | NSWeekdayOrdinalCalendarUnit)
#define CURRENT_CALENDAR [NSCalendar currentCalendar]

- (NSUInteger)minutesSinceMidnight 
{
    NSDateComponents *startComponents = [CURRENT_CALENDAR components:DATE_COMPONENTS fromDate:self.startTime];
    NSUInteger fromMidnight = [startComponents hour] * MINUTES_IN_HOUR + [startComponents minute];

    return fromMidnight;
}

Upvotes: 3

Dave DeLong
Dave DeLong

Reputation: 243156

You can do this with your NSCalendar.

First, get your date:

NSDate *date = [timePicker date];

Next, convert it into its date components:

NSDateComponents *components = [[NSCalendar currentCalendar] components:NSIntegerMax fromDate:date];

Now we'll reset the hours and minutes of the date components so that it's now pointing at midnight:

[components setHour:0];
[components setMinute:0];
[components setSecond:0];

Next, we'll turn it back in to a date:

NSDate *midnight = [[NSCalendar currentCalendar] dateFromComponents:components];

Finally, we'll ask for the hours between midnight and the date:

NSDateComponents *diff = [[NSCalendar currentCalendar] components:NSHourCalendarUnit fromDate:midnight toDate:date options:0];

NSInteger numberOfHoursPastMidnight = [diff hour];

Upvotes: 22

Related Questions