# Getting hours during the day

I have this method that returns an array of all the hours for the past 24 hours, ex: if the current time is 3:24pm it looks like @[3pm,4pm...12am,1am...2pm,3pm];

Everything is working as it should, I was wondering if there was a more efficient (or pretty) way to do it than how I have it:

NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *today = [NSDate date];
NSDateComponents *comps = [calendar components:NSCalendarUnitHour|NSCalendarUnitDay fromDate:today];

NSMutableArray *units = [[NSMutableArray alloc] init];

int currentHour = (int)[comps hour];

for (int i = 0; i < 25; i++) {

int hourToInsert = currentHour + i;

NSString *insertString = @"";

if (hourToInsert == 12 || hourToInsert == 36) {

//special case for noon
insertString = @"12PM";

} else if (hourToInsert == 24) {

//special case for midnight
insertString = @"12AM";

} else if (hourToInsert > 36) {

//next day PM
hourToInsert = hourToInsert - 36;

insertString = [NSString stringWithFormat:@"%iPM", hourToInsert];

} else if (hourToInsert > 24) {

//next day
hourToInsert = hourToInsert - 24;

insertString = [NSString stringWithFormat:@"%iAM", hourToInsert];

} else if (hourToInsert > 12) {

hourToInsert = hourToInsert - 12;

insertString = [NSString stringWithFormat:@"%iPM", hourToInsert];

} else {

insertString = [NSString stringWithFormat:@"%iAM", hourToInsert];

}

}


You can simplify the logic with fewer conditional branches:

• hour to insert = current hour + i, modulo 24
• if hour to insert is 12 : special case 12AM
• else if hour to insert is 0 : special case 12PM
• else if hour to insert < 12 : use AM
• otherwise use PM
• lastly, you can do the [units addObject:insertString] step at the end, since it's the same for all hours
• no need to initialize insertString, as you will set it in one of the conditional branches anyway
• insertString is a poor name. hourString would be better, hourWithAmPm might be even better

Something like this:

for (int i = 0; i < 25; i++) {

int hourToInsert = (currentHour + i) % 24;

NSString *insertString;

if (hourToInsert == 12) {

//special case for noon
insertString = @"12PM";

} else if (hourToInsert == 0) {

//special case for midnight
insertString = @"12AM";

} else if (hourToInsert < 12) {

insertString = [NSString stringWithFormat:@"%iAM", hourToInsert];

} else {

insertString = [NSString stringWithFormat:@"%iPM", hourToInsert - 12];

}

}

• What exactly does the int hourToInsert = currentHour + i % 24; do? – Brandon Shega Jun 3 '15 at 20:22
• I corrected to (currentHour + i) % 24. It returns the remainder after dividing by 24. For example, 39 % 24 is 15. – janos Jun 3 '15 at 20:24
• Oh I see so it removes the need to check for anything higher than 24? – Brandon Shega Jun 3 '15 at 20:25
• Excellent, works as intended and is much more readable. Thank you! – Brandon Shega Jun 3 '15 at 20:27

Instead of handling all the different cases (am, pm, special rules for midnight and noon) you can use a date formatter which does all these things for you:

NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"ha"; // h = 0..12, a = am/pm

NSString *string = [fmt stringFromDate:someDate];


Then note that not all people use the 12hour AM/PM system. You might consider to display the hours according to the locale/region that a user has configured on his computer. That is achieved simply by using the date format

fmt.dateFormat = [NSDateFormatter dateFormatFromTemplate:@"h" options:0 locale:[NSLocale currentLocale]];


Another problem occurs when the clock is adjusted for the daylight saving time. For example, when DST starts in Germany, the hour is put forward at 2am by one hour, so that the time "2am" does not exist at all. Similarly, when DST ends and the hour is put back at 3am by one hour, then time "2am" occurs twice.

Therefore, instead of simply incrementing an "hour" variable, I would recommend to use calendrical calculations which handle all these situations correctly:

// A date formatter that prints hours according to the user's locale/region settings:
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = [NSDateFormatter dateFormatFromTemplate:@"h" options:0 locale:[NSLocale currentLocale]];

NSCalendar *calendar = [NSCalendar currentCalendar];

// The last hour to print is the full hour of the current point in time:
NSDate *lastHour;
[calendar rangeOfUnit:NSCalendarUnitHour startDate:&lastHour interval:nil forDate:[NSDate date]];

// The first hour to print is one day earlier:
NSDate *firstHour = [calendar dateByAddingUnit:NSCalendarUnitDay value:-1 toDate:lastHour options:0];

NSMutableArray *units = [[NSMutableArray alloc] init];

// hour = firstHour ... lastHour:
NSDate *hour = firstHour;
do {