NSPredicate: การกรองวัตถุตามวันของคุณสมบัติ NSDate


123

ฉันมีโมเดลข้อมูลหลักที่มีNSDateคุณสมบัติ ฉันต้องการกรองฐานข้อมูลตามวัน ฉันคิดว่าการแก้ปัญหาจะเกี่ยวข้องกับNSPredicateแต่ฉันไม่แน่ใจว่าจะรวมทั้งหมดเข้าด้วยกันได้อย่างไร

ฉันรู้วิธีเปรียบเทียบวันของสองวันNSDateโดยใช้NSDateComponentsและNSCalendarแต่ฉันจะกรองด้วยNSPredicate?

บางทีฉันอาจต้องสร้างหมวดหมู่ในNSManagedObjectคลาสย่อยของฉันที่สามารถส่งคืนวันที่เปล่าโดยมีเพียงปีเดือนและวัน จากนั้นฉันสามารถเปรียบเทียบสิ่งนั้นในNSPredicateไฟล์. นี่เป็นคำแนะนำของคุณหรือมีอะไรที่ง่ายกว่านี้

คำตอบ:


193

ให้ NSDate * startDateและendDateและ 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
จะเกิดอะไรขึ้นถ้าเรามีเพียงวันเดียว?
Wasim

4
ดูเหมือนว่าคุณจะ==ใช้ได้ แม้ว่าคุณจะค้นหาตามวัน แต่อย่าลืมว่า NSDate เป็นวันที่และเวลาคุณอาจต้องการใช้ช่วงเที่ยงคืนถึงเที่ยงคืน
jlstrecker

1
ตัวอย่างวิธีการตั้งค่า startDate และ endDate ดูคำตอบของฉันด้านล่าง
d.ennis

@jlstrecker คุณสามารถแสดงตัวอย่างวิธีใช้ช่วงเที่ยงคืนถึงเที่ยงคืนได้ไหม ตัวอย่างเช่นฉันมีเวลา 2 วันเดียวกัน แต่เวลาต่างกัน และฉันต้องการกรองตามวัน (ฉันไม่สนใจเรื่องเวลา) ฉันจะทำเช่นนั้นได้อย่างไร?
iosMentalist

3
@Shady ในตัวอย่างข้างต้นคุณสามารถตั้งค่าstartDateเป็น 2012-09-17 0:00:00 และendDateถึง 2012-09-18 0:00:00 และเพรดิเคตเป็น startDate <= date <endDate ที่จะจับทุกครั้งใน 2012-09-17
jlstrecker

63

ตัวอย่างวิธีการตั้งค่า startDate และ endDate สำหรับคำตอบข้างต้น:

...

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

...

ที่นี่ฉันกำลังค้นหารายการทั้งหมดภายในหนึ่งเดือน ควรกล่าวถึงว่าตัวอย่างนี้ยังแสดงวิธีการค้นหาวันที่ 'nil'


10

ใน Swift ฉันมีบางอย่างที่คล้ายกับ:

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

ฉันมีช่วงเวลาที่ยากลำบากในการค้นพบว่าการแก้ไขสตริงใช้"\(this notation)"ไม่ได้กับการเปรียบเทียบวันที่ใน NSPredicate


ขอบคุณสำหรับคำตอบนี้ เพิ่มเวอร์ชัน Swift 3 ด้านล่าง
DS.

9

ส่วนขยาย Swift 3.0 สำหรับวันที่:

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

จากนั้นใช้เช่น:

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

8

ฉันย้ายคำตอบจากGlauco Nevesไปเป็น Swift 2.0 และห่อไว้ในฟังก์ชันที่รับวันที่และส่งคืนNSPredicateวันที่ตรงกัน:

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

ขอบคุณสำหรับคำตอบนี้ เพิ่มเวอร์ชัน Swift 3 ด้านล่าง
DS.

6

การเพิ่มคำตอบของ Rafael (มีประโยชน์อย่างไม่น่าเชื่อขอบคุณ!) การย้ายไปใช้ 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

ฉันเพิ่งใช้เวลาในการพยายามแก้ไขปัญหาเดียวกันนี้และเพิ่มสิ่งต่อไปนี้ในรายการทางเลือกเพื่อเตรียมวันที่เริ่มต้นและวันที่สิ้นสุด (รวมถึงวิธีการอัปเดตสำหรับ iOS 8 ขึ้นไป) ...

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

... และNSPredicateสำหรับ Core Data NSFetchRequest(ดังที่แสดงไว้แล้วในคำตอบอื่น ๆ ) ...

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

0

สำหรับฉันมันได้ผล

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

ฉันได้กรองอาร์เรย์จากวันที่ปัจจุบันถึง 7 วันย้อนหลัง ฉันหมายความว่าฉันได้รับข้อมูลหนึ่งสัปดาห์จากวันที่ปัจจุบัน สิ่งนี้ควรใช้งานได้

หมายเหตุ: ฉันกำลังแปลงวันที่ซึ่งมาพร้อมกับมิลลิวินาทีคูณ 1,000 และเปรียบเทียบหลังจากนั้น โปรดแจ้งให้เราทราบหากคุณต้องการความชัดเจน


ในคำตอบของคุณcompleteArrayคือมี Array ของdictionaryหรือdataModel ฉันต้องการใช้กับ DataModel
Sumeet Mourya
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.