Swift days between two NSDates
Solution 1
You have to consider the time difference as well. For example if you compare the dates 2015-01-01 10:00
and 2015-01-02 09:00
, days between those dates will return as 0 (zero) since the difference between those dates is less than 24 hours (it's 23 hours).
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
// ...
let calendar = 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.Day
let components = calendar.components(flags, fromDate: date1, toDate: date2, options: [])
components.day // This will return the number of day(s) between dates
Swift 3 and Swift 4 Version
let calendar = Calendar.current
// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDay(for: firstDate)
let date2 = calendar.startOfDay(for: secondDate)
let components = calendar.dateComponents([.day], from: date1, to: date2)
Solution 2
Here is my answer for Swift 2:
func daysBetweenDates(startDate: NSDate, endDate: NSDate) -> Int
{
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Day], fromDate: startDate, toDate: endDate, options: [])
return components.day
}
Solution 3
I see a couple Swift3 answers so I'll add my own:
public static func daysBetween(start: Date, end: Date) -> Int {
Calendar.current.dateComponents([.day], from: start, to: end).day!
}
The naming feels more Swifty, it's one line, and using the latest dateComponents()
method.
Solution 4
Here is very nice, Date
extension to get difference between dates in years, months, days, hours, minutes, seconds
extension Date {
func years(sinceDate: Date) -> Int? {
return Calendar.current.dateComponents([.year], from: sinceDate, to: self).year
}
func months(sinceDate: Date) -> Int? {
return Calendar.current.dateComponents([.month], from: sinceDate, to: self).month
}
func days(sinceDate: Date) -> Int? {
return Calendar.current.dateComponents([.day], from: sinceDate, to: self).day
}
func hours(sinceDate: Date) -> Int? {
return Calendar.current.dateComponents([.hour], from: sinceDate, to: self).hour
}
func minutes(sinceDate: Date) -> Int? {
return Calendar.current.dateComponents([.minute], from: sinceDate, to: self).minute
}
func seconds(sinceDate: Date) -> Int? {
return Calendar.current.dateComponents([.second], from: sinceDate, to: self).second
}
}
Solution 5
I translated my Objective-C answer
let start = "2010-09-01"
let end = "2010-09-05"
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let startDate:NSDate = dateFormatter.dateFromString(start)
let endDate:NSDate = dateFormatter.dateFromString(end)
let cal = NSCalendar.currentCalendar()
let unit:NSCalendarUnit = .Day
let components = cal.components(unit, fromDate: startDate, toDate: endDate, options: nil)
println(components)
result
<NSDateComponents: 0x10280a8a0>
Day: 4
The hardest part was that the autocompletion insists fromDate and toDate would be NSDate?
, but indeed they must be NSDate!
as shown in the reference.
I don't see how a good solution with an operator would look like, as you want to specify the unit differently in each case. You could return the time interval, but than won't you gain much.
Linus
Updated on July 22, 2022Comments
-
Linus almost 2 years
I'm wondering if there is some new and awesome possibility to get the amount of days between two NSDates in Swift / the "new" Cocoa?
E.g. like in Ruby I would do:
(end_date - start_date).to_i
-
Martin R almost 10 yearsDon't use (24*60*60) for the length of a day. This does not take daylight saving time transitions into account.
-
Daniel Schlaug almost 10 yearsI think NSDate would adjust for that since it always uses GMT and daylight saving is just a formatting or localisation upon that. For sure it gets trickier for months, years or anything of really variable length though.
-
Anton almost 10 yearsseconds / (24*60*60) it's wrong for days calculation. you are not counter a leap year with this way
-
Martin R almost 10 years@DanielSchlaug: Your first solution (using date components) is fine. But from the time interval (which is just a number of seconds) you cannot compute the days, month, ... correctly anymore.
-
Daniel Schlaug almost 10 years@MartinR true, I looked into it a bit more and it would seem my solution here is indeed very much inferior to rubys. Indeed a decent solution would require a lot more code. But I do maintain my position that a day is defined well enough at 86400s (±1 leap second happening approximately once per year) that one may calculate a day interval for all practical uses. But I should not have used intuition for time calculation so the criticism is valid.
-
Martin R almost 10 years@DanielSchlaug: Leap seconds are not the problem, the Unix time does not count them. But in regions with daylight saving time, the clocks are adjusted one hour forward in the spring, and adjusted backward in the autumn. That means that there are days with 23 hours or 25 hours.
-
Daniel Schlaug almost 10 years@MartinR I had to try it to believe it but indeed, now that I did I also saw that wikipedia mentions this. You are correct. Thanks for being stubborn with me.
-
Daniel Schlaug almost 10 yearsThere, edited to be correct. But the flashiness kind of went away.
-
vikingosegundo almost 10 yearsYou had to check wikipedia to learn about daylight saving time? But you realized that Sweden changes the time twice a year.
-
Daniel Schlaug almost 10 yearsWell, while I'm always very confused every time DST changes I wasn't so ignorant as to not know about it. I had to check wikipedia to confirm that DST actually makes the definition of the length of a day dependant on location and point in time. My intuition would say that even during a DST change if the time between March 30 12:00 and April 1 12:00 is 23 hours it's actually a little less than a full day. I was wrong.
-
vikingosegundo almost 10 yearsit is defined by location, point of time and calendar system. the hebrew calendar has a leap month. there is a great wwdc video: performing calendar calculation — a must-see for every cocoa coder.
-
TaylorAllred about 9 yearsLooks like
.DayCalendarUnit
is deprecated. I believe now you should use.CalendarUnitDay
instead. -
Lucas van Dongen over 8 yearsoptions is now an expected parameter
-
brandonscript over 8 yearsYou may actually want to check for 12pm (noon) instead of startOfDayForDate -- should be less likely to bork due to adjusting timezones and DST.
-
Andrej over 8 yearsRunning Swift 2 this works for me:
let components = cal.components(.Day, fromDate: startDate, toDate: endDate, options: [])
-
Delete My Account about 8 yearsI successfully used this with components of @vikingosegundo post above. It returns an integer representing the correct number of days between two dates. <thumbs up>
-
William GP about 8 years@TaylorAllred just
.Day
now -
mbonness about 8 yearsI like it but the function name should be "daysBetweenDates"
-
Peter Johnson almost 8 yearsthat measures the difference between the number portion of the dates- I.e. 21st January to 22nd of February will give 1 day, not 32 days as it should
-
Fattie over 7 yearsit would definitely be better to move them both to noon, rather than 00:00 to avoid many problems.
-
MonsieurDart about 7 yearsSetting the dates to noon can be done like this:
calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: firstDate))
-
tawheed over 6 yearsThis returns 0 if we are comparing
today
andtomorrow
-
TheTiger about 6 years
date
should besinceDate
in function parameters. -
Krunal about 6 years@TheTiger - Thank you very much for highlighting the biggest mistake of this answer.. I'll practically test and update answer soon.
-
TheTiger about 6 yearsMy pleasure! I have tested it for
days
and it works fine. -
Rob over 5 yearsGood answer. I’d only suggest
func years(since date: Date) -> Int? { return Calendar.current.dateComponents[.year], from: date, to: self).years }
, and you could the call it aslet y = date1.years(since: date2)
. That might be more consistent with modern naming conventions. -
jamix about 4 yearsShorter version for setting noon (
startOfDay()
seems to be unnecessary):calendar.date(bySettingHour: 12, minute: 0, second: 0, of: firstDate)
. -
Leo Dabus almost 4 yearsYou should use Swift native Calendar (drop the NS). The use of guard when setting the hour to 12pm is pointless. It will never fail.
-
Leo Dabus almost 4 yearscalling startOfDay before setting the hour to noon it is pointless as well.
-
ramzesenok over 3 yearsthis is the copy of another answer with just added date parsing, which was never asked for
-
Petar over 3 yearsCan someone please explain me what happens when Calendar.current's timeZone doesn't match the time zone that firstDate/secondDate were initialized with (e.g. from a time formatter) ? What should we do if we want to find the distance in days between a date that we know is in a given time zone (different from current) and the current date in the current time zone - how exactly should we convert the Calendar ?
-
Petar over 3 yearsThis can be a computed variable instead of a function.
-
Petar over 3 yearsAlso note that this won't work correctly - e.g. firstDate = 2021-01-13 22:00:00 +0000, startOfDay = 2021-01-13 22:00:00 +0000, secondDate = 2021-01-14 12:54:46 +0000, startOfDay = 2021-01-13 22:00:00 +0000 . Even though the secondDate is considered in the next day, using startOfDay will make both dates equal. For this reason I think it is better to use add the hour value / 24 to the day value and round up.
-
Lumii over 2 yearsThe accepted answer is incorrect. To know why, please look at the posted question - stackoverflow.com/questions/70014759/… However, I do not have a good answer on this.
-
Emin Bugra Saral over 2 yearsTimezone difference would have no effect since the absolute distance is the same. The shift will be on both sides. a - b = c == (a + 2) - (b + 2)
-
Admin over 2 yearsYour answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.