NSPredicate: Filtern von Objekten nach Tag der NSDate-Eigenschaft

123

Ich habe ein Core Data-Modell mit einer NSDateEigenschaft. Ich möchte die Datenbank nach Tag filtern. Ich NSPredicategehe davon aus, dass die Lösung eine beinhaltet , bin mir aber nicht sicher, wie ich alles zusammensetzen soll.

Ich weiß, wie man den Tag von zwei NSDateSekunden mit NSDateComponentsund vergleicht NSCalendar, aber wie filtere ich ihn mit einem NSPredicate?

Vielleicht muss ich in meiner NSManagedObjectUnterklasse eine Kategorie erstellen , die ein nacktes Datum mit nur Jahr, Monat und Tag zurückgeben kann. Dann könnte ich das in einem vergleichen NSPredicate. Ist das Ihre Empfehlung oder gibt es etwas Einfacheres?

Jonathan Sterling
quelle

Antworten:

193

Bei einem NSDate * startDate und endDate sowie einem 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];
diciu
quelle
4
Was ist, wenn wir nur ein Datum haben?
Wasim
4
Sieht aus wie Sie verwenden können ==. Wenn Sie jedoch tagsüber suchen, denken Sie daran, dass NSDate ein Datum und eine Uhrzeit ist. Möglicherweise möchten Sie einen Bereich von Mitternacht bis Mitternacht verwenden.
Jlstrecker
1
Beispiel, wie man auch startDate und endDate einrichtet, siehe meine Antwort unten
d.ennis
@jlstrecker Kannst du mir ein Beispiel zeigen, wie man einen Bereich von Mitternacht bis Mitternacht benutzt? Zum Beispiel: Ich habe 2 gleiche Tage, aber ein anderes Timing. und ich möchte nach Tag filtern (mir ist das Timing egal). Wie kann ich das machen?
iosMentalist
3
@Shady Im obigen Beispiel können Sie startDate2012-09-17 0:00:00 und endDate2012-09-18 0:00:00 sowie das Prädikat startDate <= date <endDate festlegen. Das würde am 17.09.2012 alle Zeiten fangen.
Jlstrecker
63

Beispiel zum Einrichten von startDate und endDate für die oben angegebene Antwort:

...

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

...

Hier habe ich innerhalb eines Monats nach allen Einträgen gesucht. Es ist erwähnenswert, dass dieses Beispiel auch zeigt, wie man 'nil' Datumsangaben sucht.

d.ennis
quelle
10

In Swift habe ich etwas Ähnliches bekommen wie:

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

Es fiel mir schwer "\(this notation)"herauszufinden , dass die Zeichenfolgeninterpolation beim Vergleichen von Daten in NSPredicate nicht funktioniert.

Glauco Neves
quelle
Vielen Dank für diese Antwort. Swift 3 Version unten hinzugefügt.
DS.
9

Swift 3.0-Erweiterung für Datum:

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

Dann benutze wie:

 let fetchReq = NSFetchRequest(entityName: "MyObject")
 fetchReq.predicate = myDate.makeDayPredicate()
Roni Leshes
quelle
8

Ich habe die Antwort von Glauco Neves auf Swift 2.0 portiert und sie in eine Funktion eingeschlossen, die ein Datum erhält und das NSPredicatefür den entsprechenden Tag zurückgibt :

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!])
}
Rafael Bugajewski
quelle
Vielen Dank für diese Antwort. Swift 3 Version unten hinzugefügt.
DS.
6

Hinzufügen zu Rafaels Antwort (unglaublich nützlich, danke!), Portierung für 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!])
}
DS.
quelle
1

Ich habe kürzlich einige Zeit damit verbracht, das gleiche Problem zu lösen und der Liste der Alternativen zur Vorbereitung von Start- und Enddaten Folgendes hinzuzufügen (einschließlich aktualisierter Methode für iOS 8 und höher) ...

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

... und die NSPredicatefür die Kerndaten NSFetchRequest(wie oben bereits in anderen Antworten gezeigt) ...

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

Bei mir funktioniert das.

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

Ich habe das Array vom aktuellen Datum bis vor 7 Tagen gefiltert. Ich meine, ich erhalte eine Woche Daten vom aktuellen Datum. Das sollte funktionieren.

Hinweis: Ich konvertiere das Datum, das mit Millisekunden von 1000 kommt, und vergleiche danach. Lassen Sie mich wissen, wenn Sie Klarheit brauchen.

Narasimha Nallamsetty
quelle
In Ihrer Antwort completeArrayist es Array von dictionaryoder dataModel ich möchte auf DataModel anwenden.
Sumeet Mourya