62

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?

Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
Vinod Vishwanath
  • 5,821
  • 2
  • 26
  • 40
  • Use `Date` and `Calendar`. Look at the reference docs for both to see the API changes. Make an attempt. Update your question with what you have tried and explain any issues you may be having in the translation. – rmaddy Oct 16 '16 at 21:53

11 Answers11

117

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.

flo_23
  • 1,914
  • 2
  • 14
  • 13
  • 11
    This does not work for me in that it requires 24 hours between days. – dwsolberg Apr 27 '18 at 20:57
  • 5
    WARNING: this gives the number of 24h-period between two dates, rounded down. The original question is asking about the difference in calendar days, and the solution is in Vinod's answer. – Kqtr Dec 27 '18 at 13:19
  • 9
    @Kqtr replace "dateA" and "dateB" with `calendar.startOfDay(for: dateA)` and `calendar.startOfDay(for: dateB)` – Stan Nov 05 '19 at 12:48
  • Need to divide diffInDays by 24 like diffInDays/24 = number of days – Mashhadi May 05 '20 at 06:26
  • To expand @stan 's answer re: the member `calendar`, it should be `Calendar.current.startOfDay(for: dateA) and Calendar.current.startOfDay(for: dateB)` – Grace Huang Sep 14 '22 at 17:30
57

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
Vinod Vishwanath
  • 5,821
  • 2
  • 26
  • 40
  • The end variable has the argument "self", what is this supposed to be equal to? I tried using your function in a custom class to get the days between a date and what a user in put but self does not work. – Hedylove Mar 24 '17 at 05:16
  • I have updated my answer with a sample usage. The given code was meant to be used as an instance method (which can be done by adding the function in an extension of `Date`). – Vinod Vishwanath Mar 25 '17 at 09:18
  • Awesome extension! Perfect for any intervals of any datetypes, exactly what I was looking for :). For others that want to add future dates more easily try this, var components = DateComponents() components.setValue(numOfDays, for: .day) let futureDate = Calendar.current.date(byAdding: components, to: dateChosen), dateChosen is just this, let dateChosen: Date = Date() – Joseph Astrahan Jun 11 '17 at 21:21
  • 2
    CAREFUL: this seems not to work on some time zones, like UTC+05:30. I suspect that this will tell you whether the two dates are within the same 24h-period since the era started, and that start of the era is midnight on a specific time zone (UTC?). So it basically disregards the time zone of the calendar. I ended up creating a dumb custom utility method that compares "yyyy-MM-dd" of both dates and adds/remove a day until it finds the same string. – Kqtr Jan 02 '19 at 20:28
  • 4
    I found that to solve timezone problems (where two date's times 'today' in my timezone were in 'different days' in the calculated timezone) I had better results when I converted both inputs dates to midnight using calendar.startOfDay. I used the above with the addition of this: // Midnight the day... let midnight1 = DateTimeHelper.midnight(date: date1) let midnight2 = DateTimeHelper.midnight(date: date2) – Adam Jack Jan 24 '19 at 18:21
  • 1
    @AdamJack Thank you for your comment, exact what I was looking for. – f0rz Jan 30 '20 at 15:12
  • I wish I read the comments. It would save me a lot of time. This answer ignores time zones. Please update it by using Adam Jack's suggestion about midnight, for example. – walker May 23 '22 at 09:28
23

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

Steve B
  • 861
  • 1
  • 7
  • 9
17

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

iTALIYA
  • 972
  • 8
  • 14
  • I didn't understand this code let stMin: String = "\((Int(strTimer)! % 3600) / 60)". Why are you doing modulo and divide function – venky Jul 17 '19 at 12:48
9
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
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Kevin
  • 711
  • 7
  • 19
7

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)
Ashim Dahal
  • 1,097
  • 13
  • 15
3

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 ""
    }
}
Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
2

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)
Coltuxumab
  • 615
  • 5
  • 16
  • I think this answer is good. If the intent is to show the number of days (or days and months or hours and seconds or whatever) elapsed between two dates in the UI, this is an excellent way of doing that. `DateComponentsFormatter` generates localized strings and is really powerful and flexible. – Rob Apr 01 '18 at 04:39
  • Thanks Rob - updated to eliminate the unhelpful second half of my answer. – Coltuxumab Apr 01 '18 at 14:56
  • Nice and simple answer! For other readers, if you want the month, day, or hour to be displayed in short form, change formatter.unitsStyle to .abbreviated instead of .brief – elarcoiris Apr 11 '19 at 02:33
1
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()
    }
Mohammad Razipour
  • 3,643
  • 3
  • 29
  • 49
0
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]
Arasuvel
  • 2,971
  • 1
  • 25
  • 40
Anuj Panwar
  • 683
  • 7
  • 10
0

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...!

Kiran Jadhav
  • 3,209
  • 26
  • 29