Short formatted relative time in iOS?

2.9k Views Asked by At

I'm using the NSDate-TimeAgo library to format relative time in my iOS application. It displays the relative time of each post in its cell header, as so:

NSDateFormatter *df = [[NSDateFormatter alloc] init];
        [df setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
        NSDate *myDate = [df dateFromString: self.datePostedString];
        UILabel *time = [[UILabel alloc]initWithFrame:CGRectMake(230,0,80,50)];
        DDLogVerbose(@"Output is: \"%@\"", [myDate dateTimeAgo]);
        time.text = [myDate dateTimeAgo];
        time.textAlignment = NSTextAlignmentRight;
        [time setFont:[UIFont systemFontOfSize:10]];

However, this library gives me strings that I find are too long. A post in the last minute would be labeled as "a minute ago". I'd like it to be shorter, such as "1m".

Is this possible to do without having to modify the library, or is there an alternative that already does this?

5

There are 5 best solutions below

2
Danny Xu On BEST ANSWER

You can not achieve this without changing the third-party class. In your situation, NSDate-TimeAgo library is too powerful for you. You even don't need any 'ago' result. So, personally I suggest you to convert time string on your own code, something like (just an example, maybe there are some performance issues here, you can use Time Profile to test it):

+ (NSString *)stringForDisplayFromDate:(NSDate *)date
{
    if (!date) {
        return nil;
    }

    NSDate *currentDate = [NSDate date];
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components:(kCFCalendarUnitYear
                                                     |kCFCalendarUnitMonth
                                                     |kCFCalendarUnitWeek
                                                     |kCFCalendarUnitDay
                                                     |kCFCalendarUnitHour
                                                     |kCFCalendarUnitMinute)
                                           fromDate:date
                                             toDate:currentDate
                                            options:0];
    if (components.year == 0) {
        // same year
        if (components.month == 0) {
            // same month
            if (components.week == 0) {
                // same week
                if (components.day == 0) {
                    // same day
                    if (components.hour == 0) {
                        // same hour
                        if (components.minute < 10) {
                            // in 10 mins
                            return @"now";
                        } else {
                            // 10 mins age
                            return [NSString stringWithFormat:@"%dm", (int)(components.minute/10)*10];
                        }
                    } else {
                        // different hour
                        return [NSString stringWithFormat:@"%dh", components.hour];
                    }
                } else {
                    // different day
                    return [NSString stringWithFormat:@"%dd", components.day];
                }    
            } else {
                // different week
                return [NSString stringWithFormat:@"%dW", components.week];
            }
        } else {
            // different month
            return [NSString stringWithFormat:@"%dM", components.month];
        }
    } else {
        // different year
        return [NSString stringWithFormat:@"%dY", components.year];
    }

    return [self stringForDisplayFromDate:date prefixed:NO];
}
0
icodesign On

I think you should probably write you own function.

- (NSString *)timeAgo:(NSDate *)compareDate{
  NSTimeInterval timeInterval = -[compareDate timeIntervalSinceNow];
  int temp = 0;
  NSString *result;
  if (timeInterval < 60) {
    result = [NSString stringWithFormat:@"Just now"];   //less than a minute
  }else if((temp = timeInterval/60) <60){
    result = [NSString stringWithFormat:@"%dm",temp];   //minutes ago
  }else if((temp = temp/60) <24){
    result = [NSString stringWithFormat:@"%dh",temp];   //hours ago
  }else{
    temp = temp / 24;
    result = [NSString stringWithFormat:@"%dd",temp];   //days ago
  }
  return  result;
}
0
ndbroadbent On

You should use DateTools. See the Time Ago section in the README, it does exactly what you want:

NSDate *timeAgoDate = [NSDate dateWithTimeIntervalSinceNow:-4];
NSLog(@"Time Ago: %@", timeAgoDate.shortTimeAgoSinceNow);

//Output:
//Time Ago: 4s

Note that this solution supports 33 languages (at the time of writing).

0
Nikita Khandelwal On

This function will return NSString starting from sec to years. Like if your date is of "1 sec ago" or if it is of "1 min ago" or "1 year ago" and so on.. it will return likewise..

-(NSString*)HourCalculation:(NSString*)PostDate

{
    NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
    [dateFormat setTimeZone:gmt];
    NSDate *ExpDate = [dateFormat dateFromString:PostDate];
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components:(NSDayCalendarUnit|NSWeekCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit|NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:ExpDate toDate:[NSDate date] options:0];
    NSString *time;
    if(components.year!=0)
    {
        if(components.year==1)
        {
            time=[NSString stringWithFormat:@"%ld year",(long)components.year];
        }
        else
        {
            time=[NSString stringWithFormat:@"%ld years",(long)components.year];
        }
    }
    else if(components.month!=0)
    {
        if(components.month==1)
        {
            time=[NSString stringWithFormat:@"%ld month",(long)components.month];
        }
        else
        {
            time=[NSString stringWithFormat:@"%ld months",(long)components.month];
        }
    }
    else if(components.week!=0)
    {
        if(components.week==1)
        {
            time=[NSString stringWithFormat:@"%ld week",(long)components.week];
        }
        else
        {
            time=[NSString stringWithFormat:@"%ld weeks",(long)components.week];
        }
    }
    else if(components.day!=0)
    {
        if(components.day==1)
        {
            time=[NSString stringWithFormat:@"%ld day",(long)components.day];
        }
        else
        {
            time=[NSString stringWithFormat:@"%ld days",(long)components.day];
        }
    }
    else if(components.hour!=0)
    {
        if(components.hour==1)
        {
            time=[NSString stringWithFormat:@"%ld hour",(long)components.hour];
        }
        else
        {
            time=[NSString stringWithFormat:@"%ld hours",(long)components.hour];
        }
    }
    else if(components.minute!=0)
    {
        if(components.minute==1)
        {
            time=[NSString stringWithFormat:@"%ld min",(long)components.minute];
        }
        else
        {
            time=[NSString stringWithFormat:@"%ld mins",(long)components.minute];
        }
    }
    else if(components.second>=0)
    {
        if(components.second==0)
        {
            time=[NSString stringWithFormat:@"1 sec"];
        }
        else
        {
            time=[NSString stringWithFormat:@"%ld secs",(long)components.second];
        }
    }
    return [NSString stringWithFormat:@"%@ ago",time];
}
0
pteofil On

Here's a Swift implementation based on Lei Wang answer.

func getShortRelativeDateFor(date: NSDate) ->String { 

    let timeInterval = -date.timeIntervalSinceNow

    switch timeInterval {
    case 0..<60:
            return String(format: "%.fs", timeInterval)
    case 60..<(60 * 60):
        return String(format: "%.fm", timeInterval / 60)
    case (60 * 60)..<(60 * 60 * 24):
        return String(format: "%.fh", timeInterval / (60 * 60))
    case (60 * 60 * 24)..<(60 * 60 * 24 * 365):
        return String(format: "%.fd", timeInterval / (60 * 60 * 24))
    default:
        return String(format: "%.fy", timeInterval / (60 * 60 * 24 * 365))
    }
}