71

In a swift playground, I have been using

NSDate.date() 

But, this always appears with the time element appended. For my app I need to ignore the time element. Is this possible in Swift? How can it be done? Even if I could set the time element to be the same time on every date that would work too.

Also, I am trying to compare two dates and at the moment I am using the following code:

var earlierDate:NSDate = firstDate.earlierDate(secondDate)

Is this the only way or can I do this in a way that ignores the time element? For instance I don't want a result if they are the same day, but different times.

jww
  • 83,594
  • 69
  • 338
  • 732
agf119105
  • 1,572
  • 4
  • 12
  • 22
  • 1
    You don't want to "ignore" the time part of the object. It seems that you want to format the date as a string without the time part. You can do that using `NSDateFormatter`. – The Paramagnetic Croissant Jul 04 '14 at 15:11

16 Answers16

140

Use this Calendar function to compare dates in iOS 8.0+

func compare(_ date1: Date, to date2: Date, toGranularity component: Calendar.Component) -> ComparisonResult


passing .day as the unit

Use this function as follows:

let now = Date()
// "Sep 23, 2015, 10:26 AM"
let olderDate = Date(timeIntervalSinceNow: -10000)
// "Sep 23, 2015, 7:40 AM"

var order = Calendar.current.compare(now, to: olderDate, toGranularity: .hour)

switch order {
case .orderedDescending:
    print("DESCENDING")
case .orderedAscending:
    print("ASCENDING")
case .orderedSame:
    print("SAME")
}

// Compare to hour: DESCENDING

var order = Calendar.current.compare(now, to: olderDate, toGranularity: .day)


switch order {
case .orderedDescending:
    print("DESCENDING")
case .orderedAscending:
    print("ASCENDING")
case .orderedSame:
    print("SAME")
}

// Compare to day: SAME
Ashley Mills
  • 41,127
  • 14
  • 115
  • 144
  • is this from UIKit? I cannot find it in a Foundation project. – vikingosegundo Jul 04 '14 at 15:30
  • 2
    This is one of the new iOS 8.0 `NSCalendar` APIs. See `NSCalendar.h` in *iOS 7.1 to iOS 8.0 API Differences* – Ashley Mills Jul 04 '14 at 15:36
  • 1
    finally, I always missed it. – vikingosegundo Jul 04 '14 at 15:36
  • Thanks Ashley, really appreciate this answer - as I am such a beginner, if you have time could you some me how to use this? @AshleyMills – agf119105 Jul 05 '14 at 22:40
  • The new NSCalendar methods like `compareDate:toDate:toUnitGranularity:` look very cool, but they're not in the Xcode docs or the online docs either. Where did you learn about them? – Duncan C May 27 '15 at 23:12
  • Swift doesn't even let me command-click on the method name to find it in the framework headers. I had to command-click on NSCalendar and look in it's header to find the method definition. – Duncan C May 27 '15 at 23:16
  • I found them in the iOS 8.0 API diffs document - these are always worth a read, all kinds of goodies appear in there! https://developer.apple.com/library/ios/releasenotes/General/iOS80APIDiffs/frameworks/Foundation.html – Ashley Mills May 28 '15 at 08:20
  • Whats is the __NSDate type and _NSCopyOnWriteCalendarWrapper ? – the Reverend Sep 22 '15 at 18:21
  • 1
    @theReverend that was a hangover from an old version of Swift. Updated for 2.0 - thanks! – Ashley Mills Sep 23 '15 at 09:30
  • 2
    I think the two comments, indicating the expected results are wrong. `// Compare to day: DESCENDING` should be `SAME` and vice versa. – jaw Feb 06 '16 at 13:44
20

There are several useful methods in NSCalendar in iOS 8.0+:

startOfDayForDate, isDateInToday, isDateInYesterday, isDateInTomorrow

And even to compare days:

func isDate(date1: NSDate!, inSameDayAsDate date2: NSDate!) -> Bool

To ignore the time element you can use this:

var toDay = Calendar.current.startOfDay(for: Date())

But, if you have to support also iOS 7, you can always write an extension

extension NSCalendar {
    func myStartOfDayForDate(date: NSDate!) -> NSDate!
    {
        let systemVersion:NSString = UIDevice.currentDevice().systemVersion
        if systemVersion.floatValue >= 8.0 {
            return self.startOfDayForDate(date)
        } else {
            return self.dateFromComponents(self.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: date))
        }
    }
}
beryllium
  • 29,214
  • 15
  • 99
  • 123
slamor
  • 3,315
  • 2
  • 12
  • 13
12

Xcode 11.2.1, Swift 5 & Above

Checks whether the date has same day component.

Calendar.current.isDate(date1, equalTo: date2, toGranularity: .day)

Adjust toGranularity as your need.

Saranjith
  • 9,547
  • 2
  • 55
  • 102
9

In Swift 4:

func compareDate(date1:Date, date2:Date) -> Bool {
    let order = NSCalendar.current.compare(date1, to: date2, toGranularity: .day)
    switch order {
    case .orderedSame:
        return true
    default:
        return false
    }
}
zs2020
  • 52,221
  • 27
  • 148
  • 209
8

I wrote the following method to compare two dates by borrowing from Ashley Mills solution. It compares two dates and returns true if the two dates are the same (stripped of time).

func compareDate(date1:NSDate, date2:NSDate) -> Bool {
    let order = NSCalendar.currentCalendar().compareDate(date1, toDate: date2,
        toUnitGranularity: .Day)
    switch order {
    case .OrderedSame:
        return true
    default:
        return false
    }
}

And it is called like this:

if compareDate(today, date2: anotherDate) {
    // The two dates are on the same day.
}
Emmett Corman
  • 81
  • 1
  • 1
6

Two Dates comparisions in swift.

    // Date comparision to compare current date and end date.
    var dateComparisionResult:NSComparisonResult = currentDate.compare(endDate)

    if dateComparisionResult == NSComparisonResult.OrderedAscending
    {
        // Current date is smaller than end date.
    }
    else if dateComparisionResult == NSComparisonResult.OrderedDescending
    {
        // Current date is greater than end date.
    }
    else if dateComparisionResult == NSComparisonResult.OrderedSame
    {
        // Current date and end date are same.
    }
abhi
  • 507
  • 6
  • 9
  • 5
    Not quite. The NSDate type represents a point in time; it contains date elements and time elements. So your code compares two points in time as opposed to the intended calendar dates in the original question. – Yer00n Oct 21 '14 at 23:01
6

For iOS7 support

let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date1String = dateFormatter.stringFromDate(date1)
let date2String = dateFormatter.stringFromDate(date2)
if date1String == date2String {
    println("Equal date")
}
Loganathan
  • 1,447
  • 1
  • 12
  • 17
5

You can compare two dates using it's description.

let date1 = NSDate()
let date2 = NSDate(timeIntervalSinceNow: 120)
if date1.description == date2.description {
    print(true)
} else {
    print(false)   // false (I have added 2 seconds between them)
}

If you want set the time element of your dates to a different time you can do as follow:

extension NSDate {
    struct Calendar {
        static let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
    }
    var day:    Int { return Calendar.gregorian.component(.Day,    fromDate: self)   }
    var month:  Int { return Calendar.gregorian.component(.Month,  fromDate: self)  }
    var year:   Int { return Calendar.gregorian.component(.Year,   fromDate: self)  }

    var noon: NSDate {
        return Calendar.gregorian.dateWithEra(1, year: year, month: month, day: day, hour: 12, minute: 0, second: 0, nanosecond: 0)!
    }
}

let date1 = NSDate()
let date2 = NSDate(timeIntervalSinceNow: 120)
print(date1.noon == date2.noon)   // true

or you can also do it using NSDateFormatter:

extension NSDate {
    struct Date {
        static let formatterYYYYMMDD: NSDateFormatter = {
            let formatter = NSDateFormatter()
            formatter.dateFormat = "yyyyMMdd"
            return formatter
        }()
    }
    var yearMonthDay: String {
        return Date.formatterYYYYMMDD.stringFromDate(self)
    }
    func isSameDayAs(date:NSDate) -> Bool {
        return yearMonthDay == date.yearMonthDay
    }
}

let date1 = NSDate()
let date2 = NSDate(timeIntervalSinceNow: 120)
print(date1.yearMonthDay == date2.yearMonthDay)   // true

print(date1.isSameDayAs(date2))    // true

Another option (iOS8+) is to use calendar method isDate(inSameDayAsDate:):

extension NSDate {
    struct Calendar {
        static let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
    }
    func isInSameDayAs(date date: NSDate) -> Bool {
        return Calendar.gregorian.isDate(self, inSameDayAsDate: date)
    }
}
let date1 = NSDate()
let date2 = NSDate(timeIntervalSinceNow: 120)
if date1.isInSameDayAs(date: date2 ){
    print(true)   // true
} else {
    print(false)
}
Leo Dabus
  • 198,248
  • 51
  • 423
  • 494
5

I wrote a Swift 4 extension for comparing two dates:

import Foundation

extension Date {      
  func isSameDate(_ comparisonDate: Date) -> Bool {
    let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)
    return order == .orderedSame
  }

  func isBeforeDate(_ comparisonDate: Date) -> Bool {
    let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)
    return order == .orderedAscending
  }

  func isAfterDate(_ comparisonDate: Date) -> Bool {
    let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)
    return order == .orderedDescending
  }
}

Usage:

startDate.isSameDateAs(endDate) // returns a true or false

Adrian
  • 14,925
  • 16
  • 92
  • 163
4

Swift 3

        let order = NSCalendar.current.compare(date1, to: date2, toGranularity: .day)

        if order == .orderedAscending { 
          // date 1 is older
        }
        else if order == .orderedDescending { 
          // date 1 is newer
        }
        else if order == .orderedSame { 
          // same day/hour depending on granularity parameter
        }
Mobile Developer
  • 5,510
  • 1
  • 36
  • 43
2

For Swift3

var order = NSCalendar.current.compare(firstDate, to: secondDate, toGranularity: .hour)

if order == .orderedSame {
    //Both the dates are same. 
    //Your Logic.
}
Uzma
  • 29
  • 3
1

Swift:

extension NSDate {

    /**
    Compares current date with the given one down to the seconds.
    If date==nil, then always return false

    :param: date date to compare or nil

    :returns: true if the dates has equal years, months, days, hours, minutes and seconds.
    */
    func sameDate(date: NSDate?) -> Bool {
        if let d = date {
            let calendar = NSCalendar.currentCalendar()
            if NSComparisonResult.OrderedSame == calendar.compareDate(self, toDate: d, toUnitGranularity: NSCalendarUnit.SecondCalendarUnit) {
                return true
            }

        }
        return false
    }
}
Alexander Volkov
  • 6,770
  • 1
  • 41
  • 37
0

When you NSDate.date() in the playground, you see the default description printed. Use NSDateFormatter to print a localized description of the date object, possibly with only the date portion.

To zero out specific portions of a date (for the sake of comparison), use NSDateComponents in conjunction with NSCalendar.

Leo Natan
  • 55,734
  • 8
  • 140
  • 186
0

In my experience, most people's problems with using NSDate comes from the incorrect assumption that an NSDate can be used to represent a date in the 'normal' sense (i.e. a 24 period starting at midnight in the local timezone). In normal (everyday / non-programming) usage, 1st January 2014 in London is the same date as 1st January in Beijing or New York even though they cover different periods in real time. To take this to the extreme, the time on Christmas Island is UTC+14 while the time on Midway Island is UTC-11. So 1st January 2014 on these two island are the same date even though one doesn't even start until the other has been completed for an hour.

If that is the kind of date you are recording (and if you are not recording the time component, it probably is), then do not use NSDate (which stores only seconds past 2001-01-01 00:00 UTC, nothing else) but store the year month and day as integers - perhaps by creating your own CivilDate class that wraps these values - and use that instead.

Only dip into NSDate to compare dates and then make sure to explicitly declare the time zone as "UTC" on both NSDates for comparison purposes.

Vince O'Sullivan
  • 2,403
  • 25
  • 40
-1

Swift 4

func compareDate(date1:Date, date2:Date) -> Bool {
    let order = Calendar.current.compare(date1, to: date2,toGranularity: .day)
    switch order {
    case .orderedSame:
        return true
    default:
        return false
    }
}
Asad Jamil
  • 160
  • 8
-3

To answer your question:

Is this possible in Swift?

Yes, it is possible


Ahh, you also want to now HOW

let cal = NSCalendar.currentCalendar()
cal.rangeOfUnit(.DayCalendarUnit, startDate: &d1, interval: nil, forDate: d1) // d1 NSDate?
cal.rangeOfUnit(.DayCalendarUnit, startDate: &d2, interval: nil, forDate: d2) // d2 NSDate?

Now d1 and d2 will contain the dates at beginning of their days.

compare with d1!.compare(d2!)

To display them without time portion, us NSDateFormatter.

vikingosegundo
  • 51,126
  • 14
  • 131
  • 172