iPhone:将datestring转换为相对时间戳

我有一个时间戳,如下所示:

四月21日,星期四19:10:09 -0700

我想将其转换为“20分钟前”或“3天前”的相对时间戳。

用iPhone的Objective-C做这件事最好的方法是什么?

-(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"; } } 

这里有来自Cocoa的方法来帮助你获得相关的信息(不知道它们是否都是可口可乐)。

  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 

然后在algorithm中使用秒。

免责声明 :如果这个接口被弃用(我没有检查),苹果的通过NSDateFormatters这样做的首选方式,如下面的评论中所build议的,看起来相当整齐 – 我会保持我的答案的历史原因,它可能仍然对于一些用来查看所使用的逻辑是有用的。

我现在还不能编辑,但是我用了Gilean的代码,做了一些调整,并把它作为NSDateFormatter的一个类别。

它接受一个格式string,所以它将工作瓦特/任意string,我添加如果子句有单数事件在语法上是正确的。

干杯,

卡尔CM

 @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 

为了完整性,基于@ Gilean的答案,下面是NSDate上一个简单类的完整代码,它模仿了rails的漂亮date助手。 对于类别的更新,这些是您将调用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 

已经有很多答案来到相同的解决scheme,但不能伤害有select的。 这是我想出来的。

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

我拿了Carl Coryell-Martin的代码,做了一个更简单的NSDate类,它没有关于单数的string格式的警告,而且在上个星期之前还收集了单数:

 @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 

在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" } } } 

使用NSDate类:

 timeIntervalSinceDate 

以秒为单位返回间隔。

快速练习在objective-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天。 保持简单 – select30。

可能有更好的方法,但是凌晨2点,这是第一个想到的事情…

我的解决scheme

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

不知道为什么这不是在cocoa触摸,我做这个很好的标准方式将是伟大的。

设置一些types来保存数据,如果你需要更多的本地化数据,它会更容易。 (如果你需要更多的时间,显然会扩大)

 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是第二个基础,我也必须在两者之间进行转换。

我看到Stack Overflow中的代码片段中有几个前面的函数,并且我想要一个真正给出最清晰时间感的函数(因为发生了一些操作)。 对于我来说,这意味着在较短的时间间隔(5分钟前,2小时前)和较长时间段(2011年4月15日而不是2年前)的特定date的“时间前”风格。 基本上我认为Facebook在这方面做得非常好,我只想按照他们的例子去做(因为我确信他们在这方面有很多想法,从消费者的angular度来看很容易理解)。

经过了很长时间的search,我很惊讶地发现,没有人实现这一点,据我所知。 决定我想花时间写作,并认为我会分享。

希望你喜欢 :)

在这里获取代码: https : //github.com/nikilster/NSDate-Time-Ago