IPhone: конвертировать строку даты в относительную отметку времени
У меня есть метка времени как строка, например:
Thu, 21 мая 09 19:10:09 -0700
и я хотел бы преобразовать его в относительную отметку времени, например, "20 минут назад" или "3 дня назад".
Какой лучший способ сделать это с помощью Objective-C для iPhone?
Ответы
Ответ 1
-(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";
}
}
Ответ 2
Вот методы из Cocoa, чтобы помочь вам получить релевантную информацию (не уверен, что все они доступны в 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
, как это предлагается в комментариях ниже, также выглядит довольно аккуратно - я "Я буду отвечать за исторические причины, но может быть полезно, чтобы кто-то посмотрел на используемую логику.
Ответ 3
Я пока не могу редактировать, но я взял код Gilean и сделал пару настроек и сделал его категорией NSDateFormatter.
Он принимает строку формата, поэтому он будет работать с произвольными строками, и я добавил, если условия для сингулярных событий будут корректно корректными.
Приветствия,
Carl C-M
@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
Ответ 4
В интересах полноты, основанной на ответе @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
Ответ 5
Уже есть много ответов, которые приходят к одному и тому же решению, но не может навредить выборам. Вот что я придумал.
- (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];
}
Ответ 6
Я взял код Карла Корьелла-Мартина и сделал более простую категорию NSDate, у которой нет предупреждений о форматировании строк сингулярностей, а также tidys неделю назад:
@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
Ответ 7
В Swift
Использование:
let time = NSDate(timeIntervalSince1970: timestamp).timeIntervalSinceNow
let relativeTimeString = NSDate.relativeTimeInString(time)
println(relativeTimeString)
Extension:
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"
}
}
}
Ответ 8
Используйте класс NSDate:
timeIntervalSinceDate
возвращает интервал в секундах.
Быстрое выполнение, чтобы реализовать это в objective-c:
- Получить время "сейчас" NSDate
- Получить NSDate, который вы хотите сравнить с
- Получить интервал в секундах, используя 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.
Там может быть лучший способ, но его 2am и это первое, что пришло в голову...
Ответ 9
Мое решение:
- (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;
}
Ответ 10
Не уверен, почему это не в cocoa -touch, я бы неплохо сделал стандартный способ сделать это.
Настройте некоторые типы для хранения данных, это упростит, если вы когда-нибудь захотите локализовать его немного больше. (очевидно, расширяются, если вам нужно больше времени)
typedef struct DayHours {
int Days;
double Hours;
} DayHours;
+ (DayHours) getHourBasedTimeInterval:(double) hourBased withHoursPerDay:(double) hpd
{
int NumberOfDays = (int)(fabs(hourBased) / hpd);
float hoursegment = fabs(hourBased) - (NumberOfDays * hpd);
DayHours dh;
dh.Days = NumberOfDays;
dh.Hours = hoursegment;
return dh;
}
ПРИМЕЧАНИЕ. Я использую вычисление, основанное на часах, так как это то, что есть в моих данных. NSTimeInterval является вторым, мне также пришлось преобразовать между ними.
Ответ 11
Я видел, что несколько раз были функции в фрагментах кода в Stack Overflow, и я хотел, чтобы это действительно давало четкое представление о времени (так как произошло какое-то действие). Для меня это означает "время назад" стиль для коротких временных интервалов (5 мин. Назад, 2 часа назад) и конкретные даты для более длительных периодов времени (15 апреля 2011 года вместо 2 лет назад). В основном я думал, что Facebook действительно хорошо справился с этим, и я хотел просто пойти по их примеру (поскольку я уверен, что они много думают об этом, и это очень легко и понятно для понимания с точки зрения потребителя).
После долгого времени работы в Интернете я был очень удивлен, увидев, что никто не реализовал это, насколько я мог судить. Решил, что я хочу, чтобы это было достаточно плохо, чтобы тратить время на то, чтобы написать и подумать, что я поделюсь.
Надеюсь, вам понравится:)
Получить код здесь: https://github.com/nikilster/NSDate-Time-Ago