Comparación de NSDate usando Swift

153

Estoy trabajando en una aplicación que requiere verificar la fecha de vencimiento de la tarea. Quiero saber si una fecha de vencimiento es dentro de la próxima semana, y si es así, realice una acción.
La mayor parte de la documentación que pude encontrar está en Objective-C y no puedo entender cómo hacerlo en Swift. ¡¡Gracias por la ayuda!!

Henry oscannlain-miller
fuente
2
swift no tiene una clase de fecha que utiliza la clase NSDate de Objective C, por lo que ha encontrado la documentación correcta
mmmmmm
Posible duplicado de Comparar NSDates sin componente de tiempo . Hay muchas respuestas muy buenas.
jww
Respuesta relacionada: stackoverflow.com/questions/29652771/…
jkdev
2
Swift 3 tiene una Dateclase. Tiene un puente NSDate, pero se llama Date.
BallpointBen

Respuestas:

188

Me gusta usar extensiones para hacer que el código sea más legible. Aquí hay algunas extensiones NSDate que pueden ayudarlo a limpiar su código y facilitar su comprensión. Puse esto en un archivo sharedCode.swift:

extension NSDate {

    func isGreaterThanDate(dateToCompare: NSDate) -> Bool {
        //Declare Variables
        var isGreater = false

        //Compare Values
        if self.compare(dateToCompare as Date) == ComparisonResult.orderedDescending {
            isGreater = true
        }

        //Return Result
        return isGreater
    }

    func isLessThanDate(dateToCompare: NSDate) -> Bool {
        //Declare Variables
        var isLess = false

        //Compare Values
        if self.compare(dateToCompare as Date) == ComparisonResult.orderedAscending {
            isLess = true
        }

        //Return Result
        return isLess
    }

    func equalToDate(dateToCompare: NSDate) -> Bool {
        //Declare Variables
        var isEqualTo = false

        //Compare Values
        if self.compare(dateToCompare as Date) == ComparisonResult.orderedSame {
            isEqualTo = true
        }

        //Return Result
        return isEqualTo
    }

    func addDays(daysToAdd: Int) -> NSDate {
        let secondsInDays: TimeInterval = Double(daysToAdd) * 60 * 60 * 24
        let dateWithDaysAdded: NSDate = self.addingTimeInterval(secondsInDays)

        //Return Result
        return dateWithDaysAdded
    }

    func addHours(hoursToAdd: Int) -> NSDate {
        let secondsInHours: TimeInterval = Double(hoursToAdd) * 60 * 60
        let dateWithHoursAdded: NSDate = self.addingTimeInterval(secondsInHours)

        //Return Result
        return dateWithHoursAdded
    }
}

Ahora si puedes hacer algo como esto:

//Get Current Date/Time
var currentDateTime = NSDate()

//Get Reminder Date (which is Due date minus 7 days lets say)
var reminderDate = dueDate.addDays(-7)

//Check if reminderDate is Greater than Right now
if(reminderDate.isGreaterThanDate(currentDateTime)) {
    //Do Something...
}
usuario2266987
fuente
28
Deberías simplificar tu código. return self.compare(dateToCompare) == NSComparisonResult.OrderedDescending
Olav Gausaker
55
isEqualToDate también es proporcionado por Apple. Su declaración entra en conflicto con la definida por Apple.
Shamas S - Restablece a Monica el
44
No todos los días tiene 24 horas
Leo Dabus
9
Esta respuesta es terrible y nunca debe ser la aceptada. No siempre añadir intervalos de tiempo a las fechas que son creados por ti. Eso es precisamente por qué NSDateComponentsexiste. Hay una gran cantidad de casos extremos que no están siendo adecuadamente mango y no tiene sentido de no añadir la conformidad con Comparablea NSDate. Recomiendo usar la solución de John .
fpg1503
3
Una mejor solución es hacer NSDate Equatable, Comparable, entonces simplemente podría hacerlodate1 < date2
aryaxt
209

Si quieres apoyar ==, <, >, <=, o >=para NSDates, sólo tiene que declarar esto en alguna parte:

public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs === rhs || lhs.compare(rhs) == .OrderedSame
}

public func <(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.compare(rhs) == .OrderedAscending
}

extension NSDate: Comparable { }
John Estropia
fuente
2
@Isuru Comparablees un descendiente del Equatableprotocolo, por lo que no debería tener que declarar conformidad con ambos.
John Estropia
2
¡¿Por qué no está integrado por defecto ?!
dVaffection
3
@dVaffection En Objective-C (donde se declaran NSDate y amigos), si se compara usando ==, <, >, etc, que voy a recibir un resultado de la comparación de su dirección en la memoria, no la comparación de su valor real. En Swift, todavía se tratan como referencias, por lo que creo que la elección fue (1) mantener las comparaciones por puntero como están en ObjC, o (2) eliminar la confusión al no proporcionar una implementación para las comparaciones.
John Estropia
2
Un beneficio adicional de este enfoque es que Array.maxElement(), etc., está disponible automáticamente de las matrices de NSDates.
pr1001
1
@MarcioCruz Eso es solo un requisito de Swift de que todas las implementaciones de los operadores deben tener un alcance global. Vea la discusión aquí: stackoverflow.com/questions/35246003/…
John Estropia
54

Así es como se comparan dos NSDates en Swift, acabo de probarlo en el patio de juegos de Xcode:

if date1.compare(date2) == NSComparisonResult.OrderedDescending
{
    NSLog("date1 after date2");
} else if date1.compare(date2) == NSComparisonResult.OrderedAscending
{
    NSLog("date1 before date2");
} else
{
    NSLog("dates are equal");
}

Para verificar si una fecha dueDatees dentro de una semana a partir de ahora:

let dueDate=...

let calendar = NSCalendar.currentCalendar()
let comps = NSDateComponents()
comps.day = 7
let date2 = calendar.dateByAddingComponents(comps, toDate: NSDate(), options: NSCalendarOptions.allZeros)

if dueDate.compare(date2!) == NSComparisonResult.OrderedDescending
{
    NSLog("not due within a week");
} else if dueDate.compare(date2!) == NSComparisonResult.OrderedAscending
{
    NSLog("due within a week");
} else
{
    NSLog("due in exactly a week (to the second, this will rarely happen in practice)");
}
Deshacer
fuente
2
¿descendente ordenado significa que fecha1> fecha2?
Henry oscannlain-miller
1
Sí, @ Henryoscannlain-miller.
Deshacer
46

Siempre lo hice en una línea:

let greater = date1.timeIntervalSince1970 < date2.timeIntervalSince1970

Aún legible en el ifbloque

Redetección
fuente
12

En Swift3, la Dateestructura en el Foundationahora implementa el Comparableprotocolo. Entonces, los NSDateenfoques anteriores de Swift2 son reemplazados por Swift3 Date.

/**
 `Date` represents a single point in time.

 A `Date` is independent of a particular calendar or time zone. To represent a `Date` to a user, you must interpret it in the context of a `Calendar`.
*/
public struct Date : ReferenceConvertible, Comparable, Equatable {

    // .... more         

    /**
        Returns the interval between the receiver and another given date.

        - Parameter another: The date with which to compare the receiver.

        - Returns: The interval between the receiver and the `another` parameter. If the receiver is earlier than `anotherDate`, the return value is negative. If `anotherDate` is `nil`, the results are undefined.

        - SeeAlso: `timeIntervalSince1970`
        - SeeAlso: `timeIntervalSinceNow`
        - SeeAlso: `timeIntervalSinceReferenceDate`
        */
    public func timeIntervalSince(_ date: Date) -> TimeInterval

   // .... more 

    /// Returns true if the two `Date` values represent the same point in time.
    public static func ==(lhs: Date, rhs: Date) -> Bool

    /// Returns true if the left hand `Date` is earlier in time than the right hand `Date`.
    public static func <(lhs: Date, rhs: Date) -> Bool

    /// Returns true if the left hand `Date` is later in time than the right hand `Date`.
    public static func >(lhs: Date, rhs: Date) -> Bool

    /// Returns a `Date` with a specified amount of time added to it.
    public static func +(lhs: Date, rhs: TimeInterval) -> Date

    /// Returns a `Date` with a specified amount of time subtracted from it.
    public static func -(lhs: Date, rhs: TimeInterval) -> Date

  // .... more
}

Nota ...

En Swift3, Datees struct, significa que lo es value type. NSDatees class, lo es reference type.

// Swift3
let a = Date()
let b = a //< `b` will copy `a`. 

// So, the addresses between `a` and `b` are different.
// `Date` is some kind different with `NSDate`.
AechoLiu
fuente
6
extension NSDate {

    // MARK: - Dates comparison

    func isGreaterThanDate(dateToCompare: NSDate) -> Bool {

        return self.compare(dateToCompare) == NSComparisonResult.OrderedDescending
    }

    func isLessThanDate(dateToCompare: NSDate) -> Bool {

        return self.compare(dateToCompare) == NSComparisonResult.OrderedAscending
    }

    func equalToDate(dateToCompare: NSDate) -> Bool {

        return self.compare(dateToCompare) == NSComparisonResult.OrderedSame
    }
}
Yura Voevodin
fuente
6

Si desea comparar fechas con granularidad (solo el mismo día o año, etc.) en swift 3.

func compareDate(date1:NSDate, date2:NSDate, toUnitGranularity: NSCalendar.Unit) -> Bool {

 let order = NSCalendar.current.compare(date1 as Date, to: date2 as Date, toGranularity: .day)
 switch order {
 case .orderedSame:
   return true
 default:
   return false
 }
}

Para otras comparaciones de calendario, cambie .day a;

.año .mes .día .hora .minuto .segundo

SashaZ
fuente
5

Swift ya implementa la comparación de fechas, solo usa date1> date2 y así sucesivamente.

/// Returns true if the two `Date` values represent the same point in time.
public static func ==(lhs: Date, rhs: Date) -> Bool

/// Returns true if the left hand `Date` is earlier in time than the right hand `Date`.
public static func <(lhs: Date, rhs: Date) -> Bool

/// Returns true if the left hand `Date` is later in time than the right hand `Date`.
public static func >(lhs: Date, rhs: Date) -> Bool

/// Returns a `Date` with a specified amount of time added to it.
public static func +(lhs: Date, rhs: TimeInterval) -> Date

/// Returns a `Date` with a specified amount of time subtracted from it.
public static func -(lhs: Date, rhs: TimeInterval) -> Date

/// Add a `TimeInterval` to a `Date`.
///
/// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more.
public static func +=(lhs: inout Date, rhs: TimeInterval)

/// Subtract a `TimeInterval` from a `Date`.
///
/// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more.
public static func -=(lhs: inout Date, rhs: TimeInterval)
Trung Phan
fuente
4

en Swift 3, Date es comparable para que podamos comparar fechas directamente como

let date1 = Date()
let date2 = Date()

let isGreater = date1 > date2
print(isGreater)

let isEqual = date1 == date2
print(isEqual)

o alternativamente

let result = date1.compare(date2)
switch result {
    case .OrderedAscending     :   print("date 1 is earlier than date 2")
    case .OrderedDescending    :   print("date 1 is later than date 2")
    case .OrderedSame          :   print("two dates are the same")
}

mejor forma de crear extensionen Fecha

extension Date {

  fun isGreater(than date: Date) -> Bool {
    return self > date 
  }

  func isSmaller(than date: Date) -> Bool {
    return self < date
  }

  func isEqual(to date: Date) -> Bool {
    return self == date
  }

}

uso let isGreater = date1.isGreater(than: date2)

Suhit Patil
fuente
3

Esta función me funcionó para comparar si una fecha (startDate) fue posterior a endDate donde ambas se definieron como variables NSDate:

if startDate.compare(endDate as Date) == ComparisonResult.orderedDescending
Diskprotek
fuente
2

implementación en Swift

let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
let files = NSFileManager.defaultManager().contentsOfDirectoryAtPath(documentsPath, error: nil)

let filesAndProperties = NSMutableArray()
for file in files! {

    let filePath = documentsPath.stringByAppendingString(file as NSString)
    let properties = NSFileManager.defaultManager().attributesOfItemAtPath(filePath, error: nil)
    let modDate = properties![NSFileModificationDate] as NSDate
    filesAndProperties.addObject(NSDictionary(objectsAndKeys: file, "path", modDate, "lastModDate"))
}

let sortedFiles = filesAndProperties.sortedArrayUsingComparator({
    (path1, path2) -> NSComparisonResult in

    var comp = (path1.objectForKey("lastModDate") as NSDate).compare(path2.objectForKey("lastModDate") as NSDate)
    if comp == .OrderedDescending {

        comp = .OrderedAscending
    } else if comp == .OrderedAscending {

        comp = .OrderedDescending
    }

    return comp
})
János
fuente
2
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let dateData: String = dateFormatter.stringFromDate(date1)
let testDate: String = dateFormatter.stringFromDate(date2)
print(dateData == testDate)
Trevor Jordet
fuente
1
someArray.sort({($0.dateAdded?.timeIntervalSinceReferenceDate)! < ($1.dateAdded?.timeIntervalSinceReferenceDate)!})

dateAdded es una variable NSDate en mi objeto

class MyClass {
    let dateAdded: NSDate?
}
larod
fuente
1

Tenemos un escenario para verificar las mentiras de la hora actual b / n dos veces (dos fechas). Por ejemplo, quiero verificar la mentira actual entre la hora de apertura de la clínica (Hospital) y la hora de cierre.

Usa el código simple.

      NSDate * now = [NSDate date];
        NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
        [outputFormatter setDateFormat:@"HH:mm:ss"];

        //current time
        NSString *currentTimeString = [outputFormatter stringFromDate:now];
        NSDate *dateCurrent = [outputFormatter dateFromString:currentTimeString];


        NSString *timeStart = @"09:00:00";
        NSString *timeEnd = @"22:00:00";

        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"HH:mm:ss"];

        NSDate *dateStart= [formatter timeStart];
        NSDate *dateEnd = [formatter timeEnd];
        NSComparisonResult result = [dateCurrent compare:dateStart];
        NSComparisonResult resultSecond = [date2 compare:dateEnd];

if(result == NSOrderedDescending && resultSecond == NSOrderedDescending)
        {
            NSLog(@"current time lies in starting and end time");
    }else {
            NSLog(@"current time doesn't lie in starting and end time");
        }
Maninderjit Singh
fuente
1

Para swift 3, puede usar la siguiente función para comparar entre dos fechas.

func compareDate(dateInitial:Date, dateFinal:Date) -> Bool {
    let order = Calendar.current.compare(dateInitial, to: dateFinal, toGranularity: .day)
    switch order {
    case .orderedSame:
        return true
    default:
        return false
    }
}

toGranularity se puede cambiar de acuerdo con las restricciones a las que desea aplicar su comparación.

Himanshu
fuente
1

Extender sobre SashaZ

Swift iOS 8 y superior Cuando necesite más que simplemente comparaciones de fechas más grandes o más pequeñas. Por ejemplo, es el mismo día o el día anterior, ...

Nota: Nunca olvides la zona horaria. La zona horaria del calendario tiene un valor predeterminado, pero si no le gusta el valor predeterminado, debe configurar la zona horaria usted mismo. Para saber qué día es, debe saber en qué zona horaria está preguntando.

extension Date {
    func compareTo(date: Date, toGranularity: Calendar.Component ) -> ComparisonResult  {
        var cal = Calendar.current
        cal.timeZone = TimeZone(identifier: "Europe/Paris")!
        return cal.compare(self, to: date, toGranularity: toGranularity)
        }
    }

Úselo así:

if thisDate.compareTo(date: Date(), toGranularity: .day) == .orderedDescending {
// thisDate is a previous day
}

De un ejemplo más complejo. Busque y filtre todas las fechas en una matriz, que son del mismo día que "findThisDay":

let formatter = DateFormatter()
formatter.timeZone = TimeZone(identifier: "Europe/Paris")
formatter.dateFormat = "yyyy/MM/dd HH:mm:ss"

let findThisDay = formatter.date(from: "2018/11/05 08:11:08")!
_ = [
    formatter.date(from: "2018/12/05 08:08:08")!, 
    formatter.date(from: "2018/11/05 08:11:08")!,
    formatter.date(from: "2018/11/05 11:08:22")!,
    formatter.date(from: "2018/11/05 22:08:22")!,
    formatter.date(from: "2018/11/05 08:08:22")!,
    formatter.date(from: "2018/11/07 08:08:22")!,
    ]
    .filter{ findThisDay.compareTo(date: $0 , toGranularity: .day) == .orderedSame }
    .map { print(formatter.string(from: $0)) }
t1ser
fuente