Come verificare se si verifica un NSDate tra altri due NSDate


90

Sto cercando di capire se la data corrente rientra o meno in un intervallo di date utilizzando NSDate.

Ad esempio, puoi ottenere la data / ora corrente utilizzando NSDate:

NSDate rightNow = [NSDate date];

Vorrei quindi utilizzare quella data per verificare se è compresa tra le 9:00 e le 17:00 .


1
Puoi dare un'occhiata alle risposte a questa domanda per ulteriori informazioni su come creare oggetti NSDate per un periodo di tempo specifico, ad esempio dalle 17:00 alle 21:00.
Marc Charbonneau

Risposte:


167

Ho trovato una soluzione. Se hai una soluzione migliore, sentiti libero di lasciarla e la contrassegnerò come corretta.

+ (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;
}

3
Sembra buono, ma potresti volerlo rinominare in questo modo: + (BOOL) date: (NSDate *) date isBetweenDate: (NSDate *) beginDate andDate: (NSDate *) endDate per evitare confusione.
Jeff Kelley

3
Codice di golf! Il compilatore dovrebbe cortocircuitare il confronto, rendendo l'efficienza più o meno uguale a quella che hai: return (([date compare: beginDate]! = NSOrderedDescending) && ([date compare: endDate]! = NSOrderedAscending));
Tim

1
Meglio ancora, lo renderei un metodo di istanza (un'aggiunta a NSDate), non un metodo di classe. Andrei con questo: -isBetweenDate: andDate:
Dave Batton

4
Eccellente! Ho fatto una Categoria da questa:- (BOOL)isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate;
Matteo Alessani

2
bella soluzione, puoi anche renderlo meno prolisso con un cortocircuito (e un'applicazione base De Morgan.return !([date compare:startDate] == NSOrderedAscending || [date compare:endDate] == NSOrderedDescending);
Gabriele Petronella

13

Per la prima parte, usa la risposta di @kperryua per costruire gli oggetti NSDate con cui vuoi confrontare. Dalla tua risposta alla tua stessa domanda, sembra che tu l'abbia capito.

Per confrontare effettivamente le date, sono totalmente d'accordo il commento di @Tim sulla tua risposta. È più conciso ma in realtà esattamente equivalente al tuo codice e spiegherò perché.

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

Sebbene possa sembrare che l'istruzione return debba valutare entrambi gli operandi dell'operatore &&, in realtà non è così. La chiave è la " valutazione di cortocircuito ", che è implementata in un'ampia varietà di linguaggi di programmazione, e certamente in C. Fondamentalmente, gli operatori &e &&"cortocircuito" se il primo argomento è 0 (o NO, nil, ecc.) , mentre |e ||fai lo stesso se il primo argomento non è 0. Se dateviene prima beginDate, il test ritorna NOsenza nemmeno bisogno di confrontare conendDate . Fondamentalmente, fa la stessa cosa del tuo codice, ma in una singola istruzione su una riga, non 5 (o 7, con spazi bianchi).

Questo è inteso come input costruttivo, poiché quando i programmatori comprendono il modo in cui il loro particolare linguaggio di programmazione valuta le espressioni logiche, possono costruirle in modo più efficace senza troppa efficienza. Tuttavia, ci sono test simili che sarebbero meno efficienti, poiché non tutti gli operatori vanno in cortocircuito. (In effetti, la maggior parte non può cortocircuitare, come gli operatori di confronto numerico.) In caso di dubbio, è sempre sicuro essere espliciti nel rompere la logica, ma il codice può essere molto più leggibile quando si lascia che il linguaggio / compilatore gestisca le piccole cose per te.


1
Comprensibile. Non avevo idea che Objective-C avrebbe cortocircuitato le valutazioni. Ti credo sulla parola che lo fa. Grazie per avermi chiarito la cosa. Suppongo che ti contrassegnerò come corretto poiché la tua risposta è più concisa della mia ... e ho imparato qualcosa :)
Brock Woolf

Non c'è bisogno di credermi sulla parola - sostengo il principio "fidati, ma verifica" (grazie, Ronald Reagan!) Se non altro per soddisfare la mia curiosità e imparare i limiti di quando qualcosa funziona e non funziona. Puoi verificarlo eseguendo un && di due metodi che registrano ciò che restituiscono quando vengono chiamati; noterai che se il primo restituisce 0, il secondo non verrà mai chiamato.
Quinn Taylor

7

Versione di Brock Woolf 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
    }
}

4

Se vuoi sapere se la data corrente cade tra due punti nel tempo (9: 00-17: 00 del 7/1/09), utilizza NSCalendar e NSDateComponents per creare istanze di NSDate per gli orari desiderati e confrontarli con la data corrente.

Se vuoi sapere se la data corrente cade ogni giorno tra queste due ore , allora potresti probabilmente andare dall'altra parte. Crea un oggetto NSDateComponents con e NSCalendar e il tuo NSDate e confronta i componenti orari.


4

Se puoi scegliere come target iOS 10.0 + / macOS 10.12+, usa l'estensione DateInterval classe.

Innanzitutto, crea un intervallo di date con una data di inizio e di fine:

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

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

Quindi, controlla se la data è nell'intervallo utilizzando il containsmetodo di DateInterval:

let dateIsInInterval: Bool = dateInterval.contains(middle) // true

3

Questo può essere ottenuto facilmente utilizzando gli intervalli di tempo delle date, in questo modo:

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

3

Continuando con le soluzioni di Quinn e Brock, è molto bello sottoclassare l'implementazione di NSDate, quindi può essere utilizzato ovunque in questo modo:

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

E in qualsiasi parte del tuo codice puoi usarlo come:

[myNSDate isBetweenDate:thisNSDate andDate:thatNSDate];

(myNSDate, thisNSDate e thatNSDate sono ovviamente NSDates :)


3

C'è una soluzione migliore e più rapida per questo problema.

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

Allora puoi chiamarlo così.

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

Anche tu puoi passare la data in modo casuale poiché questa estensione controlla quale è minima e quale no.

puoi usarlo in swift 3 e versioni successive.


2

Con Swift 5, puoi utilizzare una delle due soluzioni seguenti per verificare se si verifica una data tra due altre date.


# 1. Utilizzando DateIntervalil contains(_:)metodo di

DateIntervalha un metodo chiamato contains(_:). contains(_:)ha la seguente dichiarazione:

func contains(_ date: Date) -> Bool

Indica se questo intervallo contiene la data specificata.

Il seguente codice Playground mostra come utilizzare contains(_:)per verificare se si verifica una data tra due altre date:

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. Utilizzando ClosedRangeil contains(_:)metodo di

ClosedRangeha un metodo chiamato contains(_:). contains(_:)ha la seguente dichiarazione:

func contains(_ element: Bound) -> Bool

Restituisce un valore booleano che indica se l'elemento specificato è contenuto nell'intervallo.

Il seguente codice Playground mostra come utilizzare contains(_:)per verificare se si verifica una data tra due altre date:

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

-1

Una versione migliore 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
    }
}
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.