NSPredicate: filtrado de objetos por día de propiedad de NSDate

Tengo un modelo de Datos centrales con una propiedad NSDate . Quiero filtrar la base de datos por día. Supongo que la solución implicará un NSPredicate , pero no estoy seguro de cómo NSPredicate todo.

Sé cómo comparar el día de dos NSDate s usando NSDateComponents y NSCalendar , pero ¿cómo lo filtro con NSPredicate ?

Tal vez deba crear una categoría en mi subclase NSManagedObject que pueda devolver una fecha simple con solo el año, el mes y el día. Entonces podría comparar eso en una NSPredicate . ¿Es esta tu recomendación, o hay algo más simple?

Dado un NSDate * startDate y endDate y un NSManagedObjectContext * moc:

 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(date >= %@) AND (date <= %@)", startDate, endDate]; NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; [request setEntity:[NSEntityDescription entityForName:@"EntityName" inManagedObjectContext:moc]]; [request setPredicate:predicate]; NSError *error = nil; NSArray *results = [moc executeFetchRequest:request error:&error]; 

Ejemplo sobre cómo configurar startDate y endDate a la respuesta dada anteriormente:

 ... NSCalendar *calendar = [NSCalendar currentCalendar]; NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit ) fromDate:[NSDate date]]; //create a date with these components NSDate *startDate = [calendar dateFromComponents:components]; [components setMonth:1]; [components setDay:0]; //reset the other components [components setYear:0]; //reset the other components NSDate *endDate = [calendar dateByAddingComponents:components toDate:startDate options:0]; predicate = [NSPredicate predicateWithFormat:@"((date >= %@) AND (date < %@)) || (date = nil)",startDate,endDate]; ... 

Aquí estaba buscando todas las entradas dentro de un mes. Vale la pena mencionar que este ejemplo también muestra cómo buscar 'nil' date-entires.

En Swift obtuve algo similar a:

  let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" let date = dateFormatter.dateFromString(predicate) let calendar = NSCalendar(calendarIdentifier: NSGregorianCalendar) let components = calendar!.components( NSCalendarUnit.CalendarUnitYear | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitDay | NSCalendarUnit.CalendarUnitHour | NSCalendarUnit.CalendarUnitMinute | NSCalendarUnit.CalendarUnitSecond, fromDate: date!) components.hour = 00 components.minute = 00 components.second = 00 let startDate = calendar!.dateFromComponents(components) components.hour = 23 components.minute = 59 components.second = 59 let endDate = calendar!.dateFromComponents(components) predicate = NSPredicate(format: "day >= %@ AND day =< %@", argumentArray: [startDate!, endDate!]) 

Tuve un momento difícil para descubrir que la interpolación de cadenas "\(this notation)" no funciona para comparar fechas en NSPredicate.

Transmití la respuesta de Glauco Neves a Swift 2.0 y la envolví dentro de una función que recibe una fecha y devuelve el NSPredicate para el día correspondiente:

 func predicateForDayFromDate(date: NSDate) -> NSPredicate { let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) let components = calendar!.components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: date) components.hour = 00 components.minute = 00 components.second = 00 let startDate = calendar!.dateFromComponents(components) components.hour = 23 components.minute = 59 components.second = 59 let endDate = calendar!.dateFromComponents(components) return NSPredicate(format: "day >= %@ AND day =< %@", argumentArray: [startDate!, endDate!]) } 

Extensión de Swift 3.0 para la fecha:

 extension Date{ func makeDayPredicate() -> NSPredicate { let calendar = Calendar.current var components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: self) components.hour = 00 components.minute = 00 components.second = 00 let startDate = calendar.date(from: components) components.hour = 23 components.minute = 59 components.second = 59 let endDate = calendar.date(from: components) return NSPredicate(format: "day >= %@ AND day =< %@", argumentArray: [startDate!, endDate!]) } } 

Luego usa como:

  let fetchReq = NSFetchRequest(entityName: "MyObject") fetchReq.predicate = myDate.makeDayPredicate() 

Agregando a la respuesta de Rafael (increíblemente útil, ¡gracias!), Portar para Swift 3.

 func predicateForDayFromDate(date: Date) -> NSPredicate { let calendar = Calendar(identifier: Calendar.Identifier.gregorian) var components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date) components.hour = 00 components.minute = 00 components.second = 00 let startDate = calendar.date(from: components) components.hour = 23 components.minute = 59 components.second = 59 let endDate = calendar.date(from: components) return NSPredicate(format: "YOUR_DATE_FIELD >= %@ AND YOUR_DATE_FIELD =< %@", argumentArray: [startDate!, endDate!]) } 

Hace poco tiempo intenté resolver este problema y agregué lo siguiente a la lista de alternativas para preparar fechas de inicio y finalización (incluye el método actualizado para iOS 8 y superior) …

 NSDate *dateDay = nil; NSDate *dateDayStart = nil; NSDate *dateDayNext = nil; dateDay = <>; dateDayStart = [[NSCalendar currentCalendar] startOfDayForDate:dateDay]; // dateDayNext EITHER dateDayNext = [dateDayStart dateByAddingTimeInterval:(24 * 60 * 60)]; // dateDayNext OR NSDateComponents *dateComponentDay = nil; dateComponentDay = [[NSDateComponents alloc] init]; [dateComponentDay setDay:1]; dateDayNext = [[NSCalendar currentCalendar] dateByAddingComponents:dateComponentDay toDate:dateDayStart options:NSCalendarMatchNextTime]; 

… y NSPredicate para Core Data NSFetchRequest (como ya se ha mostrado anteriormente en otras respuestas) …

 [NSPredicate predicateWithFormat:@"(dateAttribute >= %@) AND (dateAttribute < %@)", dateDayStart, dateDayNext]] 

Para mí esto está trabajado.

 NSCalendar *calendar = [NSCalendar currentCalendar]; NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit ) fromDate:[NSDate date]]; //create a date with these components NSDate *startDate = [calendar dateFromComponents:components]; [components setMonth:0]; [components setDay:0]; //reset the other components [components setYear:0]; //reset the other components NSDate *endDate = [calendar dateByAddingComponents:components toDate:startDate options:0]; startDate = [NSDate date]; endDate = [startDate dateByAddingTimeInterval:-(7 * 24 * 60 * 60)];//change here NSString *startTimeStamp = [[NSNumber numberWithInt:floor([endDate timeIntervalSince1970])] stringValue]; NSString *endTimeStamp = [[NSNumber numberWithInt:floor([startDate timeIntervalSince1970])] stringValue]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"((paidDate1 >= %@) AND (paidDate1 < %@))",startTimeStamp,endTimeStamp]; NSLog(@"predicate is %@",predicate); totalArr = [completeArray filteredArrayUsingPredicate:predicate]; [self filterAndPopulateDataBasedonIndex]; [self.tableviewObj reloadData]; NSLog(@"result is %@",totalArr); 

He filtrado una matriz desde la fecha actual hasta 7 días atrás. Quiero decir que recibo datos de una semana a partir de la fecha actual. Esto debería funcionar.

Nota: Estoy convirtiendo la fecha que viene con mili segundos por 1000, y comparando después. Avísame si necesitas alguna claridad.