I want to convert nsdate in to relative format like \"Today\",\"Yesterday\",\"a week ago\",\"a month ago\",\"a year ago\",\"date as it is\"
.
I have writ
check NSDate-TimeAgo, it also supports multiple languages.
Here is code I created for my use:
+ (NSString*) getTimestampForDate:(NSDate*)date {
NSDate* sourceDate = date;
// Timezone Offset compensation (optional, if your target users are limited to a single time zone.)
NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithName:@"America/New_York"];
NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];
NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:sourceDate];
NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate];
NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset;
NSDate* destinationDate = [[NSDate alloc] initWithTimeInterval:interval sinceDate:sourceDate];
// Timestamp calculation (based on compensation)
NSCalendar* currentCalendar = [NSCalendar currentCalendar];
NSCalendarUnit unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit;
NSDateComponents *differenceComponents = [currentCalendar components:unitFlags fromDate:destinationDate toDate:[NSDate date] options:0];//Use `date` instead of `destinationDate` if you are not using Timezone offset correction
NSInteger yearDifference = [differenceComponents year];
NSInteger monthDifference = [differenceComponents month];
NSInteger dayDifference = [differenceComponents day];
NSInteger hourDifference = [differenceComponents hour];
NSInteger minuteDifference = [differenceComponents minute];
NSString* timestamp;
if (yearDifference == 0
&& monthDifference == 0
&& dayDifference == 0
&& hourDifference == 0
&& minuteDifference <= 2) {
//"Just Now"
timestamp = @"Just Now";
} else if (yearDifference == 0
&& monthDifference == 0
&& dayDifference == 0
&& hourDifference == 0
&& minuteDifference < 60) {
//"13 minutes ago"
timestamp = [NSString stringWithFormat:@"%ld minutes ago", (long)minuteDifference];
} else if (yearDifference == 0
&& monthDifference == 0
&& dayDifference == 0
&& hourDifference == 1) {
//"1 hour ago" EXACT
timestamp = @"1 hour ago";
} else if (yearDifference == 0
&& monthDifference == 0
&& dayDifference == 0
&& hourDifference < 24) {
timestamp = [NSString stringWithFormat:@"%ld hours ago", (long)hourDifference];
} else {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
NSString* strDate, *strDate2 = @"";
if (yearDifference == 0
&& monthDifference == 0
&& dayDifference == 1) {
//"Yesterday at 10:23 AM", "Yesterday at 5:08 PM"
[formatter setDateFormat:@"hh:mm a"];
strDate = [formatter stringFromDate:date];
timestamp = [NSString stringWithFormat:@"Yesterday at %@", strDate];
} else if (yearDifference == 0
&& monthDifference == 0
&& dayDifference < 7) {
//"Tuesday at 7:13 PM"
[formatter setDateFormat:@"EEEE"];
strDate = [formatter stringFromDate:date];
[formatter setDateFormat:@"hh:mm a"];
strDate2 = [formatter stringFromDate:date];
timestamp = [NSString stringWithFormat:@"%@ at %@", strDate, strDate2];
} else if (yearDifference == 0) {
//"July 4 at 7:36 AM"
[formatter setDateFormat:@"MMMM d"];
strDate = [formatter stringFromDate:date];
[formatter setDateFormat:@"hh:mm a"];
strDate2 = [formatter stringFromDate:date];
timestamp = [NSString stringWithFormat:@"%@ at %@", strDate, strDate2];
} else {
//"March 24 2010 at 4:50 AM"
[formatter setDateFormat:@"d MMMM yyyy"];
strDate = [formatter stringFromDate:date];
[formatter setDateFormat:@"hh:mm a"];
strDate2 = [formatter stringFromDate:date];
timestamp = [NSString stringWithFormat:@"%@ at %@", strDate, strDate2];
}
}
return timestamp;
}
Complate Code If Futures Dates
NSCalendarUnit units = NSCalendarUnitDay | NSCalendarUnitWeekOfYear |
NSCalendarUnitMonth | NSCalendarUnitYear;
NSDateComponents *components = [[NSCalendar currentCalendar] components:units fromDate:date toDate:[NSDate date] options:0];
if (components.year < 0) {
return [NSString stringWithFormat:@"%ld years from now", labs((long)components.year)];
} else if (components.month < 0) {
return [NSString stringWithFormat:@"%ld months from now", labs((long)components.month)];
} else if (components.weekOfYear < 0) {
return [NSString stringWithFormat:@"%ld weeks from now", labs((long)components.weekOfYear)];
} else if (components.day < 0) {
if (components.day < 1) {
return [NSString stringWithFormat:@"%ld days from now", labs((long)components.day)];
} else {
return @"Tomorrow";
}
}
else if (components.year > 0) {
return [NSString stringWithFormat:@"%ld years ago", (long)components.year];
} else if (components.month > 0) {
return [NSString stringWithFormat:@"%ld months ago", (long)components.month];
} else if (components.weekOfYear > 0) {
return [NSString stringWithFormat:@"%ld weeks ago", (long)components.weekOfYear];
} else if (components.day > 0) {
if (components.day > 1) {
return [NSString stringWithFormat:@"%ld days ago", (long)components.day];
} else {
return @"Yesterday";
}
} else {
return @"Today";
}
To format the given "sourceDate" as "5:56 pm" for today, "yesterday" for any time yesterday, "January 16" for any day in the same year and "January 16, 2014". I am posting my own method.
sourceDate = //some date that you need to take into consideration
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitMinute | NSCalendarUnitHour | NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
NSDateComponents *sourceDateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitMinute | NSCalendarUnitHour | NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate: sourceDate];
NSString* timestamp;
NSDateFormatter *formatSourceDate = [NSDateFormatter new];
[formatSourceDate setAMSymbol:@"AM"];
[formatSourceDate setPMSymbol:@"PM"];
//same day - time in h:mm am/pm
if (components.day == sourceDateComponents.day) {
NSLogInfo(@"time");
[formatSourceDate setDateFormat:@"h:mm a"];
timestamp = [NSString stringWithFormat:@"%@",[formatSourceDate stringFromDate:date]];
return timestamp;
}
else if (components.day - sourceDateComponents.day == 1) {
//yesterday
timestamp = NSLocalizedString(@"Yesterday", nil);
return timestamp;
}
if (components.year == sourceDateComponents.year) {
//september 29, 5:56 pm
[formatSourceDate setDateFormat:@"MMMM d"];
timestamp = [NSString stringWithFormat:@"%@",[formatSourceDate stringFromDate:date]];
return timestamp;
}
[formatSourceDate setDateFormat:@"MMMM d year"];
timestamp = [NSString stringWithFormat:@"%@",[formatSourceDate stringFromDate:date]];
return timestamp;
NSLogInfo(@"Timestamp : %@",timestamp);
Please note that as of iOS 13 there is now RelativeDateTimeFormatter which does it all most of it for you! WWDC 2019 video here.
let formatter = RelativeDateTimeFormatter()
let dateString = formatter.localizedString(for: aDate, relativeTo: now)
// en_US: "2 weeks ago"
// es_ES: "hace 2 semanas"
// zh_TW: "2 週前"
I've left my previous answer below for posterity. Cheers!
⚠️ You will want to read through the previous answer for some key tips to avoid certain bugs. Hint: use the end of the current day's date/time for the relative date when comparing dates that are not today!
Here's my answer (in Swift 3!) and why it's better.
func datePhraseRelativeToToday(from date: Date) -> String {
// Don't use the current date/time. Use the end of the current day
// (technically 0h00 the next day). Apple's calculation of
// doesRelativeDateFormatting niavely depends on this start date.
guard let todayEnd = dateEndOfToday() else {
return ""
}
let calendar = Calendar.autoupdatingCurrent
let units = Set([Calendar.Component.year,
Calendar.Component.month,
Calendar.Component.weekOfMonth,
Calendar.Component.day])
let difference = calendar.dateComponents(units, from: date, to: todayEnd)
guard let year = difference.year,
let month = difference.month,
let week = difference.weekOfMonth,
let day = difference.day else {
return ""
}
let timeAgo = NSLocalizedString("%@ ago", comment: "x days ago")
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale.autoupdatingCurrent
formatter.dateStyle = .medium
formatter.doesRelativeDateFormatting = true
return formatter
}()
if year > 0 {
// sample output: "Jan 23, 2014"
return dateFormatter.string(from: date)
} else if month > 0 {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .brief // sample output: "1mth"
formatter.allowedUnits = .month
guard let timePhrase = formatter.string(from: difference) else {
return ""
}
return String(format: timeAgo, timePhrase)
} else if week > 0 {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .brief; // sample output: "2wks"
formatter.allowedUnits = .weekOfMonth
guard let timePhrase = formatter.string(from: difference) else {
return ""
}
return String(format: timeAgo, timePhrase)
} else if day > 1 {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .abbreviated; // sample output: "3d"
formatter.allowedUnits = .day
guard let timePhrase = formatter.string(from: difference) else {
return ""
}
return String(format: timeAgo, timePhrase)
} else {
// sample output: "Yesterday" or "Today"
return dateFormatter.string(from: date)
}
}
func dateEndOfToday() -> Date? {
let calendar = Calendar.autoupdatingCurrent
let now = Date()
let todayStart = calendar.startOfDay(for: now)
var components = DateComponents()
components.day = 1
let todayEnd = calendar.date(byAdding: components, to: todayStart)
return todayEnd
}
Remember to reuse your formatters to avoid any performance hit! Hint: extensions on DateFormatter and DateComponentsFormatter are good ideas.
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
let now = NSDate()
let dateMakerFormatter = DateFormatter()
dateMakerFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss z"
let dateString = "2017-03-13 10:38:54 +0000"
let stPatricksDay = dateMakerFormatter.date(from: dateString)!
let calendar = NSCalendar.current
let components = calendar.dateComponents([.hour, .minute,.weekOfMonth,.day,.year,.month,.second], from: stPatricksDay, to: now as Date)
if components.year! > 0 {
formatter.allowedUnits = .year
} else if components.month! > 0 {
formatter.allowedUnits = .month
} else if components.weekOfMonth! > 0 {
formatter.allowedUnits = .weekOfMonth
} else if components.day! > 0 {
formatter.allowedUnits = .day
} else if components.hour! > 0 {
formatter.allowedUnits = .hour
} else if components.minute! > 0 {
formatter.allowedUnits = .minute
} else {
formatter.allowedUnits = .second
}
let formatString = NSLocalizedString("%@ ago", comment: "Used to say how much time has passed. e.g. '2 hours ago'")
let timeString = formatter.string(from: components)
String(format: formatString, timeString!)