Getting the difference between two Dates (months/days/hours/minutes/seconds) in Swift
Xcode 8.3 • Swift 3.1 or later
You can use Calendar to help you create an extension to do your date calculations as follow:
extension Date { /// Returns the amount of years from another date func years(from date: Date) -> Int { return Calendar.current.dateComponents([.year], from: date, to: self).year ?? 0 } /// Returns the amount of months from another date func months(from date: Date) -> Int { return Calendar.current.dateComponents([.month], from: date, to: self).month ?? 0 } /// Returns the amount of weeks from another date func weeks(from date: Date) -> Int { return Calendar.current.dateComponents([.weekOfMonth], from: date, to: self).weekOfMonth ?? 0 } /// Returns the amount of days from another date func days(from date: Date) -> Int { return Calendar.current.dateComponents([.day], from: date, to: self).day ?? 0 } /// Returns the amount of hours from another date func hours(from date: Date) -> Int { return Calendar.current.dateComponents([.hour], from: date, to: self).hour ?? 0 } /// Returns the amount of minutes from another date func minutes(from date: Date) -> Int { return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0 } /// Returns the amount of seconds from another date func seconds(from date: Date) -> Int { return Calendar.current.dateComponents([.second], from: date, to: self).second ?? 0 } /// Returns the a custom time interval description from another date func offset(from date: Date) -> String { if years(from: date) > 0 { return "\(years(from: date))y" } if months(from: date) > 0 { return "\(months(from: date))M" } if weeks(from: date) > 0 { return "\(weeks(from: date))w" } if days(from: date) > 0 { return "\(days(from: date))d" } if hours(from: date) > 0 { return "\(hours(from: date))h" } if minutes(from: date) > 0 { return "\(minutes(from: date))m" } if seconds(from: date) > 0 { return "\(seconds(from: date))s" } return "" }}
Using Date Components Formatter
let dateComponentsFormatter = DateComponentsFormatter()dateComponentsFormatter.allowedUnits = [.second, .minute, .hour, .day, .weekOfMonth, .month, .year]dateComponentsFormatter.maximumUnitCount = 1dateComponentsFormatter.unitsStyle = .fulldateComponentsFormatter.string(from: Date(), to: Date(timeIntervalSinceNow: 4000000)) // "1 month"
let date1 = DateComponents(calendar: .current, year: 2014, month: 11, day: 28, hour: 5, minute: 9).date!let date2 = DateComponents(calendar: .current, year: 2015, month: 8, day: 28, hour: 5, minute: 9).date!let years = date2.years(from: date1) // 0let months = date2.months(from: date1) // 9let weeks = date2.weeks(from: date1) // 39let days = date2.days(from: date1) // 273let hours = date2.hours(from: date1) // 6,553let minutes = date2.minutes(from: date1) // 393,180let seconds = date2.seconds(from: date1) // 23,590,800let timeOffset = date2.offset(from: date1) // "9M"let date3 = DateComponents(calendar: .current, year: 2014, month: 11, day: 28, hour: 5, minute: 9).date!let date4 = DateComponents(calendar: .current, year: 2015, month: 11, day: 28, hour: 5, minute: 9).date!let timeOffset2 = date4.offset(from: date3) // "1y"let date5 = DateComponents(calendar: .current, year: 2017, month: 4, day: 28).date!let now = Date()let timeOffset3 = now.offset(from: date5) // "1w"
If someone needs to display all time units e.g "hours minutes seconds" not just "hours". Let's say the time difference between two dates is 1hour 59minutes 20seconds. This function will display "1h 59m 20s".
Here is my Objective-C code:
extension NSDate { func offsetFrom(date: NSDate) -> String { let dayHourMinuteSecond: NSCalendarUnit = [.Day, .Hour, .Minute, .Second] let difference = NSCalendar.currentCalendar().components(dayHourMinuteSecond, fromDate: date, toDate: self, options: []) let seconds = "\(difference.second)s" let minutes = "\(difference.minute)m" + " " + seconds let hours = "\(difference.hour)h" + " " + minutes let days = "\(difference.day)d" + " " + hours if difference.day > 0 { return days } if difference.hour > 0 { return hours } if difference.minute > 0 { return minutes } if difference.second > 0 { return seconds } return "" }}
In Swift 3+:
extension Date { func offsetFrom(date: Date) -> String { let dayHourMinuteSecond: Set<Calendar.Component> = [.day, .hour, .minute, .second] let difference = NSCalendar.current.dateComponents(dayHourMinuteSecond, from: date, to: self) let seconds = "\(difference.second ?? 0)s" let minutes = "\(difference.minute ?? 0)m" + " " + seconds let hours = "\(difference.hour ?? 0)h" + " " + minutes let days = "\(difference.day ?? 0)d" + " " + hours if let day = difference.day, day > 0 { return days } if let hour = difference.hour, hour > 0 { return hours } if let minute = difference.minute, minute > 0 { return minutes } if let second = difference.second, second > 0 { return seconds } return "" }}
You ask:
I'd like to have a function that compares the two dates and if(seconds > 60) then it returns minutes, if(minutes > 60) return hours and if(hours > 24) return days and so on.
I'm assuming that you're trying to build a string representation of the elapsed time between two dates. Rather than writing your own code to do that, Apple already has a class designed to do precisely that. Namely, use DateComponentsFormatter
, set allowedUnits
to whatever values make sense to your app, set unitsStyle
to whatever you want (e.g. .full
), and then call string(from:to:)
.
E.g. in Swift 3:
let previousDate = ...let now = Date()let formatter = DateComponentsFormatter()formatter.unitsStyle = .fullformatter.allowedUnits = [.month, .day, .hour, .minute, .second]formatter.maximumUnitCount = 2 // often, you don't care about seconds if the elapsed time is in months, so you'll set max unit to whatever is appropriate in your caselet string = formatter.string(from: previousDate, to: now)
This also will localize the string appropriate for the device in question.
Or, in Swift 2.3:
let previousDate = ...let now = NSDate()let formatter = NSDateComponentsFormatter()formatter.unitsStyle = .Fullformatter.allowedUnits = [.Month, .Day, .Hour, .Minute, .Second]formatter.maximumUnitCount = 2let string = formatter.stringFromDate(previousDate, toDate: now)
If you're looking for the actual numeric values, just use dateComponents
. E.g. in Swift 3:
let components = Calendar.current.dateComponents([.month, .day, .hour, .minute, .second], from: previousDate, to: now)
Or, in Swift 2.3:
let components = NSCalendar.currentCalendar().components([.Month, .Day, .Hour, .Minute, .Second], fromDate: previousDate, toDate: now, options: [])