iOS - Friendly NSDate format - ios

I need to display the date of posts in my app to the user, right now I do it in this format: "Fri, 25 May". How would I format an NSDate to read something like "2 hours ago"? To make it more user friendly.

Take a look at FormaterKit https://github.com/mattt/FormatterKit
Created by mattt who also created AFNetworking.

NSDateFormatter can't do things like that; you're going to need to establish your own rules. I guess something like:
- (NSString *)formattedDate:(NSDate *)date
{
NSTimeInterval timeSinceDate = [[NSDate date] timeIntervalSinceDate:date];
// print up to 24 hours as a relative offset
if(timeSinceDate < 24.0 * 60.0 * 60.0)
{
NSUInteger hoursSinceDate = (NSUInteger)(timeSinceDate / (60.0 * 60.0));
switch(hoursSinceDate)
{
default: return [NSString stringWithFormat:#"%d hours ago", hoursSinceDate];
case 1: return #"1 hour ago";
case 0:
NSUInteger minutesSinceDate = (NSUInteger)(timeSinceDate / 60.0);
/* etc, etc */
break;
}
}
else
{
/* normal NSDateFormatter stuff here */
}
}
So that's to print 'x minutes ago' or 'x hours ago' up to 24 hours from the date, which will usually be one day.

I wanted a date format like Facebook does for their mobile apps so I whipped up this NSDate category - hope it is useful for someone (this kind of stuff should really be in a standard library!) :)
https://github.com/nikilster/NSDate-Time-Ago

There's also SEHumanizedTimeDiff which does/is about to support multiple languages if that's an issue for you:
https://github.com/sarperdag/SEHumanizedTimeDiff

There are about a million ways you could do this, but here's a quick one:
NSString* hoursAgo = [NSString stringWithFormat:#"%.0lf hours ago", fabs([date timeIntervalSinceNow] / 3600.0)]
Of course, this doesn't check that date is actually from the past, doesn't do anything but hours, etc. But, you probably get the idea.
timeIntervalSinceNow returns how many seconds have passed since a given date, with positive numbers being a date in the future and negative numbers being a date in the past. So, we get how many seconds have passed, divide it by 3600 seconds in an hour to compute the hours that have passed, and then put its absolute value into the string "n hours ago".

Here is a pretty good answer this will take in seconds since the epoch(Jan 1, 1970) and return you a nice formatted string like '3 minutes ago'. Simply call it with your date object like so:
[timeAgoFromUnixTime:[myDateObject timeIntervalSince1970]];
+ (NSString *)timeAgoFromUnixTime:(double)seconds
{
double difference = [[NSDate date] timeIntervalSince1970] - seconds;
NSMutableArray *periods = [NSMutableArray arrayWithObjects:#"second", #"minute", #"hour", #"day", #"week", #"month", #"year", #"decade", nil];
NSArray *lengths = [NSArray arrayWithObjects:#60, #60, #24, #7, #4.35, #12, #10, nil];
int j = 0;
for(j=0; difference >= [[lengths objectAtIndex:j] doubleValue]; j++)
{
difference /= [[lengths objectAtIndex:j] doubleValue];
}
difference = roundl(difference);
if(difference != 1)
{
[periods insertObject:[[periods objectAtIndex:j] stringByAppendingString:#"s"] atIndex:j];
}
return [NSString stringWithFormat:#"%li %#%#", (long)difference, [periods objectAtIndex:j], #" ago"];
}

In newer versions of iOS since this question was asked, NSDateFormatter has had this ability added. It can now do it using the doesRelativeDateFormatting property.

+(NSString*)HourCalculation:(NSString*)PostDate
{
NSLog(#"postdate=%#",PostDate);
// PostDate=#"2014-04-02 01:31:04";
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
[dateFormat setTimeZone:gmt];
NSDate *ExpDate = [dateFormat dateFromString:PostDate];
NSLog(#"expdate=%#",ExpDate);
NSLog(#"expdate=%#",[NSDate date ]);
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSDayCalendarUnit|NSWeekCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit|NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:ExpDate toDate:[NSDate date] options:0];
// NSLog(#"year=%d",components.year);
//
// NSLog(#"month=%d",components.month);
//
// NSLog(#"week=%d",components.week);
//
// NSLog(#"day=%d",components.day);
//
// NSLog(#"hour=%d",components.hour);
//
// NSLog(#"min=%d",components.minute);
//
// NSLog(#"sce=%d",components.second);
//
NSString *time;
if(components.year!=0)
{
if(components.year==1)
{
time=[NSString stringWithFormat:#"%ld year",(long)components.year];
}
else{
time=[NSString stringWithFormat:#"%ld years",(long)components.year];
}
}
else if(components.month!=0)
{
if(components.month==1)
{
time=[NSString stringWithFormat:#"%ld month",(long)components.month];
}
else{
time=[NSString stringWithFormat:#"%ld months",(long)components.month];
}
// NSLog(#"%#",time);
}
else if(components.week!=0)
{
if(components.week==1)
{
time=[NSString stringWithFormat:#"%ld week",(long)components.week];
}
else{
time=[NSString stringWithFormat:#"%ld weeks",(long)components.week];
}
// NSLog(#"%#",time);
}
else if(components.day!=0)
{
if(components.day==1)
{
time=[NSString stringWithFormat:#"%ld day",(long)components.day];
}
else{
time=[NSString stringWithFormat:#"%ld days",(long)components.day];
}
}
else if(components.hour!=0)
{
if(components.hour==1)
{
time=[NSString stringWithFormat:#"%ld hour",(long)components.hour];
}
else{
time=[NSString stringWithFormat:#"%ld hours",(long)components.hour];
}
}
else if(components.minute!=0)
{
if(components.minute==1)
{
time=[NSString stringWithFormat:#"%ld min",(long)components.minute];
}
else{
time=[NSString stringWithFormat:#"%ld mins",(long)components.minute];
}
// NSLog(#"time=%#",time);
}
else if(components.second>=0){
// NSLog(#"postdate=%#",PostDate);
// NSLog(#"expdate=%#",[NSDate date ]);
if(components.second==0)
{
time=[NSString stringWithFormat:#"1 sec"];
}
else{
time=[NSString stringWithFormat:#"%ld secs",(long)components.second];
}
}
return [NSString stringWithFormat:#"%# ago",time];
}
This code will show you time in
------------sec like 2 sec ago
------------min like 2 mins ago
------------hours like 2 hours ago
------------days like 2 days ago
------------week like 2 weeks ago
------------month like 2 months ago
Lastly....
years like 2 years ago
:) try this

Adding to the solution try this more simplified method
NSDateComponents *today = [[NSCalendar currentCalendar] components:NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond fromDate:passed toDate:[NSDate date] options:0];
NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:date];
if (interval < 60) timestampString = [NSString stringWithFormat:#"%d seconds ago" ,today.second];
else if (interval < 60 * 60) timestampString = [NSString stringWithFormat:#"%d minutes ago" ,today.minute];
else if (interval < 60 * 60 * 24) timestampString = [NSString stringWithFormat:#"%d hours ago" ,today.hour];
else timestampString = [NSString stringWithFormat:#"%d days ago" ,today.day];

Related

How to compare time greater than 3 sec in ios

How to compare time is greater than 3 sec in ios.
Time is like "2016-05-10 05:31:14". I got code for comparing like greater or less with specific time but how i will achieve it is greater than 3 sec etc.
You can get seconds difference between two dates by
NSDate *someDate;//Some Date
NSLog(#"Seconds --> %f",[[NSDate date] timeIntervalSinceDate: someDate]);
Please try this one. May be Its possible for you:
NSDate* timeNow = [NSDate date];
// If less than 30 seconds, do something
if ([timeNow timeIntervalSinceDate:anEarlierTime] < 30.0f)
{
// Do something
}
- (BOOL)isEndDateIsSmallerThanCurrent:(NSDate *)checkEndDate
{
NSDate* enddate = checkEndDate;
NSDate* currentdate = [NSDate date];
NSTimeInterval distanceBetweenDates = [enddate timeIntervalSinceDate:currentdate];
double secondsInMinute = 60;
// secondsBetweenDates is your seconds difference
NSInteger secondsBetweenDates = distanceBetweenDates / secondsInMinute;
if (secondsBetweenDates == 0)
return YES;
else if (secondsBetweenDates < 0)
return YES;
else
return NO;
}
Here, secondsBetweenDates is your difference in seconds. You can check that it is smaller or greater or equal than 3.
You can get difference here in hours also!
Hope this will help :)

Algorithm: Resolution Time

I have an enquiry that needs to be resolved in fixed hour (Critical in 4hr, Important in 10hr and Normal in 24 hr). The enquiry res hours can be increased if there's a non working hour in between or a holiday. Non working can be full day or some hour in a specific day.
For eg: A critical enquiry raised at 08:02am on Monday should get resolved by 01:00pm
//Working hours of week
Mon 09:00am - 01:00pm
Tue Holiday
Wed 09:00am - 05:00pm(non working between 01:00pm - 02:00pm)
Thu 11:00am - 03:00pm(non working between 01:00pm - 02:00pm)
Fri 09:00am - 05:00pm(non working between 01:00pm - 02:00pm)
The inputs are enquiry type and enquiry log time. Output required is the resolution time.
My Approach:
Add all res hours to an enquiry as if all hrs are working. So, if log time is 08:02am for a critical enq add 4hrs i.e. 12:02pm as res time.
Enter while(true) loop where I check if next hr is a work time. If yes skip that hr else add that to res time and continue.
But this approach does not give me correct results when log time is non working.
NSDate *enqExpiration = nil;
int hoursNeededToCoverEnq = 0;
switch (enqType) {
case normalPriority:
hoursNeededToCoverEnq = lowResHours;
enqExpiration = [NSDate dateWithTimeInterval:lowResHours*hour sinceDate:capturedTime];
break;
case importantPriority:
hoursNeededToCoverEnq = mediumResHours;
enqExpiration = [NSDate dateWithTimeInterval:mediumResHours*hour sinceDate:capturedTime];
break;
case criticalPriority:
hoursNeededToCoverEnq = highResHours;
enqExpiration = [NSDate dateWithTimeInterval:highResHours*hour sinceDate:capturedTime];
break;
}
int aggregatedHrs = 0;
while(true){
capturedTime = [NSDate dateWithTimeInterval:1*hour sinceDate:capturedTime];
NSDateFormatter *dayFormat = [[NSDateFormatter alloc] init];
[dayFormat setTimeZone: [NSTimeZone timeZoneWithAbbreviation:#"GMT"]];
[dayFormat setDateFormat:#"EEEE"];
NSString* capturedDay = [dayFormat stringFromDate:capturedTime];
[dayFormat setDateFormat:#"dd/MM/yyyy"];
NSString* todayDate = [dayFormat stringFromDate:capturedTime];
if([weekends containsObject:capturedDay]){
enqExpiration = [NSDate dateWithTimeInterval:1*hour sinceDate:enqExpiration];
continue;
}else if([holidays containsObject:todayDate]){
enqExpiration = [NSDate dateWithTimeInterval:1*hour sinceDate:enqExpiration];
continue;
}else{
NSString *openTime, *closeTime;
for(int i=0; i<weekdaysTime.count; i++){
if([[((NSMutableDictionary*)[weekdaysTime objectAtIndex:i]) valueForKey:#"workingDay"] isEqualToString:capturedDay]){
openTime = [((NSMutableDictionary*)[weekdaysTime objectAtIndex:i]) valueForKey:#"openingTime"];
closeTime = [((NSMutableDictionary*)[weekdaysTime objectAtIndex:i]) valueForKey:#"closingTime"];
break;
}
}
NSDate *openTimeToday = nil, *closeTimeToday = nil;
[dayFormat setDateFormat:#"dd/MM/yyyy hh:mmaa"];
openTimeToday = [dayFormat dateFromString:[[todayDate stringByAppendingString:#" "] stringByAppendingString:openTime]];
closeTimeToday = [dayFormat dateFromString:[[todayDate stringByAppendingString:#" "] stringByAppendingString:closeTime]];
if([capturedTime compare:openTimeToday]==NSOrderedAscending || [capturedTime compare:closeTimeToday]==NSOrderedDescending ){
enqExpiration = [NSDate dateWithTimeInterval:1*hour sinceDate:enqExpiration];
continue;
}
}
aggregatedHrs ++;
if( aggregatedHrs == hoursNeededToCoverEnq )
break;
}
return enqExpiration;

ios8 - seeing if a record is past or future

I'm parsing an array and want to weed out records from before now.
I've got this code:
int i = 0;
for (i=0; i < tempArray.count; i++) {
currentObjectArray = tempArray[i];
NSString *dateString = [currentObjectArray valueForKey:#"ScheduleTime" ];
NSDate *schedule = [dateFormatter dateFromString:dateString];
NSLog(#"schedule: %lu", (unsigned long) schedule );
NSLog(#"now: %lu", (unsigned long)[NSDate date] );
NSTimeInterval distanceBetweenDates = [schedule timeIntervalSinceDate: schedule];
NSLog(#"distanceBetweenDates: %lu", (unsigned long)distanceBetweenDates );
result:
schedule: 16436914033316069376
now: 6174145184
distanceBetweenDates: 0
but the two resulting numbers are incorrect, thus the result is incorrect. Could someone please tell me what I'm doing wrong? Thanks
UPDATE: Thanks to answers below, I've updated my code as follows:
NSString *dateString = [currentObjectArray valueForKey:#"ScheduleTime" ];
NSDate *schedule = [dateFormatter dateFromString:dateString];
float s = [schedule timeIntervalSince1970];
NSLog(#" %f", s );
NSTimeInterval timeInterval = [currentObjectArray timeIntervalSinceNow];
if (timeInterval > 0) {
NSLog(#"YES");
} else {
NSLog(#"NO");
The schedule date format is: "YYYY-MM-DD'T'HH:mm:ss"
Update2: I forgot to add in the local time zone. Thanks for all the help.
These two lines don't do what you think they do.
NSLog(#"schedule: %lu", (unsigned long) schedule );
NSLog(#"now: %lu", (unsigned long)[NSDate date] );
Performing this type cast is asking the system to return you an unsigned long representation of the pointer to the object, which is a memory address and not at all related to time. It is likely that you actually wanted to ask for the NSTimeInterval values.
NSLog(#"schedule: %f", [schedule timeIntervalSince1970] );
NSLog(#"now: %f", [[NSDate date] timeIntervalSince1970] );
Compounding your confusion, you have also misunderstood this line:
NSTimeInterval distanceBetweenDates = [schedule timeIntervalSinceDate: schedule];
You are asking the system to tell you how many seconds are between schedule and schedule; which is obviously always going to be 0 since they are identical. Instead, you probably meant one of:
NSTimeInterval distanceBetweenDates1 = [[NSDate date] timeIntervalSinceDate:schedule];
NSTimeInterval distanceBetweenDates2 = [schedule timeIntervalSinceDate:[NSDate date]];
You only need to check if the time interval is negative or positive to determine if a time comes before or after, respectively.
- (BOOL)isDateInPast:(NSDate *)date {
NSTimeInterval timeInterval = [date timeIntervalSinceNow];
if (timeInterval < 0) {
return YES;
} else {
return NO;
}
}
Note that this doesn't check the condition where the time interval is 0 (the present).
EDIT: Adding to this for further clarification. Your loop code could look something like this...
NSMutableArray *datesOnlyInFuture = [NSMutableArray array];
for (NSDate *date in dateArray) {
if (![self isDateInPast:date]) {
[datesOnlyInFuture addObject:date];
}
}
NSLog(#"Future only dates: %#", datesOnlyInFuture);
This will actually create a new array for you. Clearly plenty of optimizations should be made. For example timeIntervalSinceNow is going to be different each time it is called, so you could pass in a constant date that is set before the loop starts so you're always checking against the same date/time.

Is it possible align text to both the left and right in a section heading in a tableview?

I am trying to have some text aligned to the left of the section heading and different text to the right of the same heading.
While using toolBars I have used a flexible button to push both other buttons to the left and right and I was wondering if there was a similar thing for section headings?
A little bit more background is that I have a different section in my tableview for every piece of information entered on each different day. On the left of the section heading I want the date it was entered (Wed 20th Nov) and on the right I want how many days ago that was (20 days ago).
Here is the code I am currently using:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (section == 0) {
return nil;
}
else {
// This gets the day the data was entered
NSDate * date = [_sectionDates objectAtIndex:(section-1)];
NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"EEE"];
NSString * dateDay = [formatter stringFromDate:date];
[formatter setDateFormat:#"MMM"];
NSString * dateMonth = [formatter stringFromDate:date];
NSDateComponents * components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];
NSInteger dateOfDay = [components day];
NSTimeInterval diff = [[NSDate date] timeIntervalSinceDate:date];
NSInteger days = diff / (60.0 * 60.0 * 24);
NSString * dateScript;
if (dateOfDay < 21 && dateOfDay > 4) {
dateScript = #"th";
}
else if (dateOfDay % 10 == 1) {
dateScript = #"st";
}
else if (dateOfDay % 10 == 2) {
dateScript = #"nd";
}
else if (dateOfDay % 10 == 3) {
dateScript = #"rd";
}
else {
dateScript = #"th";
}
NSString * header;
if (days < 2) {
header = [NSString stringWithFormat:#"%# %i%# %# - %#", dateDay, dateOfDay, dateScript, dateMonth, days == 0 ? bToday : bYesterday];
}
else {
header = [NSString stringWithFormat:#"%# %i%# %# - %i %#", dateDay, dateOfDay, dateScript, dateMonth, days, bDaysAgo];
}
return header;
}
}
At the moment this outputs it like this:
|Wed 4th Dec - Today________|
I want it to be like this:
|Wed 4th Dec_________Today|
You can't do it with titleForHeaderInSection. You would need to implement viewForHeaderInSection instead. Return a view with two labels - one to the left and one to the right. Of course you view would also need to replicate the default look of a standard table section header if that's what you want.

Get NSDate to say "XX time ago" that article was published [duplicate]

This question already has answers here:
Is there some functionality in Cocoa to display time intervals in natural language?
(4 answers)
Closed 9 years ago.
I have a date an article was published, but need to get how long ago it was published in relation to the current time.
So if the Article was published at 8:45AM, and it is 9:45AM on the same day, I need to be able to have a UILabel that says "1 hr ago".
Currently, I am getting the date formatted to get a date like "May 5, 2013 5:35PM":
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Feed *feedLocal = [headlinesArray objectAtIndex:indexPath.row];
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"MMMM d, yyyy h:mma"];
NSString *dateString = [df stringFromDate:feedLocal.published];
cell.publishedLabel.text = dateString;
}
How could I convert that to get something like "1 hr ago"? Thanks!
EDIT
Here is the current method I have to at least get the time ago:
-(NSString *)timeAgo {
NSDate *todayDate = [NSDate date];
double ti = [self timeIntervalSinceDate:todayDate];
ti = ti * -1;
if (ti < 1) {
return #"1s";
} else if (ti < 60) {
return #"1m";
} else if (ti < 3600) {
int diff = round(ti / 60);
return [NSString stringWithFormat:#"%dm", diff];
} else if (ti < 86400) {
int diff = round(ti / 60 / 60);
return[NSString stringWithFormat:#"%dh", diff];
} else if (ti < 2629743) {
int diff = round(ti / 60 / 60 / 24);
return[NSString stringWithFormat:#"%dd", diff];
} else if (ti < 31556926) {
int diff = round(ti / 60 / 60 / 24 / 30);
return [NSString stringWithFormat:#"%dmo", diff];
} else {
int diff = round(ti / 60 / 60 / 24 / 30 / 12);
return [NSString stringWithFormat:#"%dy", diff];
}
}
Im not sure what timeAgo is a method of, but here is a solution assuming its in the same viewController as tableView:cellForRowAtIndexPath. If you can clarify what its a method of I may be able to modify this and help you more.
First change timeAgo to take in a date and do a comparison on it.
-(NSString *)timeSincePublished:(NSDate *)publicationDate
{
double ti = [publicationDate timeIntervalSinceNow];
Everything else should be the same in the above method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Feed *feedLocal = [headlinesArray objectAtIndex:indexPath.row];
NSString *dateString = [self timeSincePublished:feedLocal.published];
cell.publishedLabel.text = dateString;
}

Resources