Swift convert time to time ago
Solution 1
I'll just update the Truongky's answer for Swif 3:
extension Date {
func getElapsedInterval() -> String {
let interval = Calendar.current.dateComponents([.year, .month, .day], from: self, to: Date())
if let year = interval.year, year > 0 {
return year == 1 ? "\(year)" + " " + "year ago" :
"\(year)" + " " + "years ago"
} else if let month = interval.month, month > 0 {
return month == 1 ? "\(month)" + " " + "month ago" :
"\(month)" + " " + "months ago"
} else if let day = interval.day, day > 0 {
return day == 1 ? "\(day)" + " " + "day ago" :
"\(day)" + " " + "days ago"
} else {
return "a moment ago"
}
}
}
If you prefer a localizable response instead of only english this code will do the work
extension Date {
func getElapsedInterval() -> String {
var calendar = Calendar.current
calendar.locale = Locale(identifier: Bundle.main.preferredLocalizations[0])
// IF THE USER HAVE THE PHONE IN SPANISH BUT YOUR APP ONLY SUPPORTS I.E. ENGLISH AND GERMAN
// WE SHOULD CHANGE THE LOCALE OF THE FORMATTER TO THE PREFERRED ONE
// (IS THE LOCALE THAT THE USER IS SEEING THE APP), IF NOT, THIS ELAPSED TIME
// IS GOING TO APPEAR IN SPANISH
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
formatter.maximumUnitCount = 1
formatter.calendar = calendar
var dateString: String?
let interval = calendar.dateComponents([.year, .month, .weekOfYear, .day], from: self, to: Date())
if let year = interval.year, year > 0 {
formatter.allowedUnits = [.year] //2 years
} else if let month = interval.month, month > 0 {
formatter.allowedUnits = [.month] //1 month
} else if let week = interval.weekOfYear, week > 0 {
formatter.allowedUnits = [.weekOfMonth] //3 weeks
} else if let day = interval.day, day > 0 {
formatter.allowedUnits = [.day] // 6 days
} else {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: Bundle.main.preferredLocalizations[0]) //--> IF THE USER HAVE THE PHONE IN SPANISH BUT YOUR APP ONLY SUPPORTS I.E. ENGLISH AND GERMAN WE SHOULD CHANGE THE LOCALE OF THE FORMATTER TO THE PREFERRED ONE (IS THE LOCALE THAT THE USER IS SEEING THE APP), IF NOT, THIS ELAPSED TIME IS GOING TO APPEAR IN SPANISH
dateFormatter.dateStyle = .medium
dateFormatter.doesRelativeDateFormatting = true
dateString = dateFormatter.string(from: self) // IS GOING TO SHOW 'TODAY'
}
if dateString == nil {
dateString = formatter.string(from: self, to: Date())
}
return dateString!
}
Solution 2
Swift 4+ Version
extension Date {
func timeAgoSinceDate() -> String {
// From Time
let fromDate = self
// To Time
let toDate = Date()
// Estimation
// Year
if let interval = Calendar.current.dateComponents([.year], from: fromDate, to: toDate).year, interval > 0 {
return interval == 1 ? "\(interval)" + " " + "year ago" : "\(interval)" + " " + "years ago"
}
// Month
if let interval = Calendar.current.dateComponents([.month], from: fromDate, to: toDate).month, interval > 0 {
return interval == 1 ? "\(interval)" + " " + "month ago" : "\(interval)" + " " + "months ago"
}
// Day
if let interval = Calendar.current.dateComponents([.day], from: fromDate, to: toDate).day, interval > 0 {
return interval == 1 ? "\(interval)" + " " + "day ago" : "\(interval)" + " " + "days ago"
}
// Hours
if let interval = Calendar.current.dateComponents([.hour], from: fromDate, to: toDate).hour, interval > 0 {
return interval == 1 ? "\(interval)" + " " + "hour ago" : "\(interval)" + " " + "hours ago"
}
// Minute
if let interval = Calendar.current.dateComponents([.minute], from: fromDate, to: toDate).minute, interval > 0 {
return interval == 1 ? "\(interval)" + " " + "minute ago" : "\(interval)" + " " + "minutes ago"
}
return "a moment ago"
}
}
Usage
yourDate.timeAgoSinceDate()
Solution 3
Change your text as you want.
extension NSDate {
func getElapsedInterval() -> String {
var interval = NSCalendar.currentCalendar().components(.Year, fromDate: self, toDate: NSDate(), options: []).year
if interval > 0 {
return interval == 1 ? "\(interval)" + " " + "year" :
"\(interval)" + " " + "years"
}
interval = NSCalendar.currentCalendar().components(.Month, fromDate: self, toDate: NSDate(), options: []).month
if interval > 0 {
return interval == 1 ? "\(interval)" + " " + "month" :
"\(interval)" + " " + "months"
}
interval = NSCalendar.currentCalendar().components(.Day, fromDate: self, toDate: NSDate(), options: []).day
if interval > 0 {
return interval == 1 ? "\(interval)" + " " + "day" :
"\(interval)" + " " + "days"
}
interval = NSCalendar.currentCalendar().components(.Hour, fromDate: self, toDate: NSDate(), options: []).hour
if interval > 0 {
return interval == 1 ? "\(interval)" + " " + "hour" :
"\(interval)" + " " + "hours"
}
interval = NSCalendar.currentCalendar().components(.Minute, fromDate: self, toDate: NSDate(), options: []).minute
if interval > 0 {
return interval == 1 ? "\(interval)" + " " + "minute" :
"\(interval)" + " " + "minutes"
}
return "a moment ago"
}
}
Solution 4
On Swift 5 use the RelativeDateTimeFormatter,
let formatter = RelativeDateTimeFormatter()
formatter.localizedString(from: DateComponents(day: -1)) // "1 day ago"
formatter.localizedString(from: DateComponents(hour: 2)) // "in 2 hours"
formatter.localizedString(from: DateComponents(minute: 45)) // "in 45 minutes"
set the dateTimeStyle get localized deictic statements e.g. -
formatter.dateTimeStyle = .named
formatter.localizedString(from: DateComponents(day: -1)) // "yesterday"
Solution 5
Swift 3 version of truongky's code:
extension Date {
var timeAgoSinceNow: String {
return getTimeAgoSinceNow()
}
private func getTimeAgoSinceNow() -> String {
var interval = Calendar.current.dateComponents([.year], from: self, to: Date()).year!
if interval > 0 {
return interval == 1 ? "\(interval)" + " year" : "\(interval)" + " years"
}
interval = Calendar.current.dateComponents([.month], from: self, to: Date()).month!
if interval > 0 {
return interval == 1 ? "\(interval)" + " month" : "\(interval)" + " months"
}
interval = Calendar.current.dateComponents([.day], from: self, to: Date()).day!
if interval > 0 {
return interval == 1 ? "\(interval)" + " day" : "\(interval)" + " days"
}
interval = Calendar.current.dateComponents([.hour], from: self, to: Date()).hour!
if interval > 0 {
return interval == 1 ? "\(interval)" + " hour" : "\(interval)" + " hours"
}
interval = Calendar.current.dateComponents([.minute], from: self, to: Date()).minute!
if interval > 0 {
return interval == 1 ? "\(interval)" + " minute" : "\(interval)" + " minutes"
}
return "a moment ago"
}
}
Comments
-
Salah over 2 years
In Swift5, we have
RelativeDateTimeFormatter
Prior to Swift5:
I'm trying to convert time to time ago, what i wanna do is:
from 1 to 15 seconds it will say " Just now "
from 60 minutes to 119 minutes it will say " an hour ago "
from 24 hours to 47 hours it will say " Yesterday "
from 7 days to 7 days and 23 hours it will say " a week ago "
I'm not sure about my counting if am i wrong feel free to fix it for me
Here is the code
extension NSDate { struct Date { static let timeFormatter: NSDateFormatter = { let df = NSDateFormatter() df.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) df.dateFormat = "h:mm a" return df }() } var time: String { return Date.timeFormatter.stringFromDate(self) } var year: Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Year, fromDate: self) } var month: Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Month, fromDate: self) } var day: Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Day, fromDate: self) } var hour: Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Hour, fromDate: self) } var minute: Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Minute, fromDate: self) } var second: Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Second, fromDate: self) } struct DateComponents { static let formatter: NSDateComponentsFormatter = { let dcFormatter = NSDateComponentsFormatter() dcFormatter.calendar = NSCalendar(identifier: NSCalendarIdentifierISO8601)! dcFormatter.unitsStyle = .Full dcFormatter.maximumUnitCount = 1 dcFormatter.zeroFormattingBehavior = .Default dcFormatter.allowsFractionalUnits = false dcFormatter.allowedUnits = [.Year, .Month, .Weekday, .Day, .Hour, .Minute, .Second] return dcFormatter }() } var elapsedTime: String { if isecond(NSDate()) { return "Just now" } if ishour(NSDate()) { return "an hour ago" } if isyesterday(NSDate()) { return "Yesterday" } if isweek(NSDate()) { return "a week ago" } return (DateComponents.formatter.stringFromTimeInterval(NSDate().timeIntervalSinceDate(self)) ?? "") + " ago" } func isyesterday(date: NSDate) -> Bool { return NSCalendar.autoupdatingCurrentCalendar().isDate(self, inSameDayAsDate: date.yesterday) } var yesterday: NSDate { return NSCalendar.autoupdatingCurrentCalendar().dateWithEra(1, year: year, month: month, day: day-1, hour: 0, minute: 0, second: 0, nanosecond: 0)! } func isweek(date: NSDate) -> Bool { return NSCalendar.autoupdatingCurrentCalendar().isDate(self, inSameDayAsDate: date.weekago) } var weekago: NSDate { return NSCalendar.autoupdatingCurrentCalendar().dateWithEra(1, year: year, month: month, day: day-7, hour: 0, minute: 0, second: 0, nanosecond: 0)! } func isecond(date: NSDate) -> Bool { return NSCalendar.autoupdatingCurrentCalendar().isDate(self, inSameDayAsDate: date.secondsago) } var secondsago: NSDate { return NSCalendar.autoupdatingCurrentCalendar().dateWithEra(1, year: year, month: month, day: 0, hour: 0, minute: 0, second: second-15, nanosecond: 0)! } func ishour(date: NSDate) -> Bool { return NSCalendar.autoupdatingCurrentCalendar().isDate(self, inSameDayAsDate: date.hourago) } var hourago: NSDate { return NSCalendar.autoupdatingCurrentCalendar().dateWithEra(1, year: year, month: month, day: 0, hour: hour-1, minute: minute-1, second: 0, nanosecond: 0)! } }
the quotes which is "an hour ago" , " Just now", etc not showing, only " Yesterday" and i'm not sure if it is from 7 days to 23 hours only!
and here is my first question link : Swift How to convert Parse createdAt time to time ago?
Please help me to fix the code, thanks to @Leo Dabus for the code.
-
luk2302 over 8 yearsWho upvoted this question??? "help me to fix the code" - in what way, you just show code, is it not working, is crashing, compiler errors, runtime errors, not working as expected?
-
luk2302 over 8 yearsoriginal question and answer: stackoverflow.com/questions/34439560/…
-
Salah over 8 yearsSorry the code working there's no crash or compiler error, but the code not count as i expected
-
luk2302 over 8 yearsThen what is it doing as opposed to what you expect, give a few (!!!) sample input/output scenarios
-
Salah over 8 yearsits kinda hard to me to example that, i think it counting well but not showing my quotes on time like if the time is between 1-16secs not showing " Just now"
-
luk2302 over 8 yearsThen what is it showing, show some Input and show the output, where is the problem in that? What are you sending in, what is coming out? What would you want to come out, have you tried debugging it? Added log statements?
-
Leo Dabus over 8 years@Salah why did you change some read-only computed properties like var isDateInToday: Bool { return NSCalendar.autoupdatingCurrentCalendar().isDateInToday(self) } var isDateInYesterday: Bool { return NSCalendar.autoupdatingCurrentCalendar().isDateInYesterday(self) } ?
-
Leo Dabus over 8 years@Salah you should edit your question and show only the elapsedTime part of the question and post a link to the other question/answer
-
Salah over 8 years@LeoDabus Thanks question edited, And i don't know what i have changed in the code because i'm trying to do it
-
-
Geeroz about 8 yearsIs this how to use it correctly. let lastUpdate = defaults.objectForKey("LastUpdate") as? NSDate print("Last update (lastUpdate!.getElapsedInterval())") Work grate for me.
-
Kwalker108 about 7 yearsThanks Sonia! One thing - currently you are printing the whole interval with "(interval)" in the return statements for month and day. They should be "(month)" and "(day)" respectively.
-
Sonia Casas about 7 yearsOh, yes absolutely @Kwalker108. If You don't mind i'm going to update the answer and add a way to have this behaviour but for all lenguajes.
-
CertainPerformance almost 6 yearsA plain code block without any explanation usually isn't very useful...
-
Markus Rautopuro over 5 yearsThe code doesn't work, for example
toDate
is not defined (should be a parameter). -
Sarath Raveendran over 5 years@CertainPerformance The code is self-explanatory. I believe someone who knows Swift can easily understand the above code.
-
Lucas Chwe about 5 yearsi think you are just missing adding the "ago" part for the case that is not "a moment ago"
-
Sonia Casas about 5 yearsHey @LucasChwe, you are right, I didn't add it. I'll modify my answer.
-
coders about 5 yearsthis works, thanks so much! I test the locolizations
-
Ashley Mills over 4 yearsPlease don't give the same answer to multiple questions. If the question is a duplicate, mark it so - or link to your other answer in a comment.
-
Okhan Okbay over 3 yearsavailable for iOS 13+
-
Tom GODDARD over 2 yearsClean 🚀thank you!