Comment vérifier si un NSDate se produit entre deux autres NSDates

90

J'essaie de déterminer si la date actuelle tombe ou non dans une plage de dates en utilisant NSDate.

Par exemple, vous pouvez obtenir la date / heure actuelle à l'aide de NSDate:

NSDate rightNow = [NSDate date];

Je voudrais alors utiliser cette date pour vérifier si elle est comprise entre 9 h et 17 h .

Brock Woolf
la source
1
Vous pouvez consulter les réponses à cette question pour plus d'informations sur la façon de créer des objets NSDate pour une période de temps spécifique, telle que 17h-21h.
Marc Charbonneau

Réponses:

167

J'ai trouvé une solution. Si vous avez une meilleure solution, n'hésitez pas à la laisser et je la marquerai comme correcte.

+ (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
la source
3
Cela a l'air bien, mais vous voudrez peut-être le renommer comme suit: + (BOOL) date: (NSDate *) date isBetweenDate: (NSDate *) beginDate andDate: (NSDate *) endDate afin d'éviter toute confusion.
Jeff Kelley
3
Code golf! Le compilateur doit court-circuiter la comparaison, rendant l'efficacité à peu près égale à ce que vous avez: return (([date compare: beginDate]! = NSOrderedDescending) && ([date compare: endDate]! = NSOrderedAscending));
Tim
1
Mieux encore, j'en ferais une méthode d'instance (un ajout à NSDate), pas une méthode de classe. J'irais avec ceci: -isBetweenDate: andDate:
Dave Batton
4
Excellent! J'ai fait une catégorie à partir de ceci:- (BOOL)isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate;
Matteo Alessani
2
belle solution, vous pouvez même la rendre moins verbeuse par court-circuit (et une application de base De Morgan.return !([date compare:startDate] == NSOrderedAscending || [date compare:endDate] == NSOrderedDescending);
Gabriele Petronella
13

Pour la première partie, utilisez la réponse de @kperryua pour construire les objets NSDate avec lesquels vous souhaitez comparer. D'après votre réponse à votre propre question, il semble que vous ayez compris cela.

Pour comparer les dates, je suis totalement d'accord avec le commentaire de @Tim sur votre réponse. C'est plus concis mais en fait exactement équivalent à votre code, et je vais vous expliquer pourquoi.

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

Bien qu'il puisse sembler que l'instruction return doive évaluer les deux opérandes de l'opérateur &&, ce n'est en fait pas le cas. La clé est "l' évaluation de court-circuit ", qui est implémentée dans une grande variété de langages de programmation, et certainement en C. Fondamentalement, les opérateurs &et &&"court-circuit" si le premier argument est 0 (ou NON, nul, etc.) , tandis que| et ||faites de même si le premier argument n'est pas 0. Si datevient avant beginDate, le test retourne NOsans même avoir besoin de comparer avec endDate. Fondamentalement, il fait la même chose que votre code, mais en une seule instruction sur une ligne, pas 5 (ou 7, avec un espace).

Ceci est conçu comme une entrée constructive, car lorsque les programmeurs comprennent la façon dont leur langage de programmation particulier évalue les expressions logiques, ils peuvent les construire plus efficacement sans trop d'efficience. Cependant, il existe des tests similaires qui seraient moins efficaces, car tous les opérateurs ne court-circuitent pas. (En effet, la plupart ne peuvent pas court-circuiter, comme les opérateurs de comparaison numérique.) En cas de doute, il est toujours prudent d'être explicite en décomposant votre logique, mais le code peut être beaucoup plus lisible lorsque vous laissez le langage / compilateur gérer les petites choses pour vous.

Quinn Taylor
la source
1
Compréhensible. Je n'avais aucune idée que Objective-C court-circuiterait les évaluations. Je vous crois sur parole. Merci d'avoir clarifié cela pour moi. Je suppose que je vais vous marquer comme correct car votre réponse est plus concise que la mienne ... et j'ai appris quelque chose :)
Brock Woolf
Inutile de me croire sur parole - je préconise le principe «confiance, mais vérification» (merci, Ronald Reagan!) Ne serait-ce que pour satisfaire ma curiosité et apprendre les limites de quand quelque chose fonctionne et ne fonctionne pas. Vous pouvez le vérifier en faisant une && de deux méthodes qui enregistrent ce qu'elles renvoient lorsqu'elles sont appelées; vous remarquerez que si le premier renvoie 0, le second ne sera jamais appelé.
Quinn Taylor
7

Version Brock Woolf dans 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
la source
4

Si vous voulez savoir si la date actuelle se situe entre deux moments donnés (9 h 00 - 17 h 00 le 01/07/09), utilisez NSCalendar et NSDateComponents pour créer des instances NSDate aux heures souhaitées et les comparer avec la date actuelle.

Si vous voulez savoir si la date actuelle se situe entre ces deux heures chaque jour, vous pourriez probablement aller dans l'autre sens. Créez un objet NSDateComponents avec et NSCalendar et votre NSDate et comparez les composants d'heure.

Kperryua
la source
4

Si vous pouvez cibler iOS 10.0 + / macOS 10.12+, utilisez la DateIntervalclasse.

Tout d'abord, créez un intervalle de dates avec une date de début et de fin:

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

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

Ensuite, vérifiez si la date est dans l'intervalle en utilisant la containsméthode de DateInterval:

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

Cela peut être accompli facilement en utilisant les intervalles de temps des dates, comme ceci:

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

Poursuivre avec les solutions de Quinn et Brock, il est très agréable de sous-classer l'implémentation de NSDate, donc il peut être utilisé partout comme ceci:

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

Et à n'importe quelle partie de votre code, vous pouvez l'utiliser comme:

[myNSDate isBetweenDate:thisNSDate andDate:thatNSDate];

(myNSDate, thisNSDate et thatNSDate sont bien sûr NSDates :)

MiQUEL
la source
3

Il existe une solution meilleure et plus rapide à ce problème.

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

Ensuite, vous pouvez l'appeler comme ça.

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

Même vous pouvez passer la date au hasard car cette extension vérifie laquelle est minimale et laquelle ne l'est pas.

vous pouvez l'utiliser dans swift 3 et au-dessus.

Rehan Ali
la source
2

Avec Swift 5, vous pouvez utiliser l'une des deux solutions ci-dessous afin de vérifier si une date survient entre deux autres dates.


#1. Utilisation de DateIntervalla contains(_:)méthode de

DateIntervala une méthode appelée contains(_:). contains(_:)a la déclaration suivante:

func contains(_ date: Date) -> Bool

Indique si cet intervalle contient la date donnée.

Le code Playground suivant montre comment utiliser contains(_:)pour vérifier si une date se produit entre deux autres dates:

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. Utilisation de ClosedRangela contains(_:)méthode de

ClosedRangea une méthode appelée contains(_:). contains(_:)a la déclaration suivante:

func contains(_ element: Bound) -> Bool

Renvoie une valeur booléenne indiquant si l'élément donné est contenu dans la plage.

Le code Playground suivant montre comment utiliser contains(_:)pour vérifier si une date se produit entre deux autres dates:

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
la source
-1

Une meilleure version dans 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
la source