Jak sprawdzić, czy między dwoma innymi NSDate występuje data NSDate

91

Próbuję dowiedzieć się, czy aktualna data mieści się w zakresie dat za pomocą NSDate.

Na przykład możesz pobrać aktualną datę / godzinę za pomocą NSDate:

NSDate rightNow = [NSDate date];

Chciałbym wtedy wykorzystać tę datę, aby sprawdzić, czy mieści się w przedziale 9-17 .

Brock Woolf
źródło
1
Możesz spojrzeć na odpowiedzi na to pytanie, aby uzyskać więcej informacji na temat tworzenia obiektów NSDate dla określonego przedziału czasu, np. Od 17:00 do 21:00.
Marc Charbonneau

Odpowiedzi:

168

Wymyśliłem rozwiązanie. Jeśli masz lepsze rozwiązanie, możesz je zostawić, a zaznaczę je jako poprawne.

+ (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
źródło
3
Wygląda dobrze, ale możesz chcieć zmienić jej nazwę na mniej więcej taką: + (BOOL) date: (NSDate *) date isBetweenDate: (NSDate *) beginDate iDate: (NSDate *) endDate, aby uniknąć nieporozumień.
Jeff Kelley,
3
Kod golfa! Kompilator powinien zewrzeć porównanie, sprawiając, że wydajność będzie mniej więcej równa temu, co masz: return (([porównanie dat: beginDate]! = NSOrderedDescending) && ([porównanie dat: endDate]! = NSOrderedAscending));
Tim
1
Jeszcze lepiej, uczyniłbym to metodą instancji (dodatek do NSDate), a nie metodą klasową. Poszedłbym z tym: -isBetweenDate: andDate:
Dave Batton
4
Świetny! - (BOOL)isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate;
Utworzyłem
2
fajne rozwiązanie, możesz nawet sprawić, że będzie mniej gadatliwy przez zwarcie (i podstawową aplikację De Morgan.return !([date compare:startDate] == NSOrderedAscending || [date compare:endDate] == NSOrderedDescending);
Gabriele Petronella
13

W pierwszej części użyj odpowiedzi z @kperryua, aby skonstruować obiekty NSDate, z którymi chcesz porównać. Z Twojej odpowiedzi na własne pytanie wynika, że ​​już to rozgryzłeś.

Jeśli chodzi o porównanie dat, całkowicie zgadzam się z komentarzem @Tim dotyczącym Twojej odpowiedzi. Jest bardziej zwięzły, ale w rzeczywistości dokładnie odpowiada Twojemu kodowi i wyjaśnię dlaczego.

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

Chociaż może się wydawać, że instrukcja return musi oceniać oba operandy operatora &&, w rzeczywistości tak nie jest. Kluczem jest „ ocena zwarcia ”, która jest zaimplementowana w wielu różnych językach programowania, a na pewno w C. Zasadniczo operatory &i &&„zwarcie”, jeśli pierwszym argumentem jest 0 (lub NIE, zero, itp.) , while |i ||zrób to samo, jeśli pierwszy argument nie jest równy 0. Jeśli wystąpi datewcześniej beginDate, test zwraca NObez konieczności porównywania z endDate. Zasadniczo robi to samo, co twój kod, ale w pojedynczej instrukcji w jednym wierszu, a nie w 5 (lub 7, z białymi znakami).

Ma to być konstruktywne dane wejściowe, ponieważ gdy programiści rozumieją sposób, w jaki ich język programowania ocenia wyrażenia logiczne, mogą je konstruować bardziej efektywnie bez zbytniej wydajności. Istnieją jednak podobne testy, które byłyby mniej wydajne, ponieważ nie wszyscy operatorzy powodują zwarcie. (Rzeczywiście, większość nie może powodować zwarć, takich jak numeryczne operatory porównania.) W razie wątpliwości zawsze bezpiecznie jest rozbić logikę, ale kod może być znacznie bardziej czytelny, gdy pozwolisz językowi / kompilatorowi zająć się drobiazgami dla Was.

Quinn Taylor
źródło
1
Zrozumiale. Nie miałem pojęcia, że ​​Objective-C doprowadzi do zwarcia ocen. Wierzę na słowo, że tak. Dzięki za posprzątanie tego za mnie. Przypuszczam, że oznaczę cię jako poprawną, ponieważ twoja odpowiedź jest bardziej zwięzła niż moja ... i czegoś się nauczyłem :)
Brock Woolf.
Nie musisz wierzyć mi na słowo - opowiadam się za zasadą „ufaj, ale weryfikuj” (dzięki, Ronald Reagan!), Choćby po to, by zaspokoić moją ciekawość i poznać granice tego, kiedy coś działa, a kiedy nie. Możesz to zweryfikować, wykonując && dwóch metod, które rejestrują to, co zwracają po wywołaniu; zauważysz, że jeśli pierwsza zwróci 0, druga nigdy nie zostanie wywołana.
Quinn Taylor,
7

Wersja Brock Woolf w 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
źródło
4

Jeśli chcesz wiedzieć, czy bieżąca data przypada między dwoma podanymi punktami w czasie (9:00 - 17:00 dnia 7/1/09), użyj NSCalendar i NSDateComponents, aby zbudować instancje NSDate dla żądanych godzin i porównać je z bieżącą datą.

Jeśli chcesz wiedzieć, czy aktualna data przypada między tymi dwiema godzinami każdego dnia, prawdopodobnie możesz pójść w drugą stronę. Utwórz obiekt NSDateComponents z NSCalendar i NSDate i porównaj składniki godzinowe.

kperryua
źródło
4

Jeśli możesz kierować reklamy na system iOS 10.0 + / macOS 10.12+, użyj DateIntervalklasy.

Najpierw utwórz przedział dat z datą początkową i końcową:

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

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

Następnie sprawdź, czy data znajduje się w przedziale przy użyciu containsmetody DateInterval:

let dateIsInInterval: Bool = dateInterval.contains(middle) // true
Bryan Luby
źródło
3

Można to łatwo osiągnąć, korzystając z przedziałów czasowych dat, na przykład:

const NSTimeInterval i = [date timeIntervalSinceReferenceDate];
return ([startDate timeIntervalSinceReferenceDate] <= i &&
        [endDate timeIntervalSinceReferenceDate] >= i);
Justin
źródło
3

Kontynuując pracę z rozwiązaniami Quinna i Brocka, bardzo dobrze jest podklasować implementację NSDate, więc może być używana wszędzie w następujący sposób:

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

W dowolnej części kodu możesz go użyć jako:

[myNSDate isBetweenDate:thisNSDate andDate:thatNSDate];

(myNSDate, thisNSDate i thatNSDate to oczywiście NSDates :)

MiQUEL
źródło
3

Istnieje lepsze i szybsze rozwiązanie tego problemu.

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

Wtedy możesz to tak nazwać.

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

Nawet ty możesz przekazać datę losowo, ponieważ to rozszerzenie sprawdza, który z nich jest minimalny, a który nie.

możesz go używać w wersji Swift 3 i nowszych.

Rehan Ali
źródło
2

Dzięki Swift 5 możesz użyć jednego z dwóch poniższych rozwiązań, aby sprawdzić, czy data przypada między dwiema innymi datami.


# 1. Korzystanie DateIntervalz contains(_:)metody

DateIntervalma metodę o nazwie contains(_:). contains(_:)posiada następującą deklarację:

func contains(_ date: Date) -> Bool

Wskazuje, czy ten przedział zawiera podaną datę.

Poniższy kod Playground pokazuje, jak używać contains(_:), aby sprawdzić, czy data występuje między dwiema innymi datami:

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. Korzystanie ClosedRangez contains(_:)metody

ClosedRangema metodę o nazwie contains(_:). contains(_:)posiada następującą deklarację:

func contains(_ element: Bound) -> Bool

Zwraca wartość logiczną wskazującą, czy dany element znajduje się w zakresie.

Poniższy kod Playground pokazuje, jak używać contains(_:), aby sprawdzić, czy data występuje między dwiema innymi datami:

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
źródło
-1

Lepsza wersja w 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
źródło