NSPredicate - для поиска событий, которые происходят между определенным диапазоном дат - PullRequest
3 голосов
/ 03 декабря 2011

Это немного сложнее, чем просто

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

Я создаю приложение календаря, и мне нужно извлечь события из моего основного хранилища данных, которые происходят в определенный день.Однако событие может начинаться / заканчиваться не в тот день, который я пытаюсь отобразить, например,

Мой диапазон дат (начало = 2011-12-02 00:00:00 до конца= 2011-12-02 23:59:59)

Событие (начало = 2011-12-01 с 23:00:00 до конца = 2011-12-02 05:00:00)

Как я могу написать предикат, чтобы определить, попадает ли это событие в этот диапазон дат.Помните, что событие может начаться до и после диапазона дат.

Спасибо!

Ответы [ 4 ]

17 голосов
/ 08 декабря 2011

Вот метод построения предиката для извлечения неповторяющихся событий, происходящих в данный день (повторяющиеся события требуют дополнительной обработки):

- (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, за исключением того, что это не проверяет на равенство.Вот почему.Предположим, у вас есть мероприятие, начинающееся 8 декабря 23:00 и заканчивающееся 9 декабря 00:00.Если вы проверяете события с датой окончания> =, а не> данного дня, ваше приложение сообщит о событии как 8, так и 9 декабря, что явно неверно.Попробуйте добавить такое событие как в iCal, так и в Календарь Google, и вы увидите, что это событие появляется только 8 декабря. На практике не следует предполагать, что данный день заканчивается в 23:59:59 (даже если это, конечно,true): вам нужно относиться к полуночи как к последней секунде данного дня (относительно конца события).Также обратите внимание, что это не предотвращает события, начинающиеся в полночь.

7 голосов
/ 05 декабря 2011

То, что вы хотите:

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

Другими словами, вы исключаете события, которые начинаются после конца диапазона или заканчиваются до начала, то есть события, которые имеют пустое пересечение с диапазоном.

1 голос
/ 08 марта 2017

Хотя предложенное решение является правильным, оно также довольно многословно.Я повторно реализовал его в 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)
    }
}
0 голосов
/ 04 января 2016

У Массимо Камаро был отличный ответ. Я позволил себе перенести его в 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
}
...