NSPredicate: filtraggio degli oggetti in base al giorno della proprietà NSDate


123

Ho un modello Core Data con una NSDateproprietà. Voglio filtrare il database per giorno. Presumo che la soluzione implicherà un NSPredicate, ma non sono sicuro di come metterlo insieme.

So come confrontare il giorno di due NSDates usando NSDateComponentse NSCalendar, ma come lo filtro con un NSPredicate?

Forse ho bisogno di creare una categoria nella mia NSManagedObjectsottoclasse che possa restituire una data nuda con solo l'anno, il mese e il giorno. Quindi potrei confrontarlo in un file NSPredicate. È questo il tuo consiglio o c'è qualcosa di più semplice?

Risposte:


193

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

4
e se abbiamo un solo appuntamento?
Wasim

4
Sembra che tu possa usare ==. Tuttavia, se stai cercando di giorno, ricorda che NSDate è una data e un'ora: potresti voler utilizzare un intervallo da mezzanotte a mezzanotte.
jlstrecker

1
Esempio su come impostare anche startDate e endDate, vedere la mia risposta di seguito
d.ennis

@jlstrecker puoi mostrarmi un esempio su come utilizzare un intervallo da mezzanotte a mezzanotte. ad esempio: ho 2 stessi giorni ma tempi diversi. e voglio filtrare per giorno (non mi interessa il tempismo). Come lo posso fare?
iosMentalist

3
@Shady Nell'esempio precedente, è possibile impostare startDate2012-09-17 0:00:00 e endDate2012-09-18 0:00:00 e il predicato su startDate <= date <endDate. Avrebbe catturato tutte le volte il 17 settembre 2012.
jlstrecker

63

Esempio su come impostare anche startDate e endDate alla risposta data sopra:

...

NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth ) 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 ricordare che questo esempio mostra anche come cercare voci di data "zero".


10

In Swift ho ottenuto 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 difficoltà a scoprire che l'interpolazione di stringhe "\(this notation)"non funziona per confrontare le date in NSPredicate.


Grazie per questa risposta. Versione Swift 3 aggiunta di seguito.
DS.

9

Estensione Swift 3.0 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 usa come:

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

8

Ho portato la risposta da Glauco Neves a Swift 2.0 e l'ho inserita in una funzione che riceve una data e restituisce la NSPredicateper 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!])
}

Grazie per questa risposta. Versione Swift 3 aggiunta di seguito.
DS.

6

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

1

Recentemente ho passato un po 'di tempo a tentare di risolvere lo stesso problema e aggiungere quanto segue all'elenco delle alternative per preparare le date di inizio e fine (include il metodo aggiornato per iOS 8 e versioni successive) ...

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 il NSPredicateper i Core Data NSFetchRequest(come già mostrato sopra in altre risposte) ...

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

0

Per me questo ha funzionato.

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 fa. Voglio dire, ricevo i dati di una settimana dalla data corrente. Questo dovrebbe funzionare.

Nota: sto convertendo la data che sta arrivando con milli secondi per 1000 e confrontando dopo. Fammi sapere se hai bisogno di chiarezza.


Nella tua risposta completeArrayc'è Array di dictionaryo dataModel voglio applicare su DataModel.
Sumeet Mourya
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.