Относительная строка из NSDate
Кто-нибудь знает библиотеку или что-то, что преобразует NSDate в строки, подобные примерам ниже?
1 hour ago
yesterday
last Thursday
2 days ago
last month
6 months ago
last year
Ответы
Ответ 1
NSDateFormatter будет делать многое из того, что упоминалось выше, используя setDoesRelativeDateFormatting: на экземпляре. Оттуда вы можете управлять форматированием, используя стиль даты и стиль времени, чтобы точно настроить его на свои нужды.
Если это не поможет вам, посмотрите SORelativeDateTransformer, это подкласс NSValueTransformer, который пытается предоставить такое же поведение вы видите с относительными датами на SO.
Наконец, если вы хотите перевернуть свои собственные или сделать больше исследований, это (вероятный) оригинальный вопрос по теме... да, это вопрос 11.
Ответ 2
Дата математика отстой. Посмотрите библиотеку хронологии Дэйва Делонга (https://github.com/davedelong/Chronology) или, для iOS 13+, (NS) RelativeDateTimeFormatter
Я оставляю ниже для потомков, но это определенно неправильная вещь. Мой обновленный ответ выше.
Вот метод категории NSDate, который я создал вместе, который использует расчет относительности Qaru, связанный Дэвидом, а также другие советы, придуманные из других подобных вопросов SO:
- (NSString *)relativeDateString
{
const int SECOND = 1;
const int MINUTE = 60 * SECOND;
const int HOUR = 60 * MINUTE;
const int DAY = 24 * HOUR;
const int MONTH = 30 * DAY;
NSDate *now = [NSDate date];
NSTimeInterval delta = [self timeIntervalSinceDate:now] * -1.0;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSUInteger units = (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit);
NSDateComponents *components = [calendar components:units fromDate:self toDate:now options:0];
NSString *relativeString;
if (delta < 0) {
relativeString = @"!n the future!";
} else if (delta < 1 * MINUTE) {
relativeString = (components.second == 1) ? @"One second ago" : [NSString stringWithFormat:@"%d seconds ago",components.second];
} else if (delta < 2 * MINUTE) {
relativeString = @"a minute ago";
} else if (delta < 45 * MINUTE) {
relativeString = [NSString stringWithFormat:@"%d minutes ago",components.minute];
} else if (delta < 90 * MINUTE) {
relativeString = @"an hour ago";
} else if (delta < 24 * HOUR) {
relativeString = [NSString stringWithFormat:@"%d hours ago",components.hour];
} else if (delta < 48 * HOUR) {
relativeString = @"yesterday";
} else if (delta < 30 * DAY) {
relativeString = [NSString stringWithFormat:@"%d days ago",components.day];
} else if (delta < 12 * MONTH) {
relativeString = (components.month <= 1) ? @"one month ago" : [NSString stringWithFormat:@"%d months ago",components.month];
} else {
relativeString = (components.year <= 1) ? @"one year ago" : [NSString stringWithFormat:@"%d years ago",components.year];
}
return relativeString;
}
Ответ 3
НЕ используйте собственный собственный перевод строк. Написание собственного перевода, которое делает что-то вроде "3 секунды назад", не только требует постоянной необходимости обновления, но также не локализует на другие языки.
Вместо этого используйте Apple, встроенную в NSDateFormatter:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.doesRelativeDateFormatting = YES;
formatter.locale = [NSLocale currentLocale];
formatter.dateStyle = NSDateFormatterShortStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
NSString *timeString = [formatter stringFromDate:self.date];
Это выведет что-то вроде "Сегодня, 11:10 вечера" или "7/17/14, 5:44 AM".
Ответ 4
Похоже, что TTTTimeIntervalFormatter в FormatterKit может также помочь в генерации относительных строк интервала времени.
Ответ 5
DateTools - потрясающая всеобъемлющая структура для выполнения всех видов связанных с датой функций. https://github.com/MatthewYork/DateTools
Как категория NSDate, это проще в использовании, чем SORelativeDateTransformer.
Однако есть проблема с получением точных значений дня. (Например, если сегодня утром в понедельник, субботний вечер будет представлен как "вчера", должен быть "два дня назад" )
Это должно работать лучше (используя DateTools):
- (NSString *)relativeDateStringForDate:(NSDate *)d
{
if ([d daysAgo] < 1) {
//Return as is
return [d timeAgoSinceNow];
}
else { //More than 24 hours ago. The default implementation will underreport (round down) day durations.
//We should modify the reference date we're using to midnight on the current day.
NSDate *date = [NSDate date];
date = [date dateByAddingDays:1];
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSUInteger preservedComponents = (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit);
NSDate *todayAtMidnight = [calendar dateFromComponents:[calendar components:preservedComponents fromDate:date]];
return [d timeAgoSinceDate:todayAtMidnight];
}
}