NSDate Comparison utilizzando Swift


153

Sto lavorando su un'app che richiede il controllo della data di scadenza per i compiti. Voglio sapere se una data di scadenza è entro la settimana successiva e se è quindi eseguire un'azione.
La maggior parte della documentazione che ho trovato è in Objective-C e non riesco a capire come farlo in Swift. Grazie per l'aiuto!!


2
swift non ha una classe di date in cui usi la classe NSDate Objective C - quindi hai trovato la documentazione corretta
mmmmmm

Possibile duplicato di Confronto di date NSD senza componente temporale . Ci sono molte ottime risposte.
jww


2
Swift 3 ha una Dateclasse. Viene colmato NSDate, ma viene chiamato Date.
BallpointBen

Risposte:


188

Mi piace usare le estensioni per rendere il codice più leggibile. Ecco alcune estensioni NSDate che possono aiutarti a ripulire il codice e renderlo di facile comprensione. L'ho inserito in un file sharedCode.swift:

extension NSDate {

    func isGreaterThanDate(dateToCompare: NSDate) -> Bool {
        //Declare Variables
        var isGreater = false

        //Compare Values
        if self.compare(dateToCompare as Date) == ComparisonResult.orderedDescending {
            isGreater = true
        }

        //Return Result
        return isGreater
    }

    func isLessThanDate(dateToCompare: NSDate) -> Bool {
        //Declare Variables
        var isLess = false

        //Compare Values
        if self.compare(dateToCompare as Date) == ComparisonResult.orderedAscending {
            isLess = true
        }

        //Return Result
        return isLess
    }

    func equalToDate(dateToCompare: NSDate) -> Bool {
        //Declare Variables
        var isEqualTo = false

        //Compare Values
        if self.compare(dateToCompare as Date) == ComparisonResult.orderedSame {
            isEqualTo = true
        }

        //Return Result
        return isEqualTo
    }

    func addDays(daysToAdd: Int) -> NSDate {
        let secondsInDays: TimeInterval = Double(daysToAdd) * 60 * 60 * 24
        let dateWithDaysAdded: NSDate = self.addingTimeInterval(secondsInDays)

        //Return Result
        return dateWithDaysAdded
    }

    func addHours(hoursToAdd: Int) -> NSDate {
        let secondsInHours: TimeInterval = Double(hoursToAdd) * 60 * 60
        let dateWithHoursAdded: NSDate = self.addingTimeInterval(secondsInHours)

        //Return Result
        return dateWithHoursAdded
    }
}

Ora se riesci a fare qualcosa del genere:

//Get Current Date/Time
var currentDateTime = NSDate()

//Get Reminder Date (which is Due date minus 7 days lets say)
var reminderDate = dueDate.addDays(-7)

//Check if reminderDate is Greater than Right now
if(reminderDate.isGreaterThanDate(currentDateTime)) {
    //Do Something...
}

28
Dovresti semplificare il tuo codice. return self.compare(dateToCompare) == NSComparisonResult.OrderedDescending
Olav Gausaker,

5
isEqualToDate è fornito anche da Apple. La sua dichiarazione è in conflitto con quella definita da Apple.
Shamas S - Ripristina Monica il

4
Non ogni giorno ha 24 ore
Leo Dabus,

9
Questa risposta è terribile e non dovrebbe mai essere quella accettata. Non aggiungere mai intervalli di tempo alle date create da te. Questo è esattamente il motivo per cui NSDateComponentsesistono. Ci sono un sacco di casi limite che non vengono gestire correttamente e non ha senso di non aggiungere la conformità alle Comparablea NSDate. Consiglierei di usare la soluzione di John .
fpg1503,

3
Una soluzione migliore è rendere NSDate Equatable, Comparable, quindi potresti semplicemente farlodate1 < date2
aryaxt

209

Se si desidera supportare ==, <, >, <=, o >=per NSDates, è sufficiente dichiarare questo da qualche parte:

public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs === rhs || lhs.compare(rhs) == .OrderedSame
}

public func <(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.compare(rhs) == .OrderedAscending
}

extension NSDate: Comparable { }

2
@Isuru Comparableè un discendente del Equatableprotocollo, quindi non è necessario dichiarare la conformità a entrambi.
John Estropia,

2
Solo curioso perché non è integrato per impostazione predefinita ?!
dVaffection,

3
@dVaffection in Objective-C (in cui sono dichiarati NSDate e amici), se si confrontano utilizzando ==, <, >, ecc, sarai sempre un risultato del confronto del loro indirizzo in memoria, non il confronto del loro valore effettivo. In Swift, sono ancora trattati come riferimenti, quindi penso che la scelta sia stata di (1) mantenere i confronti puntatori come sono in ObjC, oppure (2) eliminare la confusione non fornendo un'implementazione per i confronti.
John Estropia,

2
Un ulteriore vantaggio di questo approccio è che Array.maxElement()ecc. È quindi automaticamente disponibile per le matrici di NSDate.
pr1001,

1
@MarcioCruz Questo è solo un requisito rapido per tutte le implementazioni degli operatori dovrebbero essere nell'ambito globale. Vedi la discussione qui: stackoverflow.com/questions/35246003/...
John Estropia

54

Ecco come confrontare due NSDate in Swift, l'ho appena testato nel parco giochi di Xcode:

if date1.compare(date2) == NSComparisonResult.OrderedDescending
{
    NSLog("date1 after date2");
} else if date1.compare(date2) == NSComparisonResult.OrderedAscending
{
    NSLog("date1 before date2");
} else
{
    NSLog("dates are equal");
}

Quindi per verificare se una data dueDateè entro una settimana da adesso:

let dueDate=...

let calendar = NSCalendar.currentCalendar()
let comps = NSDateComponents()
comps.day = 7
let date2 = calendar.dateByAddingComponents(comps, toDate: NSDate(), options: NSCalendarOptions.allZeros)

if dueDate.compare(date2!) == NSComparisonResult.OrderedDescending
{
    NSLog("not due within a week");
} else if dueDate.compare(date2!) == NSComparisonResult.OrderedAscending
{
    NSLog("due within a week");
} else
{
    NSLog("due in exactly a week (to the second, this will rarely happen in practice)");
}

2
Ordinamento decrescente significa che date1> date2?
Henry oscannlain-miller,

1
Sì, @ Henryoscannlain-miller.
Annulla

46

L'ho sempre fatto in una riga:

let greater = date1.timeIntervalSince1970 < date2.timeIntervalSince1970

Ancora leggibile nel ifblocco


12

In Swift3, la Datestruttura in Foundationora implementa il Comparableprotocollo. Quindi, i precedenti NSDateapprocci di Swift2 sono sostituiti da Swift3 Date.

/**
 `Date` represents a single point in time.

 A `Date` is independent of a particular calendar or time zone. To represent a `Date` to a user, you must interpret it in the context of a `Calendar`.
*/
public struct Date : ReferenceConvertible, Comparable, Equatable {

    // .... more         

    /**
        Returns the interval between the receiver and another given date.

        - Parameter another: The date with which to compare the receiver.

        - Returns: The interval between the receiver and the `another` parameter. If the receiver is earlier than `anotherDate`, the return value is negative. If `anotherDate` is `nil`, the results are undefined.

        - SeeAlso: `timeIntervalSince1970`
        - SeeAlso: `timeIntervalSinceNow`
        - SeeAlso: `timeIntervalSinceReferenceDate`
        */
    public func timeIntervalSince(_ date: Date) -> TimeInterval

   // .... more 

    /// Returns true if the two `Date` values represent the same point in time.
    public static func ==(lhs: Date, rhs: Date) -> Bool

    /// Returns true if the left hand `Date` is earlier in time than the right hand `Date`.
    public static func <(lhs: Date, rhs: Date) -> Bool

    /// Returns true if the left hand `Date` is later in time than the right hand `Date`.
    public static func >(lhs: Date, rhs: Date) -> Bool

    /// Returns a `Date` with a specified amount of time added to it.
    public static func +(lhs: Date, rhs: TimeInterval) -> Date

    /// Returns a `Date` with a specified amount of time subtracted from it.
    public static func -(lhs: Date, rhs: TimeInterval) -> Date

  // .... more
}

Nota ...

In Swift3, Dateè struct, significa che è value type. NSDateè class, lo è reference type.

// Swift3
let a = Date()
let b = a //< `b` will copy `a`. 

// So, the addresses between `a` and `b` are different.
// `Date` is some kind different with `NSDate`.

6
extension NSDate {

    // MARK: - Dates comparison

    func isGreaterThanDate(dateToCompare: NSDate) -> Bool {

        return self.compare(dateToCompare) == NSComparisonResult.OrderedDescending
    }

    func isLessThanDate(dateToCompare: NSDate) -> Bool {

        return self.compare(dateToCompare) == NSComparisonResult.OrderedAscending
    }

    func equalToDate(dateToCompare: NSDate) -> Bool {

        return self.compare(dateToCompare) == NSComparisonResult.OrderedSame
    }
}

6

Se si desidera confrontare le date con la granularità (solo lo stesso giorno o anno ecc.) Su Swift 3.

func compareDate(date1:NSDate, date2:NSDate, toUnitGranularity: NSCalendar.Unit) -> Bool {

 let order = NSCalendar.current.compare(date1 as Date, to: date2 as Date, toGranularity: .day)
 switch order {
 case .orderedSame:
   return true
 default:
   return false
 }
}

Per altri confronti del calendario, cambiare .day in;

.year .month .day .hour .minute .second


5

Swift già implementa il confronto delle date, usa solo date1> date2 e così via.

/// Returns true if the two `Date` values represent the same point in time.
public static func ==(lhs: Date, rhs: Date) -> Bool

/// Returns true if the left hand `Date` is earlier in time than the right hand `Date`.
public static func <(lhs: Date, rhs: Date) -> Bool

/// Returns true if the left hand `Date` is later in time than the right hand `Date`.
public static func >(lhs: Date, rhs: Date) -> Bool

/// Returns a `Date` with a specified amount of time added to it.
public static func +(lhs: Date, rhs: TimeInterval) -> Date

/// Returns a `Date` with a specified amount of time subtracted from it.
public static func -(lhs: Date, rhs: TimeInterval) -> Date

/// Add a `TimeInterval` to a `Date`.
///
/// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more.
public static func +=(lhs: inout Date, rhs: TimeInterval)

/// Subtract a `TimeInterval` from a `Date`.
///
/// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more.
public static func -=(lhs: inout Date, rhs: TimeInterval)

4

in Swift 3, la data è comparabile in modo da poter confrontare direttamente le date come

let date1 = Date()
let date2 = Date()

let isGreater = date1 > date2
print(isGreater)

let isEqual = date1 == date2
print(isEqual)

o in alternativa

let result = date1.compare(date2)
switch result {
    case .OrderedAscending     :   print("date 1 is earlier than date 2")
    case .OrderedDescending    :   print("date 1 is later than date 2")
    case .OrderedSame          :   print("two dates are the same")
}

modo migliore di creare extensionalla data

extension Date {

  fun isGreater(than date: Date) -> Bool {
    return self > date 
  }

  func isSmaller(than date: Date) -> Bool {
    return self < date
  }

  func isEqual(to date: Date) -> Bool {
    return self == date
  }

}

uso let isGreater = date1.isGreater(than: date2)


3

Questa funzione ha funzionato per me per confrontare se una data (startDate) era successiva alla data di fine in cui entrambi sono stati definiti come variabili NSDate:

if startDate.compare(endDate as Date) == ComparisonResult.orderedDescending

2

attuazione in Swift

let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
let files = NSFileManager.defaultManager().contentsOfDirectoryAtPath(documentsPath, error: nil)

let filesAndProperties = NSMutableArray()
for file in files! {

    let filePath = documentsPath.stringByAppendingString(file as NSString)
    let properties = NSFileManager.defaultManager().attributesOfItemAtPath(filePath, error: nil)
    let modDate = properties![NSFileModificationDate] as NSDate
    filesAndProperties.addObject(NSDictionary(objectsAndKeys: file, "path", modDate, "lastModDate"))
}

let sortedFiles = filesAndProperties.sortedArrayUsingComparator({
    (path1, path2) -> NSComparisonResult in

    var comp = (path1.objectForKey("lastModDate") as NSDate).compare(path2.objectForKey("lastModDate") as NSDate)
    if comp == .OrderedDescending {

        comp = .OrderedAscending
    } else if comp == .OrderedAscending {

        comp = .OrderedDescending
    }

    return comp
})

2
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let dateData: String = dateFormatter.stringFromDate(date1)
let testDate: String = dateFormatter.stringFromDate(date2)
print(dateData == testDate)

1
someArray.sort({($0.dateAdded?.timeIntervalSinceReferenceDate)! < ($1.dateAdded?.timeIntervalSinceReferenceDate)!})

dateAdded è una variabile NSDate nel mio oggetto

class MyClass {
    let dateAdded: NSDate?
}

1

Abbiamo uno scenario per verificare che l'ora corrente si trovi due volte (due date). Ad esempio, voglio controllare l'attuale menzogna tra l'orario di apertura della clinica (ospedale) e l'orario di chiusura.

Usa il semplice codice.

      NSDate * now = [NSDate date];
        NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
        [outputFormatter setDateFormat:@"HH:mm:ss"];

        //current time
        NSString *currentTimeString = [outputFormatter stringFromDate:now];
        NSDate *dateCurrent = [outputFormatter dateFromString:currentTimeString];


        NSString *timeStart = @"09:00:00";
        NSString *timeEnd = @"22:00:00";

        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"HH:mm:ss"];

        NSDate *dateStart= [formatter timeStart];
        NSDate *dateEnd = [formatter timeEnd];
        NSComparisonResult result = [dateCurrent compare:dateStart];
        NSComparisonResult resultSecond = [date2 compare:dateEnd];

if(result == NSOrderedDescending && resultSecond == NSOrderedDescending)
        {
            NSLog(@"current time lies in starting and end time");
    }else {
            NSLog(@"current time doesn't lie in starting and end time");
        }

1

Per swift 3, è possibile utilizzare la funzione di seguito per confrontare tra due date.

func compareDate(dateInitial:Date, dateFinal:Date) -> Bool {
    let order = Calendar.current.compare(dateInitial, to: dateFinal, toGranularity: .day)
    switch order {
    case .orderedSame:
        return true
    default:
        return false
    }
}

toGranularity può essere modificato in base ai vincoli su cui si desidera applicare il confronto.


1

Estendere su SashaZ

Swift iOS 8 e versioni successive Quando hai bisogno di qualcosa di più di un semplice confronto di date più grandi o più piccole. Ad esempio, è lo stesso giorno o il giorno precedente, ...

Nota: non dimenticare mai il fuso orario. Il fuso orario di Calendar ha un valore predefinito, ma se non ti piace il valore predefinito, devi impostare tu stesso il fuso orario. Per sapere che giorno è, devi sapere in quale fuso orario stai chiedendo.

extension Date {
    func compareTo(date: Date, toGranularity: Calendar.Component ) -> ComparisonResult  {
        var cal = Calendar.current
        cal.timeZone = TimeZone(identifier: "Europe/Paris")!
        return cal.compare(self, to: date, toGranularity: toGranularity)
        }
    }

Usalo in questo modo:

if thisDate.compareTo(date: Date(), toGranularity: .day) == .orderedDescending {
// thisDate is a previous day
}

Di un esempio più complesso. Trova e filtra tutte le date in un array, che sono dello stesso giorno di "findThisDay":

let formatter = DateFormatter()
formatter.timeZone = TimeZone(identifier: "Europe/Paris")
formatter.dateFormat = "yyyy/MM/dd HH:mm:ss"

let findThisDay = formatter.date(from: "2018/11/05 08:11:08")!
_ = [
    formatter.date(from: "2018/12/05 08:08:08")!, 
    formatter.date(from: "2018/11/05 08:11:08")!,
    formatter.date(from: "2018/11/05 11:08:22")!,
    formatter.date(from: "2018/11/05 22:08:22")!,
    formatter.date(from: "2018/11/05 08:08:22")!,
    formatter.date(from: "2018/11/07 08:08:22")!,
    ]
    .filter{ findThisDay.compareTo(date: $0 , toGranularity: .day) == .orderedSame }
    .map { print(formatter.string(from: $0)) }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.