Getting the difference between two Dates (months/days/hours/minutes/seconds) in Swift
Asked Answered
C

20

262

I am trying to get the difference between the current date as NSDate() and a date from a PHP time(); call for example: NSDate(timeIntervalSinceReferenceDate: 1417147270). How do I go about getting the difference in time between the two dates. 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.

How should I go about this?

EDIT: The current accepted answer has done exactly what I've wanted to do. I recommend it for easy usage for getting the time between two dates in the form that that PHP time() function uses. If you aren't particularly familiar with PHP, that's the time in seconds from January 1st, 1970. This is beneficial for a backend in PHP. If perhaps you're using a backend like NodeJS you might want to consider some of the other options you'll find below.

Compression answered 28/11, 2014 at 4:43 Comment(3)
I realize this is kind of old, but what do you intend to do with the difference? For example, if you're looking to format a string for the user, you should be using NSDateComponentsFormatter. It's very configurable, allowing you to get appropriately terse results (e.g. .maximumUnitCount = 1).Fireball
Really, the question what you intend to do is absolutely essential. Consider that a month can be as little as 28 days, or as much as 31 days plus one hour.Maize
https://mcmap.net/q/111049/-swift-3-find-number-of-calendar-days-between-two-datesErrant
E
600

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 = 1
dateComponentsFormatter.unitsStyle = .full
dateComponentsFormatter.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)     // 0
let months = date2.months(from: date1)   // 9
let weeks = date2.weeks(from: date1)     // 39
let days = date2.days(from: date1)       // 273
let hours = date2.hours(from: date1)     // 6,553
let minutes = date2.minutes(from: date1) // 393,180
let seconds = date2.seconds(from: date1) // 23,590,800

let 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"
Encratis answered 28/11, 2014 at 7:59 Comment(9)
In Swift 2.0, .CalendarUnitSecond gives error 'NSCalendarUnit.Type' does not have a member named 'CalendarUnitSecond', do you know how to solve it?Gray
@Gray you need to use .Second and instead of options: nil you need to use options: []. You can take a look at my edit.Encratis
While I generally like the detailed answer, I do not concur with months and years in the result. Both have not definite length (but best an average one). So giving them back is wrong without explanation.Cranberry
I hate the duplications in this answer, I would use a method based on NSCalendarUnit, implemented as return Calendar.current().components(unit, from: date, to: self, options: [])?. valueForComponent(unit) (on iOS > 8).Beastings
I use this methodfunc years(from date: Date) -> Int { return Calendar.current().components(.year, from: date, to: self, options: []).year ?? 0 } and it return 2147483647 for every componentKepner
@RajAggrawal the correct method in Swift 3 is dateComponents, not componentsEncratis
It's not 100% clear what 'from' means. I would suggest using 'since' instead. e.g. timeIntervalSinceSpinozism
There seems to be an error in func weeks(from date: Date): it should be Calendar.current.dateComponents([.weekOfYear], from: date, to: self).weekOfYear.Fiveandten
@Fiveandten you need to use weekOfMonth weekOfYear returns zero for meEncratis
R
64

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 ""
    }

}
Rubberize answered 8/4, 2016 at 8:14 Comment(2)
This is not working when difference is 1 daySpinthariscope
This is great, but a small improvement for digital-style monospaced displays: Add something like this to each line so it displays "05s" instead of "5s" let seconds = "\(difference.second >= 10 ? "" : "0")\(difference.second ?? 0)s"Hypoacidity
C
28

Swift 5.1 • iOS 13

You can use RelativeDateFormatter that has been introduced by Apple in iOS 13.

let exampleDate = Date().addingTimeInterval(-15000)

let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .full
let relativeDate = formatter.localizedString(for: exampleDate, relativeTo: Date())

print(relativeDate) // 4 hours ago

See How to show a relative date and time using RelativeDateTimeFormatter.

Calcicole answered 25/1, 2020 at 7:2 Comment(0)
T
16

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 = .full
formatter.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 case

let 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 = .Full
formatter.allowedUnits = [.Month, .Day, .Hour, .Minute, .Second]
formatter.maximumUnitCount = 2

let 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: [])
Transact answered 13/1, 2017 at 18:10 Comment(0)
J
6

combined Extension + DateComponentsFormatter from the answer of @leo-dabus

Xcode 8.3 • Swift 3.1

extension DateComponentsFormatter {
    func difference(from fromDate: Date, to toDate: Date) -> String? {
        self.allowedUnits = [.year,.month,.weekOfMonth,.day]
        self.maximumUnitCount = 1
        self.unitsStyle = .full
        return self.string(from: fromDate, to: toDate)
    }
}

let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.difference(from: Date(), to: Date(timeIntervalSinceNow: 4000000)) // "1 month"
Jardena answered 9/5, 2017 at 16:40 Comment(0)
S
6

--> Use this to find time gap between two dates in Swift(With two Strings).

func timeGapBetweenDates(previousDate : String,currentDate : String)
{
    let dateString1 = previousDate
    let dateString2 = currentDate

    let Dateformatter = DateFormatter()
    Dateformatter.dateFormat = "yyyy-MM-dd HH:mm:ss"


    let date1 = Dateformatter.date(from: dateString1)
    let date2 = Dateformatter.date(from: dateString2)


    let distanceBetweenDates: TimeInterval? = date2?.timeIntervalSince(date1!)
    let secondsInAnHour: Double = 3600
    let minsInAnHour: Double = 60
    let secondsInDays: Double = 86400
    let secondsInWeek: Double = 604800
    let secondsInMonths : Double = 2592000
    let secondsInYears : Double = 31104000

    let minBetweenDates = Int((distanceBetweenDates! / minsInAnHour))
    let hoursBetweenDates = Int((distanceBetweenDates! / secondsInAnHour))
    let daysBetweenDates = Int((distanceBetweenDates! / secondsInDays))
    let weekBetweenDates = Int((distanceBetweenDates! / secondsInWeek))
    let monthsbetweenDates = Int((distanceBetweenDates! / secondsInMonths))
    let yearbetweenDates = Int((distanceBetweenDates! / secondsInYears))
    let secbetweenDates = Int(distanceBetweenDates!)




    if yearbetweenDates > 0
    {
        print(yearbetweenDates,"years")//0 years
    }
    else if monthsbetweenDates > 0
    {
        print(monthsbetweenDates,"months")//0 months
    }
    else if weekBetweenDates > 0
    {
        print(weekBetweenDates,"weeks")//0 weeks
    }
    else if daysBetweenDates > 0
    {
        print(daysBetweenDates,"days")//5 days
    }
    else if hoursBetweenDates > 0
    {
        print(hoursBetweenDates,"hours")//120 hours
    }
    else if minBetweenDates > 0
    {
        print(minBetweenDates,"minutes")//7200 minutes
    }
    else if secbetweenDates > 0
    {
        print(secbetweenDates,"seconds")//seconds
    }
}
Sideways answered 10/1, 2019 at 8:54 Comment(1)
@Mona don't use this. This date formatter approach is very error prone. It will crash your app if it fails to parse your stringEncratis
L
4
  func dateDiff(dateStr:String) -> String {
        var f:NSDateFormatter = NSDateFormatter()
        f.timeZone = NSTimeZone.localTimeZone()
        f.dateFormat = "yyyy-M-dd'T'HH:mm:ss.SSSZZZ"
        
        var now = f.stringFromDate(NSDate())
        var startDate = f.dateFromString(dateStr)
        var endDate = f.dateFromString(now)
        var calendar: NSCalendar = NSCalendar.currentCalendar()
        
        let calendarUnits = NSCalendarUnit.CalendarUnitWeekOfMonth | NSCalendarUnit.CalendarUnitDay | NSCalendarUnit.CalendarUnitHour | NSCalendarUnit.CalendarUnitMinute | NSCalendarUnit.CalendarUnitSecond
        let dateComponents = calendar.components(calendarUnits, fromDate: startDate!, toDate: endDate!, options: nil)
        
        let weeks = abs(dateComponents.weekOfMonth)
        let days = abs(dateComponents.day)
        let hours = abs(dateComponents.hour)
        let min = abs(dateComponents.minute)
        let sec = abs(dateComponents.second)
        
        var timeAgo = ""

        if (sec > 0){
            if (sec > 1) {
                timeAgo = "\(sec) Seconds Ago"
            } else {
                timeAgo = "\(sec) Second Ago"
            }
        }
        
        if (min > 0){
            if (min > 1) {
                timeAgo = "\(min) Minutes Ago"
            } else {
                timeAgo = "\(min) Minute Ago"
            }
        }
        
        if(hours > 0){
            if (hours > 1) {
                timeAgo = "\(hours) Hours Ago"
            } else {
                timeAgo = "\(hours) Hour Ago"
            }
        }
        
        if (days > 0) {
            if (days > 1) {
                timeAgo = "\(days) Days Ago"
            } else {
                timeAgo = "\(days) Day Ago"
            }
        }
        
        if(weeks > 0){
            if (weeks > 1) {
                timeAgo = "\(weeks) Weeks Ago"
            } else {
                timeAgo = "\(weeks) Week Ago"
            }
        }
        
        print("timeAgo is===> \(timeAgo)")
        return timeAgo;
    }
Levana answered 16/10, 2015 at 15:19 Comment(1)
try to lazy load the date formatter and even better option would be to make it staticStulin
T
4

Slightly modified code for Swift 3.0

let calendar = NSCalendar.current as NSCalendar

// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDay(for: startDateTime)
let date2 = calendar.startOfDay(for: endDateTime)

let flags = NSCalendar.Unit.day
let components = calendar.components(flags, from: date1, to: date2, options: [])

return components.day!
Tereasaterebene answered 22/10, 2016 at 13:57 Comment(0)
P
4

I added a "long" version to Leo Dabus's asnwer in case you want to have a string that says something like "2 weeks ago" instead of just "2w"...

extension Date {
    func offsetLong(from date: Date) -> String {
        if years(from: date) > 0 {
            return years(from: date) > 1 ? "\(years(from: date)) years ago" : "\(years(from: date)) year ago"
        }
        if months(from: date) > 0 {
            return months(from: date) > 1 ? "\(months(from: date)) months ago" : "\(months(from: date)) month ago" 
        }
        if weeks(from: date) > 0 {
            return weeks(from: date) > 1 ? "\(weeks(from: date)) weeks ago" : "\(weeks(from: date)) week ago"
        }
        if days(from: date) > 0 {
            return days(from: date) > 1 ? "\(days(from: date)) days ago" : "\(days(from: date)) day ago" 
        }
        if hours(from: date) > 0 {
            return hours(from: date) > 1 ? "\(hours(from: date)) hours ago" : "\(hours(from: date)) hour ago"
        }
        if minutes(from: date) > 0 {
            return minutes(from: date) > 1 ? "\(minutes(from: date)) minutes ago" : "\(minutes(from: date)) minute ago"
        }
        if seconds(from: date) > 0 {
            return seconds(from: date) > 1 ? "\(seconds(from: date)) seconds ago" : "\(seconds(from: date)) second ago"
        }
        return ""
    }
}
Peccavi answered 9/2, 2017 at 17:54 Comment(0)
I
2

With Swift 3, according to your needs, you may choose one of the two following ways to solve your problem.


1. Display the difference between two dates to the user

You can use a DateComponentsFormatter to create strings for your app’s interface. DateComponentsFormatter has a maximumUnitCount property with the following declaration:

var maximumUnitCount: Int { get set }

Use this property to limit the number of units displayed in the resulting string. For example, with this property set to 2, instead of “1h 10m, 30s”, the resulting string would be “1h 10m”. Use this property when you are constrained for space or want to round up values to the nearest large unit.

By setting maximumUnitCount's value to 1, you are guaranteed to display the difference in only one DateComponentsFormatter's unit (years, months, days, hours or minutes).

The Playground code below shows how to display the difference between two dates:

import Foundation

let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
let newDate = Date(timeIntervalSinceReferenceDate: 0)

let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [NSCalendar.Unit.year, .month, .day, .hour, .minute]
dateComponentsFormatter.maximumUnitCount = 1
dateComponentsFormatter.unitsStyle = DateComponentsFormatter.UnitsStyle.full
let timeDifference = dateComponentsFormatter.string(from: oldDate, to: newDate)

print(String(reflecting: timeDifference)) // prints Optional("5 hours")

Note that DateComponentsFormatter rounds up the result. Therefore, a difference of 4 hours and 30 minutes will be displayed as 5 hours.

If you need to repeat this operation, you can refactor your code:

import Foundation

struct Formatters {

    static let dateComponentsFormatter: DateComponentsFormatter = {
        let dateComponentsFormatter = DateComponentsFormatter()
        dateComponentsFormatter.allowedUnits = [NSCalendar.Unit.year, .month, .day, .hour, .minute]
        dateComponentsFormatter.maximumUnitCount = 1
        dateComponentsFormatter.unitsStyle = DateComponentsFormatter.UnitsStyle.full
        return dateComponentsFormatter
    }()

}

extension Date {
    
    func offset(from: Date) -> String? {
        return Formatters.dateComponentsFormatter.string(from: oldDate, to: self)
    }
    
}

let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
let newDate = Date(timeIntervalSinceReferenceDate: 0)

let timeDifference = newDate.offset(from: oldDate)
print(String(reflecting: timeDifference)) // prints Optional("5 hours")

2. Get the difference between two dates without formatting

If you don't need to display with formatting the difference between two dates to the user, you can use Calendar. Calendar has a method dateComponents(_:from:to:) that has the following declaration:

func dateComponents(_ components: Set<Calendar.Component>, from start: Date, to end: Date) -> DateComponents

Returns the difference between two dates.

The Playground code below that uses dateComponents(_:from:to:) shows how to retrieve the difference between two dates by returning the difference in only one type of Calendar.Component (years, months, days, hours or minutes).

import Foundation

let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
let newDate = Date(timeIntervalSinceReferenceDate: 0)

let descendingOrderedComponents = [Calendar.Component.year, .month, .day, .hour, .minute]
let dateComponents = Calendar.current.dateComponents(Set(descendingOrderedComponents), from: oldDate, to: newDate)
let arrayOfTuples = descendingOrderedComponents.map { ($0, dateComponents.value(for: $0)) }

for (component, value) in arrayOfTuples {
    if let value = value, value > 0 {
        print(component, value) // prints hour 4
        break
    }
}

If you need to repeat this operation, you can refactor your code:

import Foundation

extension Date {
    
    func offset(from: Date) -> (Calendar.Component, Int)? {
        let descendingOrderedComponents = [Calendar.Component.year, .month, .day, .hour, .minute]
        let dateComponents = Calendar.current.dateComponents(Set(descendingOrderedComponents), from: from, to: self)
        let arrayOfTuples = descendingOrderedComponents.map { ($0, dateComponents.value(for: $0)) }
        
        for (component, value) in arrayOfTuples {
            if let value = value, value > 0 {
                return (component, value)
            }
        }
        
        return nil
    }

}

let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
let newDate = Date(timeIntervalSinceReferenceDate: 0)

if let (component, value) = newDate.offset(from: oldDate) {
    print(component, value) // prints hour 4
}
Intellection answered 3/3, 2017 at 23:34 Comment(0)
D
1

If your purpose is to get the exact day number between two dates, you can work around this issue like this:

// Assuming that firstDate and secondDate are defined
// ...

var calendar: NSCalendar = NSCalendar.currentCalendar()

// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDayForDate(firstDate)
let date2 = calendar.startOfDayForDate(secondDate)

let flags = NSCalendarUnit.DayCalendarUnit
let components = calendar.components(flags, fromDate: date1, toDate: date2, options: nil)

components.day  // This will return the number of day(s) between dates
Diaphysis answered 27/1, 2015 at 5:6 Comment(0)
S
1

In Swift 2.2

    /// Returns the amount of years from another date
func years(fromdate: NSDate) -> Int {
    return NSCalendar.currentCalendar().components([.Year], fromDate: fromdate, toDate: NSDate(), options: []).year ?? 0
}
/// Returns the amount of months from another date
func months(fromdate: NSDate) -> Int {
    return NSCalendar.currentCalendar().components([.Month], fromDate: fromdate, toDate: NSDate(), options: []).month ?? 0
}
/// Returns the amount of weeks from another date
func weeks(fromdate: NSDate) -> Int {
    return NSCalendar.currentCalendar().components([.WeekOfYear], fromDate: fromdate, toDate: NSDate(), options: []).weekOfYear ?? 0
}
/// Returns the amount of days from another date
func days(fromdate: NSDate) -> Int {
    return NSCalendar.currentCalendar().components([.Day], fromDate: fromdate, toDate: NSDate(), options: []).day ?? 0
}
/// Returns the amount of hours from another date
func hours(fromdate: NSDate) -> Int {
    return NSCalendar.currentCalendar().components([.Hour], fromDate: fromdate, toDate: NSDate(), options: []).hour ?? 0
}
/// Returns the amount of minutes from another date
func minutes(fromdate: NSDate) -> Int {
    return NSCalendar.currentCalendar().components([.Minute], fromDate: fromdate, toDate: NSDate(), options: []).minute ?? 0
}
/// Returns the amount of seconds from another date
func seconds(fromdate: NSDate) -> Int {
    return NSCalendar.currentCalendar().components(.Second, fromDate: fromdate, toDate: NSDate(), options: []).second ?? 0
}
Serin answered 19/4, 2017 at 7:26 Comment(0)
I
1

A small addition to Leo Dabus' answer to provide the plural versions and be more human readable.

Swift 3

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)   == 1 { return "\(years(from: date)) year"   } else if years(from: date)   > 1 { return "\(years(from: date)) years"   }
        if months(from: date)  == 1 { return "\(months(from: date)) month"  } else if months(from: date)  > 1 { return "\(months(from: date)) month"  }
        if weeks(from: date)   == 1 { return "\(weeks(from: date)) week"   } else if weeks(from: date)   > 1 { return "\(weeks(from: date)) weeks"   }
        if days(from: date)    == 1 { return "\(days(from: date)) day"    } else if days(from: date)    > 1 { return "\(days(from: date)) days"    }
        if hours(from: date)   == 1 { return "\(hours(from: date)) hour"   } else if hours(from: date)   > 1 { return "\(hours(from: date)) hours"   }
        if minutes(from: date) == 1 { return "\(minutes(from: date)) minute" } else if minutes(from: date) > 1 { return "\(minutes(from: date)) minutes" }
        return ""
    }
}
Inviolate answered 24/8, 2017 at 18:28 Comment(0)
A
1

Some addition in jose920405 answer to make it compatible with Swift 3.0 and above

func getDateTimeDiff(dateStr:String) -> String {
    
    let formatter : DateFormatter = DateFormatter()
    formatter.timeZone = NSTimeZone.local
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    
    let now = formatter.string(from: NSDate() as Date)
    let startDate = formatter.date(from: dateStr)
    let endDate = formatter.date(from: now)
    
    // *** create calendar object ***
    var calendar = NSCalendar.current
    
    // *** Get components using current Local & Timezone ***
    print(calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: startDate!))
    
    // *** define calendar components to use as well Timezone to UTC ***
    let unitFlags = Set<Calendar.Component>([.year, .month, .day, .hour, .minute, .second])
    calendar.timeZone = TimeZone(identifier: "UTC")!
    let dateComponents = calendar.dateComponents(unitFlags, from: startDate!, to: endDate!)
    
    // *** Get Individual components from date ***
    let years = dateComponents.year!
    let months = dateComponents.month!
    let days = dateComponents.day!
    let hours = dateComponents.hour!
    let minutes = dateComponents.minute!
    let seconds = dateComponents.second!
    
    var timeAgo = ""
    
    if (seconds > 0){
        if seconds < 2 {
            timeAgo = "Second Ago"
        }
        else{
            timeAgo = "\(seconds) Second Ago"
        }
    }
    
    if (minutes > 0){
        if minutes < 2 {
            timeAgo = "Minute Ago"
        }
        else{
            timeAgo = "\(minutes) Minutes Ago"
        }
    }
    
    if(hours > 0){
        if hours < 2 {
            timeAgo = "Hour Ago"
        }
        else{
            timeAgo = "\(hours) Hours Ago"
        }
    }
    
    if (days > 0) {
        if days < 2 {
            timeAgo = "Day Ago"
        }
        else{
            timeAgo = "\(days) Days Ago"
        }
    }
    
    if(months > 0){
        if months < 2 {
            timeAgo = "Month Ago"
        }
        else{
            timeAgo = "\(months) Months Ago"
        }
    }
    
    if(years > 0){
        if years < 2 {
            timeAgo = "Year Ago"
        }
        else{
            timeAgo = "\(years) Years Ago"
        }
    }
    
    DLog("timeAgo is ===> \(timeAgo)")
    return timeAgo;
}
Assisi answered 21/8, 2018 at 4:47 Comment(0)
M
1

Swift 5

func dateDiff(dateStr:String) -> String {
    let f:DateFormatter = DateFormatter()
    f.timeZone = NSTimeZone.local
      f.dateFormat = "yyyy-M-dd'T'HH:mm:ss.SSSZZZ"
      
    let now = f.string(from: NSDate() as Date)
    let startDate = f.date(from: dateStr)
    let endDate = f.date(from: now)
    var _: NSCalendar = NSCalendar.current as NSCalendar
      
    
    let dateComponents = Calendar.current.dateComponents([ .weekOfMonth, .day , .hour , .minute , .second], from: startDate!, to: endDate!)
      
      let weeks = abs(dateComponents.weekOfMonth!)
      let days = abs(dateComponents.day!)
      let hours = abs(dateComponents.hour!)
      let min = abs(dateComponents.minute!)
      let sec = abs(dateComponents.second!)
      
      var timeAgo = ""

      if (sec > 0){
          if (sec > 1) {
              timeAgo = "\(sec) Seconds Ago"
          } else {
              timeAgo = "\(sec) Second Ago"
          }
      }
      
      if (min > 0){
          if (min > 1) {
              timeAgo = "\(min) Minutes Ago"
          } else {
              timeAgo = "\(min) Minute Ago"
          }
      }
      
      if(hours > 0){
          if (hours > 1) {
              timeAgo = "\(hours) Hours Ago"
          } else {
              timeAgo = "\(hours) Hour Ago"
          }
      }
      
      if (days > 0) {
          if (days > 1) {
              timeAgo = "\(days) Days Ago"
          } else {
              timeAgo = "\(days) Day Ago"
          }
      }
      
      if(weeks > 0){
          if (weeks > 1) {
              timeAgo = "\(weeks) Weeks Ago"
          } else {
              timeAgo = "\(weeks) Week Ago"
          }
      }
      
      print("timeAgo is===> \(timeAgo)")
      return timeAgo;
  }
Marjorie answered 19/4, 2022 at 20:36 Comment(0)
E
0

Here is my answer for the Swift 3 answers above. This is current as of Nov 2016, Xcode release was 8.2 Beta (8C23). Used some of both Sagar and Emin suggestions above and sometimes had to let Xcode autocomplete to suggest the syntax. It seemed like the syntax really changed to this beta version. buyDate I got from a DatePicker:

let calendar = NSCalendar.current as NSCalendar
let currentDate = Date()
let date1 = calendar.startOfDay(for: buyDate!)
let date2 = calendar.startOfDay(for: currentDate)      
let flags = NSCalendar.Unit.day
let components = calendar.components(flags, from: date1, to: date2)
NSLog(" day= \(components.day)")
Escarole answered 30/11, 2016 at 19:28 Comment(2)
Sorry, now that I look at this, this is Sagar's solution. I did so many iterations and tried so many things I thought it had changed. Emin's solution doesn't work for the latest Swift 3.Escarole
You shouldn't really be using NSCalendar in Swift 3. Use Calendar. So this is simplified to let calendar = Calendar.current. And then components would look like: let components = calendar.dateComponents([.day], from: date1, to: date2).Transact
C
0

This is the shorter version: Basically I try to get the difference between the post timestamp with the Date() now.

// MARK: - UPDATE Time Stamp
static func updateTimeStampPost(postTimeStamp: Date?, _ completion: (_ finalString: String?) -> Void) {
    // date in the current state
    let date = Date()
    let dateComponentFormatter = DateComponentsFormatter()

    // change the styling date, wether second minute or hour
    dateComponentFormatter.unitsStyle = .abbreviated
    dateComponentFormatter.allowedUnits = [.second, .minute, .hour, .day, .weekOfMonth]
    dateComponentFormatter.maximumUnitCount = 1

    // return the date new format as a string in the completion
    completion(dateComponentFormatter.string(from: postTimeStamp!, to: date))
}
Chace answered 10/4, 2017 at 17:43 Comment(0)
B
0

For XCode Version 8.3.3 & Swift 3.0:

    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = .medium
    dateFormatter.timeStyle = .short

    var beginDate = "2017-08-24 12:00:00"
    var endDate = "2017-09-07 12:00:00"


    let startDateTime = dateFormatter.date(from: beginDate) //according to date format your date string
    print(startDateTime ?? "") //Convert String to Date

    let endDateTime = dateFormatter.date(from: endDate) //according to date format your date string
    print(endDateTime ?? "") //Convert String to Date

    let dateComponentsFormatter = DateComponentsFormatter()
    dateComponentsFormatter.allowedUnits = [NSCalendar.Unit.minute,NSCalendar.Unit.hour,NSCalendar.Unit.day]


   let interval = endDateTime!.timeIntervalSince(startDateTime!)
   var diff = dateComponentsFormatter.string(from: interval)!

   print(diff)

   var day_i  = 0
   var hour_i = 0
   var min_i = 0


     if (diff.contains("d"))
       {
              let day = diff.substring(to: (diff.range(of: "d")?.lowerBound)!)

               day_i  = Int(day)!
               print ("day --> \(day_i)")

               diff = diff.substring(from:(diff.range(of : " ")?.upperBound )!)
               print(diff)
       }


       let hour = diff.substring(to: (diff.range(of : ":")?.lowerBound )!)
       hour_i  = Int(hour)!
       print ("hour --> \(hour_i)")

       let min = diff.substring(from: (diff.range(of : ":")?.upperBound )!)
       min_i  = Int(min)!
       print ("min --> \(min_i)")
Beatabeaten answered 16/8, 2017 at 12:16 Comment(0)
L
0

Use this code:

let registrationDateString = "2008-10-06 00:00:00"
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
    if let registrationDate = dateFormatter.date(from: registrationDateString) {
        let currentDate = Date()
        let dateDifference = Calendar.current.dateComponents([.day, .month, .year],
                                                               from: registrationDate,
                                                               to: currentDate)
        print("--------------------- Result: \(dateDifference.year ?? 0) years \(dateDifference.month ?? 0) months and \(dateDifference.day ?? 0) days")
    } else {
        print("--------------------- No result")
    }

Output is: Result: 10 years 1 months and 18 days

Longe answered 24/11, 2018 at 6:52 Comment(0)
N
0
import Foundation

extension DateComponents {

    func dateComponentsToTimeString() -> String {

        var hour = "\(self.hour!)"
        var minute = "\(self.minute!)"
        var second = "\(self.second!)"

        if self.hour! < 10 { hour = "0" + hour }
        if self.minute! < 10 { minute = "0" + minute }
        if self.second! < 10 { second = "0" + second }

        let str = "\(hour):\(minute):\(second)"
        return str
    }

}

extension Date {

    func offset(from date: Date)-> DateComponents {
        let components = Set<Calendar.Component>([.second, .minute, .hour, .day, .month, .year])
        let differenceOfDate = Calendar.current.dateComponents(components, from: date, to: self)
        return differenceOfDate
    }
}

Use:

var durationString: String {
        return self.endTime.offset(from: self.startTime).dateComponentsToTimeString()
    }
Nicely answered 9/8, 2019 at 13:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.