NSPredicate-用于查找在特定日期范围之间发生的事件

时间:2011-12-03 00:41:47

标签: iphone core-data nsdate nspredicate

这比仅仅更复杂一点

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"startDate >= %@ AND endDate <= %@", startDay,endDay];

我正在构建一个日历应用程序,我需要从某个特定日期发生的核心数据存储中提取事件。但是,事件可能会在我尝试显示的那天开始/结束,例如。

我的日期范围(Start = 2011-12-02 00:00:00 to End = 2011-12-02 23:59:59)

活动(开始= 2011-12-01 23:00:00至结束= 2011-12-02 05:00:00)

如何编写谓词以确定该事件是否属于该日期范围。请记住,事件可以在日期范围之前和之后开始。

谢谢!

4 个答案:

答案 0 :(得分:17)

这是一种构建谓词以检索特定日期发生的非经常性事件的方法(定期事件需要额外处理):

- (NSPredicate *) predicateToRetrieveEventsForDate:(NSDate *)aDate {

    // start by retrieving day, weekday, month and year components for the given day
    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *todayComponents = [gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:aDate];
    NSInteger theDay = [todayComponents day];
    NSInteger theMonth = [todayComponents month];
    NSInteger theYear = [todayComponents year];

    // now build a NSDate object for the input date using these components
    NSDateComponents *components = [[NSDateComponents alloc] init];
    [components setDay:theDay]; 
    [components setMonth:theMonth]; 
    [components setYear:theYear];
    NSDate *thisDate = [gregorian dateFromComponents:components];
    [components release];

    // build a NSDate object for aDate next day
    NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
    [offsetComponents setDay:1];
    NSDate *nextDate = [gregorian dateByAddingComponents:offsetComponents toDate:thisDate options:0];
    [offsetComponents release];

    [gregorian release];


    // build the predicate 
    NSPredicate *predicate = [NSPredicate predicateWithFormat: @"startDate < %@ && endDate > %@", nextDate, thisDate];

        return predicate;

}

谓词几乎等于@Tony提出的谓词,除了它不检查相等性。这就是原因。假设您的活动从12月8日23:00开始,到12月9日00:00结束。如果您检查结束日期为&gt; =而不是&gt;的事件在给定的一天,您的应用程序将在12月8日和9日报告该事件,这显然是错误的。尝试将这样的事件添加到iCal和Google日历中,您将看到该事件仅在12月8日出现。在实践中,您不应该假设某一天在23:59:59结束(尽管这当然是true):你需要将午夜视为某一天的最后一秒(关于事件的结束)。另请注意,这不会阻止从午夜开始的事件。

答案 1 :(得分:7)

你想要的是:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"startDate <= %@ AND endDate >= %@", endDay, startDay];

换句话说,您将消除在范围结束后或在开始之前结束的事件,即与范围具有空交集的事件。

答案 2 :(得分:1)

虽然提出的解决方案是正确的,但它也非常冗长。我在Swift 3.0中重新实现了它:

import Foundation

extension NSPredicate {

    static func filter(key: String, onDayRangeForDate date: Date, calendar: Calendar = Calendar(identifier: .gregorian)) -> NSPredicate {

        let dateComponents = calendar.dateComponents([.day, .month, .year], from: date)

        let startOfDay = calendar.date(from: dateComponents)!

        let offsetComponents = NSDateComponents()
        offsetComponents.day = 1
        let endOfDay = calendar.date(byAdding: offsetComponents as DateComponents, to: startOfDay)!

        return NSPredicate(format: "\(key) >= %@ && \(key) < %@",
            startOfDay as NSDate, endOfDay as NSDate)
    }
}

答案 3 :(得分:0)

Massimo Camaro有一个很好的答案。我冒昧地将它移植到Swift并稍微修改它以支持不同的列名。

Swift 2.0

func predicateToRetrieveEventsForDate(aDate:NSDate, predicateColumn:String) -> NSPredicate {

    // start by retrieving day, weekday, month and year components for the given day
    let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
    let todayComponents = gregorian?.components([.Day, .Month, .Year], fromDate: aDate)
    let theDay = todayComponents?.day
    let theMonth = todayComponents?.month
    let theYear = todayComponents?.year

    // now build a NSDate object for the input date using these components
    let components = NSDateComponents()
    components.day = theDay!
    components.month = theMonth!
    components.year = theYear!
    let thisDate = gregorian?.dateFromComponents(components)

    // build a NSDate object for aDate next day
    let offsetComponents = NSDateComponents()
    offsetComponents.day = 1
    let nextDate = gregorian?.dateByAddingComponents(offsetComponents, toDate: thisDate!, options: NSCalendarOptions(rawValue: 0))

    // build the predicate
    let predicate = NSPredicate(format: "\(predicateColumn) >= %@ && \(predicateColumn) < %@", thisDate!, nextDate!)

    return predicate
}