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];
}

8 réflexions au sujet de “Relative date to now”

  1. If there are less than 24h between the two dates, doesnt work:

    If [NSDate date] = 2009-02-20 12:26:00:

    NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
    [NSDateFormatter setDefaultFormatterBehavior:NSDateFormatterBehavior10_4];
    [formatter setDateFormat:@ »yyyy-MM-dd HH:mm:ss »]; /* Unicode Locale Data Markup Language */
    NSDate *date1 = [formatter dateFromString:@ »2009-02-20 12:10:00″];
    NSDate *date2 = [formatter dateFromString:@ »2009-02-19 14:10:00″];
    NSDate *date3 = [formatter dateFromString:@ »2009-02-18 23:59:00″];
    NSDate *date4 = [formatter dateFromString:@ »2009-02-18 11:30:00″];

    NSLog(@ »diff1: %d », (int)([date1 timeIntervalSinceNow] / (60*60*24)));
    NSLog(@ »diff2: %d », (int)([date2 timeIntervalSinceNow] / (60*60*24)));
    NSLog(@ »diff3: %d », (int)([date3 timeIntervalSinceNow] / (60*60*24)));
    NSLog(@ »diff4: %d », (int)([date4 timeIntervalSinceNow] / (60*60*24)));

    diff1: 0 correct: today
    diff2: 0 incorrect: yesterday
    diff3: -1 incorrect: two days ago
    diff4: -2 correct: two days ago

  2. Hi Stephan,

    Nice catch, and thanks for letting me know about the daysAgo issue.

    I really enjoyed your comment above, « Simple task, I thought. Until I looked at the documentation… », I experienced the same thing when putting together my own code. When I look around at examples it just seems like there aren’t too many snappy ways of doing this. Also, I’m spoiled by ActiveSupport in Rails, so that’s why I started the NSDate (Helper) project, figure at least group up some of these calculations.

    http://github.com/billymeltdown/nsdate-helper

    I should have the daysAgo fix in there soon!

    Cheers,
    Billy

  3. Thanks a lot! I’m still facing a lot of problems. Can you help me out a bit?

    Basically I’m trying to get the date and be able to get to the next day or previous day.. but I just can’t figure out! ( with the assumption of taking into consideration the handling of time zones and all different calendars around the world.

    Do you have any suggestions and all? I would even donate for the help!

  4. Hi Stephan

    That is what i’m trying to do, the problem is that I do not know how to get the « right date ». For example, I could get the day and all but I don’t know how to deal with daylight savings and timezones and all that. I’m lost man

  5. Even if the daylight savings changes, substracting or adding 86400 seconds to your date will always give you the correct day. No worries.

Laisser un commentaire