NSPredicate: lọc các đối tượng theo ngày của thuộc tính NSDate


123

Tôi có một mô hình Dữ liệu cốt lõi với một NSDatetài sản. Tôi muốn lọc cơ sở dữ liệu theo ngày. Tôi cho rằng giải pháp sẽ liên quan đến một NSPredicate, nhưng tôi không chắc làm thế nào để kết hợp tất cả lại.

Tôi biết cách so sánh ngày của hai NSDates bằng cách sử dụng NSDateComponentsNSCalendar, nhưng làm cách nào để lọc nó với một NSPredicate?

Có lẽ tôi cần tạo một danh mục trên NSManagedObjectlớp con của mình để có thể trả về một ngày trống chỉ với năm, tháng và ngày. Sau đó tôi có thể so sánh điều đó trong một NSPredicate. Đây có phải là đề xuất của bạn, hoặc có một cái gì đó đơn giản hơn?

Câu trả lời:


193

Đưa ra một NSDate * startDateendDate và 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
Nếu chúng ta chỉ có một ngày thì sao?
Wasim

4
Hình như bạn có thể sử dụng ==. Mặc dù nếu bạn đang tìm kiếm theo ngày, hãy nhớ rằng NSDate là ngày và giờ - bạn có thể muốn sử dụng phạm vi từ nửa đêm đến nửa đêm.
jlstrecker

1
Ví dụ về cách thiết lập startDate và endDate, xem câu trả lời của tôi bên dưới
d.ennis

@jlstrecker bạn có thể chỉ cho tôi một ví dụ về cách sử dụng phạm vi từ nửa đêm đến nửa đêm. ví dụ: tôi có 2 ngày giống nhau nhưng thời gian khác nhau. và tôi muốn lọc theo ngày (tôi không quan tâm đến thời gian). Làm thế nào tôi có thể làm điều đó?
iosMentalist

3
@Shady Trong ví dụ trên, bạn có thể đặt startDatethành 2012-09-17 0:00:00 và endDate2012-09-18 0:00:00 và vị từ thành startDate <= date <endDate. Điều đó sẽ bắt tất cả các lần vào ngày 2012-09-17.
jlstrecker

63

Ví dụ về cách thiết lập startDate và endDate cho câu trả lời đã cho ở trên:

...

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

...

Ở đây tôi đã tìm kiếm tất cả các mục trong vòng một tháng. Điều đáng nói là, ví dụ này cũng cho thấy cách tìm kiếm các mục nhập ngày tháng.


10

Trong Swift tôi có một cái gì đó tương tự như:

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

Tôi đã có một thời gian khó khăn để phát hiện ra rằng phép nội suy chuỗi "\(this notation)"không hoạt động để so sánh ngày trong NSPredicate.


Cảm ơn bạn cho câu trả lời này. Phiên bản Swift 3 được thêm vào bên dưới.
DS.

9

Tiện ích mở rộng Swift 3.0 cho Ngày:

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

Sau đó sử dụng như:

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

8

Tôi đã chuyển câu trả lời từ Glauco Neves sang Swift 2.0 và gói nó bên trong một chức năng nhận được một ngày và trả về NSPredicatecho ngày tương ứng:

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

Cảm ơn bạn cho câu trả lời này. Phiên bản Swift 3 được thêm vào bên dưới.
DS.

6

Thêm vào câu trả lời của Rafael (cực kỳ hữu ích, cảm ơn bạn!), Chuyển sang 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

Gần đây tôi đã dành một chút thời gian để cố gắng giải quyết vấn đề tương tự này và thêm phần sau vào danh sách các lựa chọn thay thế để chuẩn bị ngày bắt đầu và ngày kết thúc (bao gồm phương pháp cập nhật cho iOS 8 trở lên) ...

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

... và NSPredicatedữ liệu cốt lõi NSFetchRequest(như đã được trình bày ở trên trong các câu trả lời khác) ...

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

0

Đối với tôi điều này là làm việc.

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

Tôi đã lọc mảng từ ngày hiện tại đến 7 ngày trở lại. Tôi có nghĩa là tôi đang nhận được một tuần dữ liệu từ ngày hiện tại. Điều này nên làm việc.

Lưu ý: Tôi đang chuyển đổi ngày sắp tới với milli giây bằng 1000 và so sánh sau. Hãy cho tôi biết nếu bạn cần bất kỳ sự rõ ràng.


Trong câu trả lời của bạn completeArraylà nó có Array dictionaryhoặc dataModel tôi muốn áp dụng trên DataModel.
Sumeet Mourya
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.