'Swift 3 - find number of calendar days between two dates

The way I did this in Swift 2.3 was:

let currentDate         = NSDate()
let currentCalendar     = NSCalendar.currentCalendar()

var startDate : NSDate?
var endDate   : NSDate?

// The following two lines set the `startDate` and `endDate` to the start of the day

currentCalendar.rangeOfUnit(.Day, startDate: &startDate, interval: nil, forDate: currentDate)
currentCalendar.rangeOfUnit(.Day, startDate: &endDate, interval: nil, forDate: self)

let intervalComps = currentCalendar.components([.Day], fromDate: startDate!, toDate: endDate!, options: [])

print(intervalComps.day)

Now this has all changed with Swift 3. I have to either use NSCalendar and NSDate by constantly type casting with as, or find the Swift 3 way of doing it.

What's the right way to do it in Swift 3?



Solution 1:[1]

Turns out this is much simpler to do in Swift 3:

extension Date {    

    func interval(ofComponent comp: Calendar.Component, fromDate date: Date) -> Int {

        let currentCalendar = Calendar.current

        guard let start = currentCalendar.ordinality(of: comp, in: .era, for: date) else { return 0 }
        guard let end = currentCalendar.ordinality(of: comp, in: .era, for: self) else { return 0 }

        return end - start
    }
}

Edit

Comparing the ordinality of the two dates should be within the same era instead of the same year, since naturally the two dates may fall in different years.

Usage

let yesterday = Date(timeInterval: -86400, since: Date())
let tomorrow = Date(timeInterval: 86400, since: Date())


let diff = tomorrow.interval(ofComponent: .day, fromDate: yesterday)
// return 2

Solution 2:[2]

In Swift 5 there is a simple one-liner to get the number of days (or any other DateComponent) between two dates:

let diffInDays = Calendar.current.dateComponents([.day], from: dateA, to: dateB).day

Note: As pointed out in the comments, this solution measures the 24h periods and therefore requires at least 24h between dateA and dateB.

Solution 3:[3]

Swift 4 Version

let startDate = "2000-11-22"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let formatedStartDate = dateFormatter.date(from: startDate)
let currentDate = Date()
let components = Set<Calendar.Component>([.second, .minute, .hour, .day, .month, .year])
let differenceOfDate = Calendar.current.dateComponents(components, from: formatedStartDate!, to: currentDate)

print (differenceOfDate)

Printed - year: 16 month: 10 day: 19 hour: 12 minute: 16 second: 42 isLeapMonth: false

Solution 4:[4]

If any one want to do it more specifically follow Below Steps

1.Add this Date Extension

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 amount of nanoseconds from another date
func nanoseconds(from date: Date) -> Int {
    return Calendar.current.dateComponents([.nanosecond], from: date, to: self).nanosecond ?? 0
}
/// Returns the a custom time interval description from another date
func offset(from date: Date) -> String {
    var result: 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 seconds(from: date) > 0 { return "\(seconds(from: date))" }
            if days(from: date)    > 0 { result = result + " " + "\(days(from: date)) D" }
            if hours(from: date)   > 0 { result = result + " " + "\(hours(from: date)) H" }
            if minutes(from: date) > 0 { result = result + " " + "\(minutes(from: date)) M" }
           if seconds(from: date) > 0 { return "\(seconds(from: date))" }
    return ""
 }
}

2.Define it in globally

 fileprivate var timer: Timer?

3.Call this Method in viewDidLoad or where ever you want

override func viewDidLoad() {
    super.viewDidLoad()
    self.getRemainingTime()
} 

4.Usage

fileprivate func getRemainingTime() {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

    let startDate = "2018-06-02 10:11:12"
    let currentDate = dateFormatter.string(from: Date())

    if currentDate != startDate {
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(calculateTime)), userInfo: nil, repeats: true)
        RunLoop.current.add(timer!, forMode: RunLoopMode.commonModes)
        timer?.fire()
    }
    else {
        self.timer?.invalidate()
        self.timer = nil
    }
}

func calculateTime() {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

    let stdate : String = "2018-06-02 10:11:12"
    let startDate = dateFormatter.date(from: stdate)!

    let currentDate = Date()

    let strTimer : String = startDate.offset(from: currentDate)
    if !strTimer.isEmpty {
        let stDay: String = "\((Int(strTimer)! % 31536000) / 86400)"
        let stHour: String = "\((Int(strTimer)! % 86400) / 3600)"
        let stMin: String = "\((Int(strTimer)! % 3600) / 60)"
        let stSec: String = "\(Int(strTimer)! % 60)"
        yourLabelOutlet.text = "Start In :\(stDay) Days \(stHour) Hours \(stMin) Minutes \(stSec) Seconds"
    }

}

Works like Charm You can Use every separate string to your UI Side, Enjoy

Solution 5:[5]

private func calculateDaysBetweenTwoDates(start: Date, end: Date) -> Int {

    let currentCalendar = Calendar.current
    guard let start = currentCalendar.ordinality(of: .day, in: .era, for: start) else {
        return 0
    }
    guard let end = currentCalendar.ordinality(of: .day, in: .era, for: end) else {
        return 0
    }
    return end - start
}

Solution 6:[6]

In Swift4 we can easily get no of days between two different calendar dates using below codes.

First one is the difference in days with the current date.

let previousDate = "2017-03-01"
let currentDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let previousDateFormated : Date? = dateFormatter.date(from: previousDate)
let difference = currentDate.timeIntervalSince(previousDateFormated!)
var differenceInDays = Int(difference/(60 * 60 * 24 ))
print(differenceInDays)

Continuing with the above code ... Below is for finding no of days for two different dates. the content of previous date is taken from above date

let futureDate = "2017-12-30"
let futureDateFormatted : Date? = dateFormatter.date(from: futureDate)
differenceInDays = (futureDateFormatted?.timeIntervalSince(previousDateFormated!))! / (60 * 60 * 24)

print(differenceInDays)

Solution 7:[7]

If someone would need 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 code:

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'hh:mm:ss"
let start = dateFormatter.date(from: "2019-01-31T07:45:00")!
let end = dateFormatter.date(from: "2019-03-01T06:30:00")!
print("Date Difference :   ", end.offsetFrom(date: start))

Function Definition:

extension Date {

    func offsetFrom(date : Date) -> String {

        let dayHourMinuteSecond: Set = [.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 ""
    }
}

Solution 8:[8]

Found this on a different thread, but it was finally the most simple solution for me using Swift 4:

let previousDate = ENTER DATE HERE
let now = Date()

let formatter = DateComponentsFormatter()
formatter.unitsStyle = .brief // May delete the word brief to let Xcode show you the other options
formatter.allowedUnits = [.month, .day, .hour]
formatter.maximumUnitCount = 1   // Show just one unit (i.e. 1d vs. 1d 6hrs)

let stringDate = formatter.string(from: previousDate, to: now)

Solution 9:[9]

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()
    }

Solution 10:[10]

Updated for Swift 3:

if you want to print the number of days as well as days list between two calendar dates, used below simple code;

// Variable Declaration:

var daysListArray = [String]()

// function Defination:

func printCountBtnTwoDates(mStartDate: Date, mEndDate: Date) -> Int {
    let calendar = Calendar.current
    let formatter = DateFormatter()
    var newDate = mStartDate
    daysListArray.removeAll()

    while newDate <= mEndDate {
        formatter.dateFormat = "yyyy-MM-dd"
        daysListArray.append(formatter.string(from: newDate))
        newDate = calendar.date(byAdding: .day, value: 1, to: newDate)!
    }
   // print("daysListArray: \(daysListArray)") // if you want to print list between start date and end date
    return daysListArray.count
}

// To call above function:

    let count = self.printCountBtnTwoDates(mStartDate: your_start_date, mEndDate: your_end_date)
    print("count: \(count)") // date count

// Enjoy coding...!

Solution 11:[11]

private func days(actual day1:[Int],expect day2:[Int]) -> Int {

    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd"
    
    let first = "\(day1[2])-\(day1[1])-\(day1[0])"
    let firstDate = dateFormatter.date(from:first)!
    let last = "\(day2[2])-\(day2[1])-\(day2[0])"
    let lastDate = dateFormatter.date(from:last)!
    
    let currentCalendar = NSCalendar.current
    
    let components = currentCalendar.dateComponents([.day], from: firstDate, to: lastDate)
    
    return components.day!
}

Another approach to compare with components of day month year

Usage:

Input the dates in following format

[dd, mm, yyyy]
[9, 6, 2017]
[6, 6, 2017]