I have 2 text fields where users can insert 2 time (ie. 12.00pm and 15.00pm) and a label that return the hours (ie 3 hours). I'm using timeIntervalSinceDate but I'm getting it in seconds (10800 sec which is equal to 3 hours). How can I get the value in hours? here the code:
-(IBAction)calcoloBlockTime{
NSString *blockOff = [NSString stringWithFormat:#"%#", [offBlock text]];
NSString *blockIn= [NSString stringWithFormat: #"%#", [inBlock text]];
NSDateFormatter *dateFormatter =[[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"HHmm"];
NSDate *dateFromString = [dateFormatter dateFromString:blockOff];
NSDate *dateFromString2 = [dateFormatter dateFromString:blockIn];
NSLog(#"Time elapsed: %f", [dateFromString2 timeIntervalSinceDate:dateFromString]);
}
thanks in advance.
If you are only interested in hours you can write:
NSInteger hours = 10800 / 3600; // You know that in an hour there is 3600 seconds
If you want minutes etc you will continue with the result of 10800 modulus 3600, which will give you the seconds which are left after dividing it by 3600.
NSInteger temp = 10800 % 3600;
NSInteger minutes = temp / 60;
And to get the seconds left after this you write:
NSInteger seconds = minutes % 60;
EDIT
myLabel.text = [NSString stringWithFormat:#"%d hours", hours];
If you want to set the hours on the label.
hours = seconds / 3600; Simple!!! And, it seems there is no way to get the hour from NSDate. You can use NSDateComponents, but they are too costly for your requirement.
I made a class method that returns a string with a formatted date of the remaining time between an NSDate and now.
+(NSString *)TimeRemainingUntilDate:(NSDate *)date {
NSTimeInterval interval = [date timeIntervalSinceNow];
NSString * timeRemaining = nil;
if (interval > 0) {
div_t d = div(interval, 86400);
int day = d.quot;
div_t h = div(d.rem, 3600);
int hour = h.quot;
div_t m = div(h.rem, 60);
int min = m.quot;
NSString * nbday = nil;
if(day > 1)
nbday = #"days";
else if(day == 1)
nbday = #"day";
else
nbday = #"";
NSString * nbhour = nil;
if(hour > 1)
nbhour = #"hours";
else if (hour == 1)
nbhour = #"hour";
else
nbhour = #"";
NSString * nbmin = nil;
if(min > 1)
nbmin = #"mins";
else
nbmin = #"min";
timeRemaining = [NSString stringWithFormat:#"%#%# %#%# %#%#",day ? [NSNumber numberWithInt:day] : #"",nbday,hour ? [NSNumber numberWithInt:hour] : #"",nbhour,min ? [NSNumber numberWithInt:min] : #"",nbmin];
}
else
timeRemaining = #"Over";
return timeRemaining;
}
Related
I need a method to format a NSTimeInterval (time span in seconds) into a string to produce something like "about 10 minutes ago", "1h, 20min", or "less than 1 minute".
-(NSString*) formattedTimeSpan:(NSTimeInterval)interval;
Target platform is iOS. Sample code is welcome.
This is a category for NSDate. It's not exactly using an NSTimeInterval, well internally :) I assume you are working with timestamps.
Header file NSDate+PrettyDate.h
#interface NSDate (PrettyDate)
- (NSString *)prettyDate;
#end
Implementation NSDate+PrettyDate.m
#implementation NSDate (PrettyDate)
- (NSString *)prettyDate
{
NSString * prettyTimestamp;
float delta = [self timeIntervalSinceNow] * -1;
if (delta < 60) {
prettyTimestamp = #"just now";
} else if (delta < 120) {
prettyTimestamp = #"one minute ago";
} else if (delta < 3600) {
prettyTimestamp = [NSString stringWithFormat:#"%d minutes ago", (int) floor(delta/60.0) ];
} else if (delta < 7200) {
prettyTimestamp = #"one hour ago";
} else if (delta < 86400) {
prettyTimestamp = [NSString stringWithFormat:#"%d hours ago", (int) floor(delta/3600.0) ];
} else if (delta < ( 86400 * 2 ) ) {
prettyTimestamp = #"one day ago";
} else if (delta < ( 86400 * 7 ) ) {
prettyTimestamp = [NSString stringWithFormat:#"%d days ago", (int) floor(delta/86400.0) ];
} else {
NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterMediumStyle];
prettyTimestamp = [NSString stringWithFormat:#"on %#", [formatter stringFromDate:self]];
[formatter release];
}
return prettyTimestamp;
}
You might want to refer to Facebook three20 framework. In their NSDateAdditions, they provided a few pretty formats for date. It might be better than you extend it.
Refer to the source at https://github.com/facebook/three20/blob/master/src/Three20Core/Sources/NSDateAdditions.m
- (NSString*)formatShortRelativeTime; will give you "<1m", "50m", "3h", "3d"
Here is one that formats the date in the style of Facebook:
https://github.com/nikilster/NSDate-Time-Ago
I am new to iphone development, I am developing an application, which takes restaurant order data (Name of Restaurant and order price) and save to an array and display on another UITable, but now I want to start a timer such as
Royal Taj
$35
1 hour, 40 mints remaining,
so how can I count such time, on different orders, I need a little guide regarding it, and time should go in decrement, not increment ,,,,
if you are not clear about my question, then u can ask me anything again....
here is what i have done and its working ...........
{
NSDictionary* temp = [allOrder objectAtIndex:indexPath.row];
NSDate* dt = (NSDate*)[temp objectForKey:#"date"];
NSTimeInterval inter = [dt timeIntervalSinceNow];
int hour = inter / 3600;
int min = ((long)inter % 3600)/60;
NSString* str;
if (hour == 0)
{
if (min == 0)
str = [NSString stringWithFormat:#"Order is ready to serve"];
else
str = [NSString stringWithFormat:#"%d minutes remaining",min];
}
else {
if (min == 0)
str = [NSString stringWithFormat:#"%d hours remaining",hour];
else
str = [NSString stringWithFormat:#"%d hours and %d minutes remaining",hour,min];
}
cell.detailTextLabel.text = str;
}
here i supposed that u have an array allOrder which contains dictionaries which has object for keys #"date" as the date on which order has to be delivered.
I got help with correct calculation the other day but I've hit a block about how to implement it into code.
-(IBAction)done:(id)sender {
int result = (([startHours.text intValue] * 60) + [startMinutes.text intValue]) -
(([finishHours.text intValue] * 60) + [finishMinutes.text intValue]);
totalHours.text = [NSString stringWithFormat:#"%i", result / 60];
if (result < 0) {
totalHours.text = [NSString stringWithFormat:#"%d", result * -1];
}
The above is the code. However, in the text field it comes out as the total number of minutes. I want to covert it so it would show up as total hours and minutes (10.30). How would I do that in code?
If result is a time in minutes:
int minutes = result%60
int hours = (result - minutes)/60
totalHours.text = [NSString stringWithFormat:#"%d.%d", hours, minutes];
Pheelicks's answer is OK. Just two small additions: handle sign of interval before calculating parts, and use more common time format:
int result = (([startHours.text intValue] * 60) + [startMinutes.text intValue]) -
(([finishHours.text intValue] * 60) + [finishMinutes.text intValue]);
int minutes = abs(result)%60
int hours = (abs(result) - minutes)/60
totalHours.text = [NSString stringWithFormat:#"%02d:%02d", hours, minutes];
Try changing the int to a float.
-(IBAction)done:(id)sender {
float result = (([startHours.text intValue] * 60) + [startMinutes.text intValue]) -
(([finishHours.text intValue] * 60) + [finishMinutes.text intValue]);
totalHours.text = [NSString stringWithFormat:#"%2.2f", result / 60];
if (result < 0) {
totalHours.text = [NSString stringWithFormat:#"%2.2f", result * -1];
}
Forgive me as I'm new to Objective C.
I am getting back dates from a .NET webservice in the /Date(xxxxxxxxxxxxx-xxxx)/ format. I'm looking for some direction on how to best parse this into an NSDate object. I've tried using dateWithTimeIntervalSince1970 on it but it comes back with a date in the year 1969 for a date I know is in 2006.
Looking for some direction on the proper way to handle JSON dates.
Thanks in advance!
I just wrote this for iOS 4.0+ (because it uses NSRegularExpression). It handles dates with or without timezone offsets. Seems to work pretty well, what do you think?
+ (NSDate *)mfDateFromDotNetJSONString:(NSString *)string {
static NSRegularExpression *dateRegEx = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dateRegEx = [[NSRegularExpression alloc] initWithPattern:#"^\\/date\\((-?\\d++)(?:([+-])(\\d{2})(\\d{2}))?\\)\\/$" options:NSRegularExpressionCaseInsensitive error:nil];
});
NSTextCheckingResult *regexResult = [dateRegEx firstMatchInString:string options:0 range:NSMakeRange(0, [string length])];
if (regexResult) {
// milliseconds
NSTimeInterval seconds = [[string substringWithRange:[regexResult rangeAtIndex:1]] doubleValue] / 1000.0;
// timezone offset
if ([regexResult rangeAtIndex:2].location != NSNotFound) {
NSString *sign = [string substringWithRange:[regexResult rangeAtIndex:2]];
// hours
seconds += [[NSString stringWithFormat:#"%#%#", sign, [string substringWithRange:[regexResult rangeAtIndex:3]]] doubleValue] * 60.0 * 60.0;
// minutes
seconds += [[NSString stringWithFormat:#"%#%#", sign, [string substringWithRange:[regexResult rangeAtIndex:4]]] doubleValue] * 60.0;
}
return [NSDate dateWithTimeIntervalSince1970:seconds];
}
return nil;
}
I was in the same boat whilst using json-framework which doesn't support the date format as it's not official JSON. My source is from an API built using JSON.Net. This is what I came up with:
- (NSDate*) getDateFromJSON:(NSString *)dateString
{
// Expect date in this format "/Date(1268123281843)/"
int startPos = [dateString rangeOfString:#"("].location+1;
int endPos = [dateString rangeOfString:#")"].location;
NSRange range = NSMakeRange(startPos,endPos-startPos);
unsigned long long milliseconds = [[dateString substringWithRange:range] longLongValue];
NSLog(#"%llu",milliseconds);
NSTimeInterval interval = milliseconds/1000;
return [NSDate dateWithTimeIntervalSince1970:interval];
}
I don't have the appended portion in the date format that you do so I haven't dealt with that like the answer above. No error catching either, it's all new to me at this point.
I actually found the snippet with NSRegularExpression pretty useful, till i came up with another solution that uses NSCharecterSet for stipping off the milliseconds.
+ (NSDate*) dateFromJSONString:(NSString *)dateString
{
NSCharacterSet *charactersToRemove = [[ NSCharacterSet decimalDigitCharacterSet ] invertedSet ];
NSString* milliseconds = [dateString stringByTrimmingCharactersInSet:charactersToRemove];
if (milliseconds != nil && ![milliseconds isEqualToString:#"62135596800000"]) {
NSTimeInterval seconds = [milliseconds doubleValue] / 1000;
return [NSDate dateWithTimeIntervalSince1970:seconds];
}
return nil;
}
Saves a lot of the manual string processing and makes the code much cleaner.
As a .NET programmer learning Objective-C I had the same problem when I tried to consume a .Net WebService.
At first I thought I would be able to use the NSDateFormatter...
I found a really good reference for it's symbols here, but I quickly realized that I needed to convert the number from milliseconds to seconds.
I wrote the code to do it...
I'm still learning Obj-C but I dont think It should've been this hard...
- (NSDate *) getJSONDate{
NSString* header = #"/Date(";
uint headerLength = [header length];
NSString* timestampString;
NSScanner* scanner = [[NSScanner alloc] initWithString:self];
[scanner setScanLocation:headerLength];
[scanner scanUpToString:#")" intoString:×tampString];
NSCharacterSet* timezoneDelimiter = [NSCharacterSet characterSetWithCharactersInString:#"+-"];
NSRange rangeOfTimezoneSymbol = [timestampString rangeOfCharacterFromSet:timezoneDelimiter];
[scanner dealloc];
if (rangeOfTimezoneSymbol.length!=0) {
scanner = [[NSScanner alloc] initWithString:timestampString];
NSRange rangeOfFirstNumber;
rangeOfFirstNumber.location = 0;
rangeOfFirstNumber.length = rangeOfTimezoneSymbol.location;
NSRange rangeOfSecondNumber;
rangeOfSecondNumber.location = rangeOfTimezoneSymbol.location + 1;
rangeOfSecondNumber.length = [timestampString length] - rangeOfSecondNumber.location;
NSString* firstNumberString = [timestampString substringWithRange:rangeOfFirstNumber];
NSString* secondNumberString = [timestampString substringWithRange:rangeOfSecondNumber];
unsigned long long firstNumber = [firstNumberString longLongValue];
uint secondNumber = [secondNumberString intValue];
NSTimeInterval interval = firstNumber/1000;
return [NSDate dateWithTimeIntervalSince1970:interval];
}
unsigned long long firstNumber = [timestampString longLongValue];
NSTimeInterval interval = firstNumber/1000;
return [NSDate dateWithTimeIntervalSince1970:interval];
}
Hopefully someone can provide a better Obj-C solution.
If not I may keep this or look for a way to change the serialization format in .NET
EDIT:
About that JSON DateTime format...
If you have any control on the service it would probably be best to convert the date to a string in your DataContract objects.
Formatting to RFC1123 seems like a good idea to me right now. As I can probably pick it up easily using a NSDateFormatter.
Quote from Rick Strahl
There's no JavaScript date literal and Microsoft engineered a custom date format that is essentially a marked up string. The format is a string that's encoded and contains the standard new Date(milliseconds since 1970) value.
Theory: MS encoded the C# DateTime in JSON as milliseconds since 1970.
Solution:
NSString*
dateAsString = #"/Date(1353720343336+0000)/";
dateAsString = [dateAsString stringByReplacingOccurrencesOfString:#"/Date("
withString:#""];
dateAsString = [dateAsString stringByReplacingOccurrencesOfString:#"+0000)/"
withString:#""];
unsigned long long milliseconds = [dateAsString longLongValue];
NSTimeInterval interval = milliseconds/1000;
NSDate* date = [NSDate dateWithTimeIntervalSince1970:interval];
This is the shortest solution I can think of.
Use an NSDateFormatter's dateFromString: method after setting the date format.
-(NSString*)convertToUTCTime:(NSString*)strDate{
NSDate *currentDate = [NSDate date];
myDate = [commonDateFormatter dateFromString: strDate];
NSTimeInterval distanceBetweenDates = [currentDate timeIntervalSinceDate:myDate];
return [self stringFromTimeInterval:distanceBetweenDates];
}
- (NSString *)stringFromTimeInterval:(NSTimeInterval)interval {
NSInteger ti = (NSInteger)interval;
NSInteger minutes = (ti / 60) % 60;
NSInteger hours = (ti / 3600);
if (hours > 24) {
NSInteger days = hours/24;
if (days > 30) {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"EEE d MMM, h:mm a"];
//[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"IST"]];
NSString *daydate = [dateFormatter stringFromDate:myDate];
return daydate;
}
else{
return [NSString stringWithFormat:#" %2ldd",(long)days];
}
}else{
if (hours == 0 && minutes < 1) {
return [NSString stringWithFormat:#"Today"];
}
else if (hours == 0 && minutes < 60){
return [NSString stringWithFormat:#"%2ldm ",(long)minutes];
}
else{
return [NSString stringWithFormat:#" %2ldh",(long)hours];
}
}
}
I've got a timestamp as a string like:
Thu, 21 May 09 19:10:09 -0700
and I'd like to convert it to a relative time stamp like '20 minutes ago' or '3 days ago'.
What's the best way to do this using Objective-C for the iPhone?
-(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";
}
}
Here are methods from Cocoa to help you to get relevant info (not sure if they are all available in 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]);
The NSDateComponents object seems to hold the difference in relevant units (as specified).
If you specify all units you can then use this method:
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]);
}
If you want to calculate yourself you can have a look at:
NSDate timeIntervalSinceDate
And then use seconds in the algorithm.
Disclaimer: If this interface is getting deprecated (I haven't checked), Apple's preferred way of doing this via NSDateFormatters, as suggested in comments below, looks pretty neat as well - I'll keep my answer for historical reasons, it may still be useful for some to look at the logic used.
I can't edit yet, but I took Gilean's code and made a couple of tweaks and made it a category of NSDateFormatter.
It accepts a format string so it will work w/ arbitrary strings and I added if clauses to have singular events be grammatically correct.
Cheers,
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
In the interest of completeness, based on a #Gilean's answer, here's the complete code for a simple category on NSDate that mimics rails' nifty date helpers. For a refresher on categories, these are instance methods that you would call on NSDate objects. So, if I have an NSDate that represents yesterday, [myDate distanceOfTimeInWordsToNow] => "1 day".
Hope it's useful!
#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
There are already a lot of answers that come to the same solution but it can't hurt to have choices. Here's what I came up with.
- (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];
}
I took Carl Coryell-Martin's code and made a simpler NSDate category that doesn't have warnings about the string formatting of the singulars, and also tidys up the week ago singular:
#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
In Swift
Usage:
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"
}
}
}
Use the NSDate class:
timeIntervalSinceDate
returns the interval in seconds.
Quick exercise to implement this in objective-c:
Get time "now" NSDate
Get the NSDate you wish to compare with
Get the interval in seconds using timeIntervalSinceDate
Then implement this pseudo code:
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
and so on...
then you need to decide whether a month is 30,31 or 28 days. Keep it simple - pick 30.
There might be a better way, but its 2am and this is the first thing that came to mind...
My solution:
- (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;
}
Not sure why this isnt in cocoa-touch, i nice standard way of doing this would be great.
Set up some types to keep the data in, it will make it easier if you ever ned to localise it a bit more. (obviously expand if you need more time periods)
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;
}
NOTE: I"m using an hour based calculation , as that is what my data is in. NSTimeInterval is second based. I also had to convert between the two.
I saw that there were several time ago functions in snippets of code on Stack Overflow and I wanted one that really gave the clearest sense of the time (since some action occurred). To me this means "time ago" style for short time intervals (5 min ago, 2 hours ago) and specific dates for longer time periods (April 15, 2011 instead of 2 years ago). Basically I thought Facebook did a really good job at this and I wanted to just go by their example (as I'm sure they out a lot of thought into this and it is very easy and clear to understand from the consumer perspective).
After a long time of googling I was pretty surprised to see that no one had implemented this as far as I could tell. Decided that I wanted it bad enough to spend the time writing and thought that I would share.
Hope you enjoy :)
Get the code here: https://github.com/nikilster/NSDate-Time-Ago