Relative date to now

In my current project, I needed to know if a given NSDate was today, yesterday or earlier. Simple task, I thought. Until I looked at the documentation for NSCalendar, NSDateComponents and NSDate. While these are extremely powerful, I am not proud of the code I’ve written. It should be easier.

Anyway, the code below will take a date and return a formatted version relative from today:

  • Today, 11h35
  • Yesterday, 8h27
  • January 12, 22h27

- (NSString *) formattedDateRelativeToNow:(NSDate *)date
{
  NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init]  autorelease];
  
  NSCalendar *gregorian = [[NSCalendar alloc] 
                                initWithCalendarIdentifier:NSGregorianCalendar];
  NSDateComponents *newsComponents = [gregorian components:(NSYearCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) 
                                                  fromDate:date];
  [newsComponents setHour:0];
  [newsComponents setMinute:0];
  [newsComponents setSecond:0];
  NSDate *dateAtMidnight =  [gregorian dateFromComponents:newsComponents];
  NSInteger dayDiff = [[gregorian components:(NSDayCalendarUnit) 
                                    fromDate:dateAtMidnight 
                                      toDate:[NSDate date] 
                                     options:0] 
                                              day];

  if (dayDiff == 0) {
    [dateFormatter setDateFormat:@"'today, 'H'h'mm"];
  } else {
    if (dayDiff == 1) {
      [dateFormatter setDateFormat:@"'yesterday, 'H'h'mm"];
    } else {
      [dateFormatter setDateFormat:@"MMMM d, H'h'mm"];
    }
  }
  [gregorian release];
  return [dateFormatter stringFromDate:date]; 
}

After having written this code, I found these similar posts on the subject (look at the answers given by mmalc – Malcolm Crawford):

Hint: if you display relative dates in your project, on the iPhone you should handle the « UIApplicationSignificantTimeChangeNotification » notification.

Update: I knew there was an easier way. Solution by Nicolas Seriot:

- (NSString *) formattedDateRelativeToNow2:(NSDate *)date {
  NSDateFormatter *mdf = [[NSDateFormatter alloc] init];
  [mdf setDateFormat:@"yyyy-MM-dd"];
  NSDate *midnight = [mdf dateFromString:[mdf stringFromDate:date]];
  [mdf release];
  
  NSUInteger dayDiff = (int)[midnight timeIntervalSinceNow] / (60*60*24);
  
  NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
  
  switch(dayDiff) {
    case 0:
      [dateFormatter setDateFormat:@"'today, 'H'h'mm"]; break;
    case -1:
      [dateFormatter setDateFormat:@"'yesterday, 'H'h'mm"]; break;
    default:
      [dateFormatter setDateFormat:@"MMMM d, H'h'mm"];
  }
  
  return [dateFormatter stringFromDate:date];
}