【问题标题】:NSPredicate: filtering objects by day of NSDate propertyNSPredicate:按 NSDate 属性的日期过滤对象
【发布时间】:2010-12-30 05:39:56
【问题描述】:

我有一个带有NSDate 属性的核心数据模型。我想按天过滤数据库。我认为解决方案将涉及NSPredicate,但我不确定如何将它们放在一起。

我知道如何使用NSDateComponentsNSCalendar 比较两个NSDates 的日期,但是如何使用NSPredicate 过滤它?

也许我需要在我的 NSManagedObject 子类上创建一个类别,该类别可以返回仅包含年、月和日的裸日期。然后我可以在NSPredicate 中进行比较。这是您的建议,还是有更简单的方法?

【问题讨论】:

    标签: objective-c core-data nsdate nspredicate nsfetchedresultscontroller


    【解决方案1】:

    给定一个 NSDate * startDateendDate 以及一个 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];
    

    【讨论】:

    • 如果我们只有一个约会呢?
    • 看起来你可以使用==。如果您在白天搜索,请记住 NSDate 是日期和时间 - 您可能希望使用午夜到午夜的范围。
    • 如何设置 startDate 和 endDate 的示例,请参阅下面的答案
    • @jlstrecker 你能告诉我一个如何使用午夜到午夜范围的例子吗?例如:我有 2 个相同的日子,但时间不同。我想按天过滤(我不在乎时间)。我该怎么做?
    • @Shady 在上面的示例中,您可以将 startDate 设置为 2012-09-17 0:00:00 并将 endDate 设置为 2012-09-18 0:00:00 和谓词开始日期
    【解决方案2】:

    关于如何将 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”日期元素。

    【讨论】:

      【解决方案3】:

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

      然后使用like:

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

      【讨论】:

        【解决方案4】:

        在 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 版本。
        【解决方案5】:

        我将答案从 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 版本。
        【解决方案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!])
        }
        

        【讨论】:

          【解决方案7】:

          我最近花了一些时间尝试解决同样的问题,并将以下内容添加到准备开始和结束日期的备选列表中(包括 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];
          

          ...以及核心数据NSFetchRequestNSPredicate(如上面其他答案中所示)...

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

          【讨论】:

            【解决方案8】:

            基于之前的答案,使用 Swift 5.x 的更新和替代方法

            func predicateForDayUsingDate(_ date: Date) -> NSPredicate {
                
                var calendar = Calendar.current
                calendar.timeZone = NSTimeZone.local
                // following creates exact midnight 12:00:00:000 AM of day
                let startOfDay = calendar.startOfDay(for: date)
                // following creates exact midnight 12:00:00:000 AM of next day
                let endOfDay = calendar.date(byAdding: .day, value: 1, to: startOfDay)!
                
                return NSPredicate(format: "day >= %@ AND day < %@", argumentArray: [startOfDay, endOfDay])
            }
            

            如果您希望将 endOfDay 的时间创建为晚上 11:59:59,则可以改为包含...

                let endOfDayLessOneSecond = endOfDay.addingTimeInterval(TimeInterval(-1))
            

            但是您可能会将 NSPredicate 更改为...

                return NSPredicate(format: "day >= %@ AND day <= %@", argumentArray: [startOfDay, endOfDayLessOneSecond])
            

            ...具体说明从 day &lt; %@day &lt;= %@ 的更改。

            【讨论】:

              【解决方案9】:

              对我来说这是可行的。

              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,然后进行比较。如果您需要任何澄清,请告诉我。

              【讨论】:

              • 在你的回答completeArray 中是否有dictionarydataModel 的数组我想申请DataModel。
              猜你喜欢
              • 1970-01-01
              • 2016-12-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-12-12
              • 2017-09-20
              • 2020-06-30
              相关资源
              最近更新 更多