NSPredicate: memfilter objek berdasarkan hari properti NSDate

123

Saya memiliki model Data Inti dengan NSDateproperti. Saya ingin memfilter database berdasarkan hari. Saya berasumsi solusinya akan melibatkan NSPredicate, tetapi saya tidak yakin bagaimana menggabungkan semuanya.

Saya tahu cara membandingkan hari kedua NSDatemenggunakan NSDateComponentsdan NSCalendar, tetapi bagaimana cara memfilternya dengan NSPredicate?

Mungkin saya perlu membuat kategori di NSManagedObjectsubkelas saya yang dapat mengembalikan tanggal kosong hanya dengan tahun, bulan dan hari. Kemudian saya bisa membandingkannya di file NSPredicate. Apakah ini rekomendasi Anda, atau ada sesuatu yang lebih sederhana?

Jonathan Sterling
sumber

Jawaban:

193

Diberikan NSDate * startDate dan endDate serta 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
sumber
4
bagaimana jika kita hanya memiliki satu kencan?
Wasim
4
Sepertinya Anda bisa menggunakan ==. Meskipun jika Anda mencari pada siang hari, ingatlah bahwa NSDate adalah tanggal dan waktu - Anda mungkin ingin menggunakan rentang tengah malam hingga tengah malam.
jlstrecker
1
Contoh tentang cara juga mengatur startDate dan endDate, lihat jawaban saya di bawah ini
d.ennis
@jlstrecker dapatkah Anda menunjukkan kepada saya contoh bagaimana menggunakan rentang tengah malam hingga tengah malam. Misalnya: saya punya 2 hari yang sama tetapi waktu yang berbeda. dan saya ingin memfilter berdasarkan hari (saya tidak peduli dengan waktunya). Bagaimana saya bisa melakukan itu?
iosMentalist
3
@Shady Dalam contoh di atas, Anda dapat mengatur startDateke 2012-09-17 0:00:00 dan endDate2012-09-18 0:00:00 dan predikat ke startDate <= date <endDate. Itu akan terjadi sepanjang waktu pada 2012-09-17.
jlstrecker
63

Contoh tentang cara juga mengatur startDate dan endDate ke jawaban yang diberikan di atas:

...

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

...

Di sini saya mencari semua entri dalam satu bulan. Perlu disebutkan, bahwa contoh ini juga menunjukkan cara mencari tanggal 'nil'.

d.ennis
sumber
10

Di Swift saya mendapatkan sesuatu yang mirip dengan:

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

Saya mengalami kesulitan untuk menemukan bahwa interpolasi string "\(this notation)"tidak berfungsi untuk membandingkan tanggal di NSPredicate.

Glauco Neves
sumber
Terima kasih atas jawaban ini. Versi Swift 3 ditambahkan di bawah.
DS.
9

Ekstensi Swift 3.0 untuk Tanggal:

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

Kemudian gunakan seperti:

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

Saya memindahkan jawaban dari Glauco Neves ke Swift 2.0 dan membungkusnya di dalam fungsi yang menerima tanggal dan mengembalikan NSPredicateuntuk hari yang sesuai:

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
sumber
Terima kasih atas jawaban ini. Versi Swift 3 ditambahkan di bawah.
DS.
6

Menambah jawaban Rafael (sangat berguna, terima kasih!), Porting untuk 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.
sumber
1

Saya baru-baru ini menghabiskan beberapa waktu untuk mencoba memecahkan masalah yang sama ini dan menambahkan yang berikut ini ke daftar alternatif untuk menyiapkan tanggal mulai dan akhir (termasuk metode yang diperbarui untuk iOS 8 dan lebih tinggi) ...

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

... dan NSPredicateuntuk Data Inti NSFetchRequest(seperti yang sudah ditunjukkan di atas pada jawaban lain) ...

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

Bagi saya ini berhasil.

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

Saya telah memfilter array dari tanggal sekarang hingga 7 hari yang lalu. Maksud saya, saya mendapatkan data satu minggu dari tanggal sekarang. Ini seharusnya berhasil.

Catatan: Saya mengubah tanggal yang datang dengan mili detik dengan 1000, dan membandingkan setelahnya. Beri tahu saya jika Anda membutuhkan kejelasan.

Narasimha Nallamsetty
sumber
Dalam jawaban Anda completeArrayapakah itu memiliki Array dictionaryatau dataModel saya ingin menerapkannya di DataModel.
Sumeet Mourya