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
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)
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
}
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.
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
}
}
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.
Update for Swift 3 iOS 10 Beta 4
func daysBetweenDates(startDate: Date, endDate: Date) -> Int {
let calendar = Calendar.current
let components = calendar.dateComponents([Calendar.Component.day], from: startDate, to: endDate)
return components.day!
}
Swift 5. Thanks to Emin Buğra Saral above for the startOfDay suggestion.
extension Date {
func daysBetween(date: Date) -> Int {
return Date.daysBetween(start: self, end: date)
}
static func daysBetween(start: Date, end: Date) -> Int {
let calendar = Calendar.current
// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDay(for: start)
let date2 = calendar.startOfDay(for: end)
let a = calendar.dateComponents([.day], from: date1, to: date2)
return a.value(for: .day)!
}
}
Usage:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let start = dateFormatter.date(from: "2017-01-01")!
let end = dateFormatter.date(from: "2018-01-01")!
let diff = Date.daysBetween(start: start, end: end) // 365
// or
let diff = start.daysBetween(date: end) // 365
Here is the answer for Swift 3 (tested for IOS 10 Beta)
func daysBetweenDates(startDate: Date, endDate: Date) -> Int
{
let calendar = Calendar.current
let components = calendar.components([.day], from: startDate, to: endDate, options: [])
return components.day!
}
Then you can call it like this
let pickedDate: Date = sender.date
let NumOfDays: Int = daysBetweenDates(startDate: pickedDate, endDate: Date())
print("Num of Days: \(NumOfDays)")
Swift 5
Working, you need to set the time to be the same for both days, if you are off by seconds it will be wrong
func daysBetween(start: Date, end: Date) -> Int {
let start = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: start)!
let end = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: end)!
return Calendar.current.dateComponents([.day], from: start, to: end).day ?? 0
}
The things built into swift are still very basic. As they should be at this early stage. But you can add your own stuff with the risk that comes with overloading operators and global domain functions. They will be local to your module though.
let now = NSDate()
let seventies = NSDate(timeIntervalSince1970: 0)
// Standard solution still works
let days = NSCalendar.currentCalendar().components(.CalendarUnitDay,
fromDate: seventies, toDate: now, options: nil).day
// Flashy swift... maybe...
func -(lhs:NSDate, rhs:NSDate) -> DateRange {
return DateRange(startDate: rhs, endDate: lhs)
}
class DateRange {
let startDate:NSDate
let endDate:NSDate
var calendar = NSCalendar.currentCalendar()
var days: Int {
return calendar.components(.CalendarUnitDay,
fromDate: startDate, toDate: endDate, options: nil).day
}
var months: Int {
return calendar.components(.CalendarUnitMonth,
fromDate: startDate, toDate: endDate, options: nil).month
}
init(startDate:NSDate, endDate:NSDate) {
self.startDate = startDate
self.endDate = endDate
}
}
// Now you can do this...
(now - seventies).months
(now - seventies).days
This is an updated version of Emin's answer for Swift 5 that incorporates the suggestion to use noon instead of midnight as the definitive time for comparing days. It also handles the potential failure of various date functions by returning an optional.
///
/// This is an approximation; it does not account for time differences. It will set the time to 1200 (noon) and provide the absolute number
/// of days between now and the given date. If the result is negative, it should be read as "days ago" instead of "days from today."
/// Returns nil if something goes wrong initializing or adjusting dates.
///
func daysFromToday() -> Int?
{
let calendar = NSCalendar.current
// Replace the hour (time) of both dates with noon. (Noon is less likely to be affected by DST changes, timezones, etc. than midnight.)
guard let date1 = calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: Date())),
let date2 = calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: self)) else
{
return nil
}
return calendar.dateComponents([.day], from: date1, to: date2).day
}
Here is my answer for Swift 3:
func daysBetweenDates(startDate: NSDate, endDate: NSDate, inTimeZone timeZone: TimeZone? = nil) -> Int {
var calendar = Calendar.current
if let timeZone = timeZone {
calendar.timeZone = timeZone
}
let dateComponents = calendar.dateComponents([.day], from: startDate.startOfDay, to: endDate.startOfDay)
return dateComponents.day!
}
You could use the following extension:
public extension Date {
func daysTo(_ date: Date) -> Int? {
let calendar = Calendar.current
// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDay(for: self)
let date2 = calendar.startOfDay(for: date)
let components = calendar.dateComponents([.day], from: date1, to: date2)
return components.day // This will return the number of day(s) between dates
}
}
Then, you can call it like this:
startDate.daysTo(endDate)
There's hardly any Swift-specific standard library yet; just the lean basic numeric, string, and collection types.
It's perfectly possible to define such shorthands using extensions, but as far as the actual out-of-the-box APIs goes, there is no "new" Cocoa; Swift just maps directly to the same old verbose Cocoa APIs as they already exist.
I'm going to add my version even though this thread is a year old. My code looks like this:
var name = txtName.stringValue // Get the users name
// Get the date components from the window controls
var dateComponents = NSDateComponents()
dateComponents.day = txtDOBDay.integerValue
dateComponents.month = txtDOBMonth.integerValue
dateComponents.year = txtDOBYear.integerValue
// Make a Gregorian calendar
let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)
// Get the two dates we need
var birthdate = calendar?.dateFromComponents(dateComponents)
let currentDate = NSDate()
var durationDateComponents = calendar?.components(NSCalendarUnit.CalendarUnitDay, fromDate: birthdate!, toDate: currentDate, options: nil)
let numberOfDaysAlive = durationDateComponents?.day
println("\(numberOfDaysAlive!)")
txtGreeting.stringValue = "Hello \(name), You have been alive for \(numberOfDaysAlive!) days."
I hope it helps someone.
Cheers,
Erin's method updated to Swift 3, This shows days from today (disregarding time of day)
func daysBetweenDates( endDate: Date) -> Int
let calendar: Calendar = Calendar.current
let date1 = calendar.startOfDay(for: Date())
let date2 = calendar.startOfDay(for: secondDate)
return calendar.dateComponents([.day], from: date1, to: date2).day!
}
This returns an absolute difference in days between some Date and today:
extension Date {
func daysFromToday() -> Int {
return abs(Calendar.current.dateComponents([.day], from: self, to: Date()).day!)
}
}
and then use it:
if someDate.daysFromToday() >= 7 {
// at least a week from today
}
easier option would be to create a extension on Date
public extension Date {
public var currentCalendar: Calendar {
return Calendar.autoupdatingCurrent
}
public func daysBetween(_ date: Date) -> Int {
let components = currentCalendar.dateComponents([.day], from: self, to: date)
return components.day!
}
}
Swift 3.2
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)
}
}
All answer is good. But for Localizations we need calculates a number of decimal days in between two dates. so we can provide the sustainable decimal format.
// This method returns the fractional number of days between to dates
func getFractionalDaysBetweenDates(date1: Date, date2: Date) -> Double {
let components = Calendar.current.dateComponents([.day, .hour], from: date1, to: date2)
var decimalDays = Double(components.day!)
decimalDays += Double(components.hour!) / 24.0
return decimalDays
}
Nice handy one liner :
extension Date {
var daysFromNow: Int {
return Calendar.current.dateComponents([.day], from: Date(), to: self).day!
}
}
Swift 3 - Days from today until date
func daysUntilDate(endDateComponents: DateComponents) -> Int
{
let cal = Calendar.current
var components = cal.dateComponents([.era, .year, .month, .day], from: NSDate() as Date)
let today = cal.date(from: components)
let otherDate = cal.date(from: endDateComponents)
components = cal.dateComponents([Calendar.Component.day], from: (today! as Date), to: otherDate!)
return components.day!
}
Call function like this
// Days from today until date
var examnDate = DateComponents()
examnDate.year = 2016
examnDate.month = 12
examnDate.day = 15
let daysCount = daysUntilDate(endDateComponents: examnDate)
extension Date {
func daysFromToday() -> Int {
return Calendar.current.dateComponents([.day], from: self, to: Date()).day!
}
}
Then use it like
func dayCount(dateString: String) -> String{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM dd,yyyy hh:mm a"
let fetchedDate = dateFormatter.date(from: dateString)
let day = fetchedDate?.daysFromToday()
if day! > -1{
return "\(day!) days passed."
}else{
return "\(day! * -1) days left."
}
}
extension Date {
static func - (recent: Date, previous: Date) -> DateComponents {
var dateComponents = DateComponents()
dateComponents.year = Calendar.current.dateComponents([.day], from: previous, to: recent).year
dateComponents.month = Calendar.current.dateComponents([.month], from: previous, to: recent).month
dateComponents.day = Calendar.current.dateComponents([.day], from: previous, to: recent).day
dateComponents.hour = Calendar.current.dateComponents([.hour], from: previous, to: recent).hour
dateComponents.minute = Calendar.current.dateComponents([.minute], from: previous, to: recent).minute
dateComponents.second = Calendar.current.dateComponents([.second], from: previous, to: recent).second
return dateComponents
}
}
func completeOffset(from date:Date) -> String? {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .brief
return formatter.string(from: Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date, to: self))
}
if you need year month days and hours as string use this
var tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date())!
let dc = tomorrow.completeOffset(from: Date())
2017 version, copy and paste
func simpleIndex(ofDate: Date) -> Int {
// index here just means today 0, yesterday -1, tomorrow 1 etc.
let c = Calendar.current
let todayRightNow = Date()
let d = c.date(bySetting: .hour, value: 13, of: ofDate)
let t = c.date(bySetting: .hour, value: 13, of: todayRightNow)
if d == nil || today == nil {
print("weird problem simpleIndex#ofDate")
return 0
}
let r = c.dateComponents([.day], from: today!, to: d!)
// yesterday is negative one, tomorrow is one
if let o = r.value(for: .day) {
return o
}
else {
print("another weird problem simpleIndex#ofDate")
return 0
}
}
let calendar = NSCalendar.currentCalendar();
let component1 = calendar.component(.Day, fromDate: fromDate)
let component2 = calendar.component(.Day, fromDate: toDate)
let difference = component1 - component2
Swift 5.2.4 solution:
import UIKit
let calendar = Calendar.current
let start = "2010-09-01"
let end = "2010-09-05"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let firstDate = dateFormatter.date(from: start)!
let secondDate = dateFormatter.date(from: end)!
// 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([Calendar.Component.day], from: date1, to: date2)
components.day // This will return the number of day(s) between dates
I need to check a date before downloading / manipulate some data from a server. Let's say I need to do that only if 24 hours or more are gone by. this code seems to work, but I'm not sure about it, no way to do it with less lines of code? it seems to be too long to me. i checked this but solutions are quite different from mine.
import UIKit
//standard day formatter
let dateFormatter = DateFormatter()
//let's say this is the date I saved last time i updated data from online server:
let previousDate: String = "2019-03-19 06:40 PM"
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm a"
let old = dateFormatter.date(from: previousDate)
//today I try to download data
let today = Date()
//this simply tests if "moment before" is greater than "moment after"
if today > old! {
print("update data")
} else {
print("do not update")
}
//here I create a measure
let minute:TimeInterval = 60.0
let hour:TimeInterval = 60.0 * minute
let day:TimeInterval = 24 * hour
//here I measure if the old date added of 24h is greater than now, in that case a day or more is passed and I can update
let theOldDateMore24h = Date(timeInterval: day, since: old!)
if theOldDateMore24h < today {
print("passed more than a day: Update!")
} else {
print("less than a day, do not update")
}
There is a method in Calendar
func dateComponents(_ components: Set<Calendar.Component>, from start: Date, to end: Date) -> DateComponents
Get the day component and check greater than 0
let dateFormatter = DateFormatter()
let previousDate = "2019-03-19 06:40 PM"
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm a"
let old = dateFormatter.date(from: previousDate)
//today I try to download data
let today = Date()
if let validDate = old, Calendar.current.dateComponents([.day], from: validDate, to: today).day! > 0 {
print("passed more than a day: Update!")
} else {
print("less than a day, do not update")
}
Quick extension function to simplify it:
extension Date {
func isWithin(_ distanceTime: TimeInterval, after laterDate: Date) -> Bool{
let distance = timeIntervalSince(laterDate)
let result = distanceTime >= distance
return result
}
}
//Usage
let secondsInDay = TimeInterval(60 * 60 * 24)
let isUpToDate = Date().isWithin(secondsInDay, after: previousDate)
if !isUpToDate {
print("passed more than a day: Update!")
}
else {
print("less than a day, do not update")
}
You can actually use an extension for this. It will return the required calendar component
Extension
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
}
}
Usage
let yesterday = Date(timeInterval: -86400, since: Date())
let tomorrow = Date(timeInterval: 86400, since: Date())
// Change the component to your preference
let difference = tomorrow.interval(ofComponent: .day, fromDate: yesterday) // returns 2
I'm looking for a way to get the first day of a month (in Swift).
I would like to know if it is a Monday, Tuesday etc... by returning the number corresponding.
I try many solution like getting a NSCalendar component .weekDay but no one work.
Example :
print(getTheFirstDate("2016-2-18"))
// Should return : 0 (because the first day of February 2016 is Monday).
Any help would be appreciate.
First you need to parse your date string, then you can use Calendar method dateComponents to get the calendar, year and month components from that date and create a new date from those components. Then you can extract the weekday date component from it:
Xcode 11.5 • Swift 5.2
extension Date {
var weekday: Int { Calendar.current.component(.weekday, from: self) }
var firstDayOfTheMonth: Date {
Calendar.current.dateComponents([.calendar, .year,.month], from: self).date!
}
}
extension String {
static var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
var date: Date? {
String.dateFormatter.date(from: self)
}
}
"2016-2-18".date?.firstDayOfTheMonth.weekday // 2 = Monday (Sunday-Saturday 1-7)
extension Date {
var startOfWeek: Date {
let gregorian = Calendar(identifier: .gregorian)
let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
return gregorian.date(byAdding: .day, value: 1, to: sunday!)!
}
var endOfWeek: Date {
let gregorian = Calendar(identifier: .gregorian)
let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
return gregorian.date(byAdding: .day, value: 7, to: sunday!)!
}
var startOfPreviousWeek: Date {
let gregorian = Calendar(identifier: .gregorian)
let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
return gregorian.date(byAdding: .day, value: -6, to: sunday!)!
}
var endOfPreviousWeek: Date {
let gregorian = Calendar(identifier: .gregorian)
let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
return gregorian.date(byAdding: .day, value: 0, to: sunday!)!
}
var startDateOfMonth: Date {
return Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self)))!
}
var endDateOfMonth: Date {
return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startDateOfMonth)!
}
var getPreviousMonthDate: Date {
return Calendar.current.date(byAdding: .month, value: -1, to: self)!
}
var startDateOfPreviousMonth: Date {
return getPreviousMonthDate.startDateOfMonth
}
var endOfPreviousMonth: Date {
return getPreviousMonthDate.endDateOfMonth
}
}
Well to get the corresponding number of the day of the week you can use:
var weekday = calendar!.component(NSCalendarUnit.Weekday, fromDate: yourNSDate)
var weekdayName = self.getDayOfWeek(weekday)
Then you can call this function I created to get the corresponding name of the day of the week based on the number passed in to getDayOfWeek()
func getDayOfWeek(weekday:Int) -> String {
if(weekday == 1) {
return "Sunday"
}
else if(weekday == 2) {
return "Monday"
}
else if(weekday == 3) {
return "Tuesday"
}
else if(weekday == 4) {
return "Wednesday"
}
else if(weekday == 5) {
return "Thursday"
}
else if(weekday == 6) {
return "Friday"
}
else {
return "Saturday"
}
}
And if you are using a String as a date to begin with you can change it into an NSDate like this:
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = /*place date format here*/
let date = dateFormatter.dateFromString(/*your_date_string*/)
I like Leo's answer, but in case you want to encapsulate the logic into a single function or don't want to use extensions.
func getDayOfWeekForFirstDayOfMonthFromDateString(dateString:String) -> String? {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
if let date = formatter.dateFromString(dateString) {
if let firstOfMonth = NSCalendar.currentCalendar().dateFromComponents(NSCalendar.currentCalendar().components([.Year, .Month], fromDate: date)) {
formatter.dateFormat = "EEEE"
return formatter.stringFromDate(firstOfMonth)
}
}
return nil
}
EDIT: Adjusted code to utilize Leo's great suggestion, and borrowed his more terse creation of the creation of the first of the month object. Thanks, Leo!