NSPredicate: фильтрация объектов по дню свойства NSDate
у меня есть модель основных данных с NSDate
собственность. Я хочу отфильтровать базу данных по дням. Я предполагаю, что решение будет включать в себя NSPredicate
, но я не уверен, как поставить все это вместе.
Я знаю, как сравнить день два NSDate
с помощью NSDateComponents
и NSCalendar
, но как я могу отфильтровать его с помощью NSPredicate
?
возможно, мне нужно создать Категорию на моем NSManagedObject
подкласс, который может возвращать голую дату только с годом, месяцем и днем. Тогда я мог бы сравнить это в Ан NSPredicate
. Это ваша рекомендация, или есть что-то проще?
8 ответов:
учитывая 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];
пример того, как также настроить startDate и endDate для приведенного выше ответа:
... 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]; ...
здесь я искал все записи в течение одного месяца. Стоит отметить, что этот пример также показывает, как искать 'nil' date-entires.
в 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 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.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()
добавление к ответу Рафаэля (невероятно полезно, спасибо!), портирование для 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!]) }
Я недавно провел некоторое время, пытаясь решить эту же проблему и добавить в список вариантов для подготовки даты начала и окончания (включая обновление способ на 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
для основных данныхNSFetchRequest
(как уже показано выше в других ответах)...[NSPredicate predicateWithFormat:@"(dateAttribute >= %@) AND (dateAttribute < %@)", dateDayStart, dateDayNext]]
для меня это сработало.
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 дней назад. Я имею в виду, что я получаю данные за одну неделю с текущей даты. Это должно сработать.
Примечание: я конвертирую дату, которая идет с миллисекундами на 1000, и сравниваю после. Дайте мне знать, если вам нужна ясность.