114

Tôi có mô hình Dữ liệu chính với thuộc tính NSDate. 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 chắn làm thế nào để đặt tất cả lại với nhau.NSPredicate: lọc đối tượng theo ngày thuộc tính NSDate

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

Có lẽ tôi cần tạo danh mục trên lớp con NSManagedObject của mình có thể trả lại 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 số NSPredicate. Đây có phải là đề xuất của bạn hay không?

Trả lời

181

Cho 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]; 
+3

gì nếu chúng ta chỉ có một ngày? – Wasim

+3

Có vẻ 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 dải 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 –

60

Ví dụ về cách cũng thiết lập STARTDATE và ENDDATE để câu trả lời đưa ra ở trên:

... 

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: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 nhập trong vòng một tháng. Điều đáng nói, ví dụ này cũng cho thấy làm thế nào để tìm kiếm 'nil' ngày-entires.

+1

Đã lưu ngày của tôi :) – Xeieshan

9

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 chuỗi suy "\(this notation)" không hoạt động để so sánh ngày tháng trong NSPredicate.

+0

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

0

Đối với tôi, tính năng này đã hoạt động.

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 dữ liệu một tuần từ ngày hiện tại. Điều này sẽ làm việc.

Lưu ý: Tôi đang chuyển đổi ngày sắp tới bằng milli giây trước 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 nào.

+0

Trong câu trả lời của bạn 'completeArray' là nó có Array của' dictionary' hoặc 'dataModel' Tôi muốn áp dụng trên DataModel. –

8

tôi chuyển câu trả lời từ Glauco Neves để Swift 2.0 và bọc nó trong một chức năng tiếp nhận một ngày và trả về NSPredicate cho 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!]) 
} 
+0

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

1

tôi vừa mới trải qua một thời gian cố gắng để giải quyết việc này cùng một vấn đề và thêm dòng sau vào danh sách các lựa chọn thay thế để chuẩn bị bắt đầu và ngày kết thúc (bao gồm phương pháp cập nhật dành 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à NSPredicate cho Tập dữ liệu cốt lõi 012.(như đã trình bày ở trên trong câu trả lời khác) ...

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

Thêm vào câu trả lời của Rafael (vô cùng hữu ích, cảm ơn bạn!), Porting cho 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!]) 
} 
5

Swift 3.0 phần mở rộng 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() 
Các vấn đề liên quan