So überprüfen Sie, ob ein NSDate zwischen zwei anderen NSDates auftritt

90

Ich versuche mit NSDate herauszufinden, ob das aktuelle Datum in einen Datumsbereich fällt oder nicht.

Beispielsweise können Sie mit NSDate das aktuelle Datum und die aktuelle Uhrzeit abrufen:

NSDate rightNow = [NSDate date];

Ich möchte dieses Datum dann verwenden, um zu überprüfen, ob es im Bereich von 9.00 bis 17.00 Uhr liegt .

Brock Woolf
quelle
1
In den Antworten auf diese Frage finden Sie weitere Informationen zum Erstellen von NSDate-Objekten für einen bestimmten Zeitraum, z. B. 17 bis 21 Uhr.
Marc Charbonneau

Antworten:

167

Ich habe eine Lösung gefunden. Wenn Sie eine bessere Lösung haben, können Sie diese gerne verlassen, und ich werde sie als korrekt markieren.

+ (BOOL)date:(NSDate*)date isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate
{
    if ([date compare:beginDate] == NSOrderedAscending)
        return NO;

    if ([date compare:endDate] == NSOrderedDescending) 
        return NO;

    return YES;
}
Brock Woolf
quelle
3
Das sieht gut aus, aber Sie können es wie folgt umbenennen: + (BOOL) Datum: (NSDate *) Datum isBetweenDate: (NSDate *) beginDate undDate: (NSDate *) endDate, um Verwirrung zu vermeiden.
Jeff Kelley
3
Code Golf! Der Compiler sollte den Vergleich kurzschließen, sodass die Effizienz ungefähr der von Ihnen entspricht: return (([Datumsvergleich: beginDate]! = NSOrderedDescending) && ([Datumsvergleich: endDate]! = NSOrderedAscending));
Tim
1
Besser noch, ich würde dies zu einer Instanzmethode (eine Ergänzung zu NSDate) machen, nicht zu einer Klassenmethode. Ich würde mit diesem gehen: -isBetweenDate: andDate:
Dave Batton
4
Ausgezeichnet! Ich habe eine Kategorie daraus gemacht:- (BOOL)isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate;
Matteo Alessani
2
schöne Lösung, Sie können es sogar durch Kurzschluss (und eine grundlegende De Morgan-Anwendung) weniger ausführlich machen.return !([date compare:startDate] == NSOrderedAscending || [date compare:endDate] == NSOrderedDescending);
Gabriele Petronella
13

Verwenden Sie im ersten Teil die Antwort von @kperryua , um die NSDate-Objekte zu erstellen, mit denen Sie vergleichen möchten. Aus Ihrer Antwort auf Ihre eigene Frage geht hervor, dass Sie das herausgefunden haben.

Für den tatsächlichen Vergleich der Daten stimme ich dem Kommentar von @Tim zu Ihrer Antwort voll und ganz zu. Es ist prägnanter und entspricht genau Ihrem Code, und ich werde erklären, warum.

+ (BOOL) date:(NSDate*)date isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate {
    return (([date compare:beginDate] != NSOrderedAscending) && ([date compare:endDate] != NSOrderedDescending));
}

Obwohl es den Anschein haben mag, dass die return-Anweisung beide Operanden des Operators && auswerten muss, ist dies tatsächlich nicht der Fall. Der Schlüssel ist " Kurzschlussauswertung ", die in einer Vielzahl von Programmiersprachen und sicherlich in C implementiert ist. Grundsätzlich die Operatoren &und &&"Kurzschluss", wenn das erste Argument 0 ist (oder NO, Null usw.) , während |und ||das gleiche tun , wenn das erste Argument ist nicht 0. wenn datevor kommt beginDate, die Test kehrt , NOohne auch nur mit zu vergleichen brauchen endDate. Grundsätzlich funktioniert es genauso wie Ihr Code, jedoch in einer einzelnen Anweisung in einer Zeile, nicht in 5 (oder 7 mit Leerzeichen).

Dies ist als konstruktive Eingabe gedacht, da Programmierer, die verstehen, wie ihre jeweilige Programmiersprache logische Ausdrücke bewertet, diese effektiver konstruieren können, ohne so viel über Effizienz zu wissen. Es gibt jedoch ähnliche Tests, die weniger effizient wären , da nicht alle Bediener kurzschließen. (In der Tat können die meisten nicht kurzschließen, z. B. numerische Vergleichsoperatoren.) Im Zweifelsfall ist es immer sicher, Ihre Logik explizit auseinanderzubrechen, aber Code kann viel besser lesbar sein, wenn Sie die Sprache / den Compiler die kleinen Dinge erledigen lassen für dich.

Quinn Taylor
quelle
1
Verständlich. Ich hatte keine Ahnung, dass Objective-C Bewertungen kurzschließen würde. Ich werde dein Wort darauf nehmen, dass es tut. Danke, dass du das für mich geklärt hast. Ich nehme an, ich werde dich als richtig markieren, da deine Antwort prägnanter ist als meine eigene ... und ich etwas gelernt habe :)
Brock Woolf
Ich brauche nicht mein Wort dafür zu nehmen - ich befürworte das Prinzip "Vertrauen, aber verifizieren" (danke, Ronald Reagan!), Wenn auch nur, um meine Neugier zu befriedigen und die Grenzen zu lernen, wann etwas funktioniert und wann nicht. Sie können dies überprüfen, indem Sie ein && von zwei Methoden ausführen, die protokollieren, was sie beim Aufruf zurückgeben. Sie werden feststellen, dass der zweite niemals aufgerufen wird, wenn der erste 0 zurückgibt.
Quinn Taylor
7

Brock Woolf Version in Swift:

extension NSDate
{
    func isBetweenDates(beginDate: NSDate, endDate: NSDate) -> Bool
    {
        if self.compare(beginDate) == .OrderedAscending
        {
            return false
        }

        if self.compare(endDate) == .OrderedDescending
        {
            return false
        }

        return true
    }
}
ChikabuZ
quelle
4

Wenn Sie wissen möchten, ob das aktuelle Datum zwischen zwei bestimmten Zeitpunkten liegt (9.00 bis 17.00 Uhr am 01.07.09), erstellen Sie mit NSCalendar und NSDateComponents NSDate-Instanzen für die gewünschten Zeiten und vergleichen Sie sie mit dem aktuellen Datum.

Wenn Sie , wenn das aktuelle Datum zwischen diesen 2 Stunden fällt wissen wollen , jeden Tag, dann könnte man wahrscheinlich in die andere Richtung gehen. Erstellen Sie ein NSDateComponents-Objekt mit und NSCalendar und Ihrem NSDate und vergleichen Sie die Stundenkomponenten.

kperryua
quelle
4

Wenn Sie auf iOS 10.0 + / macOS 10.12+ abzielen können, verwenden Sie die DateIntervalKlasse.

Erstellen Sie zunächst ein Datumsintervall mit einem Start- und Enddatum:

let start: Date = Date()
let middle: Date = Date()
let end: Date = Date()

let dateInterval: DateInterval = DateInterval(start: start, end: end)

Überprüfen Sie dann mithilfe der folgenden containsMethode , ob das Datum im Intervall liegt DateInterval:

let dateIsInInterval: Bool = dateInterval.contains(middle) // true
Bryan Luby
quelle
3

Dies kann einfach mithilfe der Zeitintervalle der Daten erreicht werden, wie folgt:

const NSTimeInterval i = [date timeIntervalSinceReferenceDate];
return ([startDate timeIntervalSinceReferenceDate] <= i &&
        [endDate timeIntervalSinceReferenceDate] >= i);
Justin
quelle
3

Die Fortsetzung der Lösungen von Quinn und Brock ist sehr hilfreich, um die NSDate-Implementierung in Unterklassen zu unterteilen, sodass sie überall wie folgt verwendet werden kann:

-(BOOL) isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate {
    return (([self compare:beginDate] != NSOrderedAscending) && ([self compare:endDate] != NSOrderedDescending));
}

Und an jedem Teil Ihres Codes können Sie ihn verwenden als:

[myNSDate isBetweenDate:thisNSDate andDate:thatNSDate];

(myNSDate, thisNSDate und thatNSDate sind natürlich NSDates :)

MiQUEL
quelle
3

Es gibt eine bessere und schnellere Lösung für dieses Problem.

extention Date {
    func isBetween(from startDate: Date,to endDate: Date) -> Bool {
        let result = (min(startDate, endDate) ... max(startDate, endDate)).contains(self)
        return result
    }
}

Dann können Sie es so nennen.

todayDate.isBetween(from: startDate, to: endDate)

Sogar Sie können das Datum zufällig übergeben, da diese Erweiterung prüft, welches Minimum ist und welches nicht.

Sie können es in Swift 3 und höher verwenden.

Rehan Ali
quelle
2

Mit Swift 5 können Sie eine der beiden folgenden Lösungen verwenden, um zu überprüfen, ob ein Datum zwischen zwei anderen Daten liegt.


# 1. Mit DateIntervalder contains(_:)Methode von

DateIntervalhat eine Methode namens contains(_:). contains(_:)hat die folgende Erklärung:

func contains(_ date: Date) -> Bool

Gibt an, ob dieses Intervall das angegebene Datum enthält.

Der folgende Spielplatzcode zeigt, wie Sie contains(_:)überprüfen können, ob ein Datum zwischen zwei anderen Daten liegt:

import Foundation

let calendar = Calendar.current
let startDate = calendar.date(from: DateComponents(year: 2010, month: 11, day: 22))!
let endDate = calendar.date(from: DateComponents(year: 2015, month: 5, day: 1))!
let myDate = calendar.date(from: DateComponents(year: 2012, month: 8, day: 15))!

let dateInterval = DateInterval(start: startDate, end: endDate)
let result = dateInterval.contains(myDate)
print(result) // prints: true

# 2. Mit ClosedRangeder contains(_:)Methode von

ClosedRangehat eine Methode namens contains(_:). contains(_:)hat die folgende Erklärung:

func contains(_ element: Bound) -> Bool

Gibt einen booleschen Wert zurück, der angibt, ob das angegebene Element im Bereich enthalten ist.

Der folgende Spielplatzcode zeigt, wie Sie contains(_:)überprüfen können, ob ein Datum zwischen zwei anderen Daten liegt:

import Foundation

let calendar = Calendar.current
let startDate = calendar.date(from: DateComponents(year: 2010, month: 11, day: 22))!
let endDate = calendar.date(from: DateComponents(year: 2015, month: 5, day: 1))!
let myDate = calendar.date(from: DateComponents(year: 2012, month: 8, day: 15))!

let range = startDate ... endDate
let result = range.contains(myDate)
//let result = range ~= myDate // also works
print(result) // prints: true
Imanou Petit
quelle
-1

Eine bessere Version in Swift:

@objc public class DateRange: NSObject {
    let startDate: Date
    let endDate: Date

    init(startDate: Date, endDate: Date) {
        self.startDate = startDate
        self.endDate = endDate
    }

    @objc(containsDate:)
    func contains(_ date: Date) -> Bool {
        let startDateOrder = date.compare(startDate)
        let endDateOrder = date.compare(endDate)
        let validStartDate = startDateOrder == .orderedAscending || startDateOrder == .orderedSame
        let validEndDate = endDateOrder == .orderedDescending || endDateOrder == .orderedSame
        return validStartDate && validEndDate
    }
}
TheCodingArt
quelle