114

Ho un modello Core Data con una proprietà NSDate. Voglio filtrare il database di giorno. Presumo che la soluzione comporterà un NSPredicate, ma non sono sicuro di come mettere tutto insieme.NSPredicate: filtraggio degli oggetti per giorno della proprietà NSDate

so come confrontare il giorno di due NSDate s utilizzando NSDateComponents e NSCalendar, ma come faccio a filtrare con un NSPredicate?

Forse ho bisogno di creare una categoria sulla mia sottoclasse NSManagedObject che può restituire una data vuota con solo l'anno, il mese e il giorno. Quindi potrei confrontarlo in un NSPredicate. Questa è la tua raccomandazione o c'è qualcosa di più semplice?

risposta

181

Dato un NSDate * startDate e endDate e 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]; 
+3

cosa succede se abbiamo solo una data? – Wasim

+3

Sembra che tu possa usare '=='. Anche se stai cercando per giorno, ricorda che NSDate è una data e ora - potresti voler usare un intervallo da mezzanotte a mezzanotte. – jlstrecker

+1

Esempio su come impostare anche startDate e endDate, vedere la mia risposta sotto –

60

Esempio su come configurare anche startDate e endDate a quanto sopra risposta data:

... 

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]; 

... 

Qui stavo cercando tutte le voci entro un mese. Vale la pena menzionare che questo esempio mostra anche come cercare "date zero".

+1

Salvato il mio giorno :) – Xeieshan

9

In Swift ho avuto qualcosa di simile 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!]) 

ho avuto un momento difficile per scoprire che stringa di interpolazione "\(this notation)" non funziona per il confronto di date in NSPredicate.

+0

Grazie per questa risposta. Swift 3 versione aggiunta di seguito. –

0

Per me questo è funzionante.

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); 

Ho filtrato l'array dalla data corrente a 7 giorni indietro. Voglio dire, sto ricevendo dati di una settimana dalla data corrente. Questo dovrebbe funzionare.

Nota: sto convertendo la data che viene con milli secondi per 1000 e il confronto dopo. Fammi sapere se hai bisogno di chiarezza.

+0

Nella tua risposta 'completeArray' ha Array di' dictionary' o 'dataModel' Voglio applicare su DataModel. –

8

ho portato la risposta da Glauco Neves a Swift 2.0 e avvolto all'interno di una funzione che riceve una data e restituisce il NSPredicate per il giorno corrispondente:

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!]) 
} 
+0

Grazie per questa risposta. Swift 3 versione aggiunta di seguito. –

1

Recentemente ho trascorso un po 'di tempo cercando di risolvere questo stesso problema e aggiungere il seguente alla lista di alternative per preparare le date di inizio e di fine (include metodo aggiornato per iOS 8 e superiori) ...

NSDate *dateDay = nil; 
NSDate *dateDayStart = nil; 
NSDate *dateDayNext = nil; 

dateDay = <<USER_INPUT>>; 

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]; 

... e la NSPredicate per il core data 012.(come già mostrato in precedenza in altre risposte) ...

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

aggiunta alla risposta di Rafael (incredibilmente utile, grazie!), Porting per 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!]) 
} 
5

Swift 3.0 estensione per Data:

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!]) 
} 
} 

Quindi utilizzare come:

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