iPhone: конвертировать строку даты в метку относительного времени - PullRequest
39 голосов
/ 24 мая 2009

У меня есть метка времени в виде строки:

Четверг, 21 мая 09 19:10:09 -0700

и я хотел бы преобразовать его в метку относительного времени, например «20 минут назад» или «3 дня назад».

Какой лучший способ сделать это, используя Objective-C для iPhone?

Ответы [ 11 ]

72 голосов
/ 31 мая 2009
-(NSString *)dateDiff:(NSString *)origDate {
    NSDateFormatter *df = [[NSDateFormatter alloc] init];
    [df setFormatterBehavior:NSDateFormatterBehavior10_4];
    [df setDateFormat:@"EEE, dd MMM yy HH:mm:ss VVVV"];
    NSDate *convertedDate = [df dateFromString:origDate];
    [df release];
    NSDate *todayDate = [NSDate date];
    double ti = [convertedDate timeIntervalSinceDate:todayDate];
    ti = ti * -1;
    if(ti < 1) {
        return @"never";
    } else  if (ti < 60) {
        return @"less than a minute ago";
    } else if (ti < 3600) {
        int diff = round(ti / 60);
        return [NSString stringWithFormat:@"%d minutes ago", diff];
    } else if (ti < 86400) {
        int diff = round(ti / 60 / 60);
        return[NSString stringWithFormat:@"%d hours ago", diff];
    } else if (ti < 2629743) {
        int diff = round(ti / 60 / 60 / 24);
        return[NSString stringWithFormat:@"%d days ago", diff];
    } else {
        return @"never";
    }   
}
22 голосов
/ 24 мая 2009

Вот методы из Какао, которые помогут вам получить соответствующую информацию (не уверен, что все они доступны в Coca-Touch).

    NSDate * today = [NSDate date];
    NSLog(@"today: %@", today);

    NSString * str = @"Thu, 21 May 09 19:10:09 -0700";
    NSDate * past = [NSDate dateWithNaturalLanguageString:str
                            locale:[[NSUserDefaults 
                            standardUserDefaults] dictionaryRepresentation]];

    NSLog(@"str: %@", str);
    NSLog(@"past: %@", past);

    NSCalendar *gregorian = [[NSCalendar alloc]
                             initWithCalendarIdentifier:NSGregorianCalendar];
    unsigned int unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | 
                             NSDayCalendarUnit | 
                             NSHourCalendarUnit | NSMinuteCalendarUnit | 
                             NSSecondCalendarUnit;
    NSDateComponents *components = [gregorian components:unitFlags
                                                fromDate:past
                                                  toDate:today
                                                 options:0];

    NSLog(@"months: %d", [components month]);
    NSLog(@"days: %d", [components day]);
    NSLog(@"hours: %d", [components hour]);
    NSLog(@"seconds: %d", [components second]);

Кажется, что объект NSDateComponents содержит разницу в соответствующих единицах (как указано). Если вы укажете все единицы измерения, то сможете использовать этот метод:

void dump(NSDateComponents * t)
{
    if ([t year]) NSLog(@"%d years ago", [t year]);
    else if ([t month]) NSLog(@"%d months ago", [t month]);
    else if ([t day]) NSLog(@"%d days ago", [t day]);
    else if ([t minute]) NSLog(@"%d minutes ago", [t minute]);
    else if ([t second]) NSLog(@"%d seconds ago", [t second]);
}

Если вы хотите рассчитать себя, вы можете взглянуть на:

NSDate timeIntervalSinceDate

А затем использовать секунды в алгоритме.

Отказ от ответственности : если этот интерфейс устарел (я не проверял), предпочтительный способ Apple сделать это с помощью NSDateFormatters, как предлагается в комментариях ниже, также выглядит довольно аккуратно - я Я сохраню свой ответ по историческим причинам, для некоторых все же будет полезно взглянуть на использованную логику.

14 голосов
/ 04 августа 2009

Я пока не могу редактировать, но я взял код Джилин, сделал пару настроек и сделал его категорией NSDateFormatter.

Он принимает строку формата, поэтому он будет работать с произвольными строками, и я добавил, что предложения, имеющие единичные события, будут грамматически правильными.

Приветствия

Карл С-М

@interface NSDateFormatter (Extras)
+ (NSString *)dateDifferenceStringFromString:(NSString *)dateString
                                  withFormat:(NSString *)dateFormat;

@end

@implementation NSDateFormatter (Extras)

+ (NSString *)dateDifferenceStringFromString:(NSString *)dateString
                                  withFormat:(NSString *)dateFormat
{
  NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
  [dateFormatter setDateFormat:dateFormat];
  NSDate *date = [dateFormatter dateFromString:dateString];
  [dateFormatter release];
  NSDate *now = [NSDate date];
  double time = [date timeIntervalSinceDate:now];
  time *= -1;
  if(time < 1) {
    return dateString;
  } else if (time < 60) {
    return @"less than a minute ago";
  } else if (time < 3600) {
    int diff = round(time / 60);
    if (diff == 1) 
      return [NSString stringWithFormat:@"1 minute ago", diff];
    return [NSString stringWithFormat:@"%d minutes ago", diff];
  } else if (time < 86400) {
    int diff = round(time / 60 / 60);
    if (diff == 1)
      return [NSString stringWithFormat:@"1 hour ago", diff];
    return [NSString stringWithFormat:@"%d hours ago", diff];
  } else if (time < 604800) {
    int diff = round(time / 60 / 60 / 24);
    if (diff == 1) 
      return [NSString stringWithFormat:@"yesterday", diff];
    if (diff == 7) 
      return [NSString stringWithFormat:@"last week", diff];
    return[NSString stringWithFormat:@"%d days ago", diff];
  } else {
    int diff = round(time / 60 / 60 / 24 / 7);
    if (diff == 1)
      return [NSString stringWithFormat:@"last week", diff];
    return [NSString stringWithFormat:@"%d weeks ago", diff];
  }   
}

@end
8 голосов
/ 28 июня 2011

В интересах полноты, основанной на ответе @ Gilean, вот полный код для простой категории на NSDate, которая имитирует отличные помощники рельсов даты. Для обновления категорий это методы экземпляра, которые вы вызываете для объектов NSDate. Итак, если у меня есть NSDate, которая представляет вчерашний день, [myDate distanceOfTimeInWordsToNow] => «1 день».

Надеюсь, это полезно!

@interface NSDate (NSDate_Relativity)

-(NSString *)distanceOfTimeInWordsSinceDate:(NSDate *)aDate;
-(NSString *)distanceOfTimeInWordsToNow;

@end



@implementation NSDate (NSDate_Relativity)


-(NSString *)distanceOfTimeInWordsToNow {
    return [self distanceOfTimeInWordsSinceDate:[NSDate date]];

}

-(NSString *)distanceOfTimeInWordsSinceDate:(NSDate *)aDate {
    double interval = [self timeIntervalSinceDate:aDate];

    NSString *timeUnit;
    int timeValue;

    if (interval < 0) {
        interval = interval * -1;        
    }

    if (interval< 60) {
        return @"seconds";

    } else if (interval< 3600) { // minutes

        timeValue = round(interval / 60);

        if (timeValue == 1) {
            timeUnit = @"minute";

        } else {
            timeUnit = @"minutes";

        }


    } else if (interval< 86400) {
        timeValue = round(interval / 60 / 60);

        if (timeValue == 1) {
            timeUnit = @"hour";

        } else {
            timeUnit = @"hours";
        }


    } else if (interval< 2629743) {
        int days = round(interval / 60 / 60 / 24);

        if (days < 7) {

            timeValue = days;

            if (timeValue == 1) {
                timeUnit = @"day";
            } else {
                timeUnit = @"days";
            }

        } else if (days < 30) {
            int weeks = days / 7;

            timeValue = weeks;

            if (timeValue == 1) {
                timeUnit = @"week";
            } else {
                timeUnit = @"weeks";
            }


        } else if (days < 365) {

            int months = days / 30;
            timeValue = months;

            if (timeValue == 1) {
                timeUnit = @"month";
            } else {
                timeUnit = @"months";
            }

        } else if (days < 30000) { // this is roughly 82 years. After that, we'll say 'forever'
            int years = days / 365;
            timeValue = years;

            if (timeValue == 1) {
                timeUnit = @"year";
            } else {
                timeUnit = @"years";
            }

        } else {
            return @"forever ago";
        }
    }

    return [NSString stringWithFormat:@"%d %@", timeValue, timeUnit];

}

@end
6 голосов
/ 12 сентября 2013

Уже есть много ответов, которые приходят к одному и тому же решению, но выбор вариантов не помешает. Вот что я придумал.

- (NSString *)stringForTimeIntervalSinceCreated:(NSDate *)dateTime
{
    NSDictionary *timeScale = @{@"second":@1,
                                @"minute":@60,
                                @"hour":@3600,
                                @"day":@86400,
                                @"week":@605800,
                                @"month":@2629743,
                                @"year":@31556926};
    NSString *scale;
    int timeAgo = 0-(int)[dateTime timeIntervalSinceNow];
    if (timeAgo < 60) {
        scale = @"second";
    } else if (timeAgo < 3600) {
        scale = @"minute";
    } else if (timeAgo < 86400) {
        scale = @"hour";
    } else if (timeAgo < 605800) {
        scale = @"day";
    } else if (timeAgo < 2629743) {
        scale = @"week";
    } else if (timeAgo < 31556926) {
        scale = @"month";
    } else {
        scale = @"year";
    }

    timeAgo = timeAgo/[[timeScale objectForKey:scale] integerValue];
    NSString *s = @"";
    if (timeAgo > 1) {
        s = @"s";
    } 
    return [NSString stringWithFormat:@"%d %@%@ ago", timeAgo, scale, s];
}
4 голосов
/ 26 июля 2012

Я взял код Карла Кориелла-Мартина и создал более простую категорию NSDate, в которой нет предупреждений о форматировании строк в единственном числе, а также приведен порядок недели назад в единственном числе:

@interface NSDate (Extras)
- (NSString *)differenceString;
@end

@implementation NSDate (Extras)

- (NSString *)differenceString{
    NSDate* date = self;
    NSDate *now = [NSDate date];
    double time = [date timeIntervalSinceDate:now];
    time *= -1;
    if (time < 60) {
        int diff = round(time);
        if (diff == 1)
            return @"1 second ago";
        return [NSString stringWithFormat:@"%d seconds ago", diff];
    } else if (time < 3600) {
        int diff = round(time / 60);
        if (diff == 1)
            return @"1 minute ago";
        return [NSString stringWithFormat:@"%d minutes ago", diff];
    } else if (time < 86400) {
        int diff = round(time / 60 / 60);
        if (diff == 1)
            return @"1 hour ago";
        return [NSString stringWithFormat:@"%d hours ago", diff];
    } else if (time < 604800) {
        int diff = round(time / 60 / 60 / 24);
        if (diff == 1)
            return @"yesterday";
        if (diff == 7)
            return @"a week ago";
        return[NSString stringWithFormat:@"%d days ago", diff];
    } else {
        int diff = round(time / 60 / 60 / 24 / 7);
        if (diff == 1)
            return @"a week ago";
        return [NSString stringWithFormat:@"%d weeks ago", diff];
    }   
}

@end
3 голосов
/ 15 января 2015

In Swift

Использование:

let time = NSDate(timeIntervalSince1970: timestamp).timeIntervalSinceNow
let relativeTimeString = NSDate.relativeTimeInString(time)
println(relativeTimeString)

Расширение:

extension NSDate {
    class func relativeTimeInString(value: NSTimeInterval) -> String {
        func getTimeData(value: NSTimeInterval) -> (count: Int, suffix: String) {
            let count = Int(floor(value))
            let suffix = count != 1 ? "s" : ""
            return (count: count, suffix: suffix)
        }

        let value = -value
        switch value {
            case 0...15: return "just now"

            case 0..<60:
                let timeData = getTimeData(value)
                return "\(timeData.count) second\(timeData.suffix) ago"

            case 0..<3600:
                let timeData = getTimeData(value/60)
                return "\(timeData.count) minute\(timeData.suffix) ago"

            case 0..<86400:
                let timeData = getTimeData(value/3600)
                return "\(timeData.count) hour\(timeData.suffix) ago"

            case 0..<604800:
                let timeData = getTimeData(value/86400)
                return "\(timeData.count) day\(timeData.suffix) ago"

            default:
                let timeData = getTimeData(value/604800)
                return "\(timeData.count) week\(timeData.suffix) ago"
        }
    }
}
1 голос
/ 20 июля 2012

Мое решение:

- (NSString *) dateToName:(NSDate*)dt withSec:(BOOL)sec {

    NSLocale *locale = [NSLocale currentLocale];
    NSTimeInterval tI = [[NSDate date] timeIntervalSinceDate:dt];
    if (tI < 60) {
      if (sec == NO) {
           return NSLocalizedString(@"Just Now", @"");
       }
       return [NSString stringWithFormat:
                 NSLocalizedString(@"%d seconds ago", @""),(int)tI];
     }
     if (tI < 3600) {
       return [NSString stringWithFormat:
                 NSLocalizedString(@"%d minutes ago", @""),(int)(tI/60)];
     }
     if (tI < 86400) {
      return [NSString stringWithFormat:
                 NSLocalizedString(@"%d hours ago", @""),(int)tI/3600];
     }

     NSDateFormatter *relativeDateFormatter = [[NSDateFormatter alloc] init];
     [relativeDateFormatter setTimeStyle:NSDateFormatterNoStyle];
     [relativeDateFormatter setDateStyle:NSDateFormatterMediumStyle];
     [relativeDateFormatter setDoesRelativeDateFormatting:YES];
     [relativeDateFormatter setLocale:locale];

     NSString * relativeFormattedString = 
            [relativeDateFormatter stringForObjectValue:dt];
     return relativeFormattedString;
}
1 голос
/ 24 мая 2009

Используйте класс NSDate:

timeIntervalSinceDate

возвращает интервал в секундах.

Быстрое упражнение для реализации этого в target-c:

  1. Получить время "сейчас" NSDate
  2. Получите NSDate, который вы хотите сравнить с
  3. Получить интервал в секундах, используя timeIntervalSinceDate

Затем реализуйте этот псевдокод:

if (x < 60) // x seconds ago

else if( x/60 < 60) // floor(x/60) minutes ago

else if (x/(60*60) < 24) // floor(x/(60*60) hours ago

else if (x/(24*60*60) < 7) // floor(x(24*60*60) days ago

и так далее ...

тогда вам нужно решить, будет ли месяц 30,31 или 28 дней. Будьте проще - выберите 30.

Возможно, есть лучший путь, но сейчас 2 часа ночи, и это первое, что пришло в голову ...

0 голосов
/ 19 апреля 2013

Я видел, что было несколько функций назад во фрагментах кода в Stack Overflow, и я хотел, чтобы эта функция действительно давала ясное представление о времени (поскольку произошло какое-то действие). Для меня это означает стиль «время назад» для коротких интервалов времени (5 минут назад, 2 часа назад) и конкретные даты для более длинных периодов времени (15 апреля 2011 года вместо 2 лет назад). В основном я думал, что Facebook проделал действительно хорошую работу в этом, и я хотел просто следовать их примеру (поскольку я уверен, что они много обдумали это, и это очень легко и ясно понять с точки зрения потребителя).

После долгого поиска в Google я был довольно удивлен, увидев, что никто, насколько я могу судить, не реализовал это. Решил, что хочу достаточно сильно потратить время на написание и подумал, что поделюсь.

Надеюсь, вам понравится:)

Получить код здесь: https://github.com/nikilster/NSDate-Time-Ago

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...