这比仅仅更复杂一点
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)
如何编写谓词以确定该事件是否属于该日期范围。请记住,事件可以在日期范围之前和之后开始。
谢谢!
答案 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并稍微修改它以支持不同的列名。
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
}