Cómo comprobar si se produce una NSDate entre otras dos NSDates

90

Estoy tratando de averiguar si la fecha actual se encuentra dentro de un rango de fechas usando NSDate.

Por ejemplo, puede obtener la fecha / hora actual usando NSDate:

NSDate rightNow = [NSDate date];

Después me gustaría utilizar esa fecha para comprobar si está en el rango de 09 a.m.-5 p.m. .

Brock Woolf
fuente
1
Puede echar un vistazo a las respuestas a esta pregunta para obtener más información sobre cómo crear objetos NSDate para un período de tiempo específico, como de 5 p.m. a 9 p.m.
Marc Charbonneau

Respuestas:

167

Se me ocurrió una solución. Si tiene una solución mejor, no dude en dejarla y la marcaré como correcta.

+ (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
fuente
3
Eso se ve bien, pero es posible que desee cambiarle el nombre de la siguiente manera: + (BOOL) date: (NSDate *) date isBetweenDate: (NSDate *) beginDate andDate: (NSDate *) endDate para evitar confusiones.
Jeff Kelley
3
Código de golf! El compilador debería cortocircuitar la comparación, haciendo que la eficiencia sea aproximadamente igual a lo que tiene: return (([date compare: beginDate]! = NSOrderedDescending) && ([date compare: endDate]! = NSOrderedAscending));
Tim
1
Mejor aún, haría de esto un método de instancia (una adición a NSDate), no un método de clase. Yo iría con esto: -isBetweenDate: andDate:
Dave Batton
4
¡Excelente! Hice una categoría de esto:- (BOOL)isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate;
Matteo Alessani
2
buena solución, incluso puede hacerlo menos detallado por cortocircuito (y una aplicación básica de De Morgan.return !([date compare:startDate] == NSOrderedAscending || [date compare:endDate] == NSOrderedDescending);
Gabriele Petronella
13

Para la primera parte, use la respuesta de @kperryua para construir los objetos NSDate con los que desea comparar. De su respuesta a su propia pregunta, parece que ya lo ha resuelto.

Para comparar las fechas, estoy totalmente de acuerdo con el comentario de @Tim sobre su respuesta. Es más conciso pero en realidad es exactamente equivalente a su código, y explicaré por qué.

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

Aunque pueda parecer que la declaración de retorno debe evaluar ambos operandos del operador &&, este no es el caso. La clave es " evaluación de cortocircuito ", que se implementa en una amplia variedad de lenguajes de programación, y ciertamente en C. Básicamente, los operadores &y &&"cortocircuito" si el primer argumento es 0 (o NO, nil, etc.) , while |y ||hacen lo mismo si el primer argumento no es 0. Si dateviene antes beginDate, la prueba devuelve NOsin necesidad de comparar con endDate. Básicamente, hace lo mismo que su código, pero en una sola declaración en una línea, no 5 (o 7, con espacios en blanco).

Esto tiene la intención de ser una entrada constructiva, ya que cuando los programadores comprenden la forma en que su lenguaje de programación particular evalúa las expresiones lógicas, pueden construirlas de manera más efectiva sin tanto sobre la eficiencia. Sin embargo, existen pruebas similares que serían menos eficientes, ya que no todos los operadores cortocircuitan. (De hecho, la mayoría no puede hacer cortocircuitos, como los operadores de comparación numérica). En caso de duda, siempre es seguro ser explícito al dividir su lógica, pero el código puede ser mucho más legible cuando deja que el lenguaje / compilador maneje las pequeñas cosas para ti.

Quinn Taylor
fuente
1
Comprensible. No tenía idea de que Objective-C provocaría un cortocircuito en las evaluaciones. Confiaré en su palabra de que lo hace. Gracias por aclarar eso para mí. Supongo que lo marcaré como correcto ya que su respuesta es más concisa que la mía ... y aprendí algo :)
Brock Woolf
No es necesario que confíe en mi palabra: defiendo el principio "confía, pero verifica" (¡gracias, Ronald Reagan!) Aunque sólo sea para satisfacer mi curiosidad y conocer los límites de cuándo algo funciona y no funciona. Puede verificarlo haciendo un && de dos métodos que registran lo que devuelven cuando se llaman; notará que si el primero devuelve 0, nunca se llamará al segundo.
Quinn Taylor
7

Versión de Brock Woolf en 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
fuente
4

Si desea saber si la fecha actual se encuentra entre dos momentos determinados (de 9 a. M. A 5 p. M. El 1 de julio de 2009), use NSCalendar y NSDateComponents para crear instancias de NSDate para los tiempos deseados y compárelas con la fecha actual.

Si desea saber si la fecha actual se encuentra entre estas dos horas todos los días, probablemente podría ir al revés. Cree un objeto NSDateComponents con y NSCalendar y su NSDate y compare los componentes de la hora.

kperryua
fuente
4

Si puede orientar sus anuncios a iOS 10.0 + / macOS 10.12+, utilice la DateIntervalclase.

Primero, cree un intervalo de fechas con una fecha de inicio y finalización:

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

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

Luego, verifique si la fecha está en el intervalo usando el containsmétodo de DateInterval:

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

Esto se puede lograr fácilmente usando los intervalos de tiempo de las fechas, así:

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

Continuando con las soluciones de Quinn y Brock, es muy bueno subclasificar la implementación de NSDate, por lo que se puede usar en todas partes de esta manera:

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

Y en cualquier parte de su código puede usarlo como:

[myNSDate isBetweenDate:thisNSDate andDate:thatNSDate];

(myNSDate, thisNSDate y thatNSDate son, por supuesto, NSDates :)

MiQUEL
fuente
3

Existe una solución mejor y más rápida para este problema.

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

Entonces puedes llamarlo así.

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

Incluso puede pasar la fecha al azar, ya que esta extensión comprueba cuál es mínima y cuál no.

puedes usarlo en swift 3 y superior.

Rehan Ali
fuente
2

Con Swift 5, puede usar una de las dos soluciones a continuación para verificar si ocurre una fecha entre otras dos fechas.


# 1. Usando DateIntervalel contains(_:)método de

DateIntervaltiene un método llamado contains(_:). contains(_:)tiene la siguiente declaración:

func contains(_ date: Date) -> Bool

Indica si este intervalo contiene la fecha dada.

El siguiente código de Playground muestra cómo usarlo contains(_:)para verificar si una fecha ocurre entre otras dos fechas:

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. Usando ClosedRangeel contains(_:)método de

ClosedRangetiene un método llamado contains(_:). contains(_:)tiene la siguiente declaración:

func contains(_ element: Bound) -> Bool

Devuelve un valor booleano que indica si el elemento dado está contenido dentro del rango.

El siguiente código de Playground muestra cómo usarlo contains(_:)para verificar si una fecha ocurre entre otras dos fechas:

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
fuente
-1

Una mejor versión en 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
    }
}
El Arte De Codificación
fuente