简体   繁体   中英

Filtering a NSArray containing objects with a NSDate field with NSPredicate

I have an array of objects, with each object containing a field called date. I want to create a subset of this array using NSPredicate where I pull only those objects that fall within a certain date range. Here's the code I am using:

predicate = [NSPredicate predicateWithFormat:@"(date >= %@) AND (date =< %@)", startDate, endDate];
_daysArray = [_cachedDaysArray filteredArrayUsingPredicate:predicate];

The problem I am having is when startDate and endDate are the same day, but different times. Here's the variables view in the XCode debugger

在此处输入图片说明

As you can see, I have an object in the array that has a date field equal to startDate , but you can also see that _daysArray contains no elements after the filter is executed.

This works fine when the dates are not the same day.

Any help would be greatly appreciated.

EDIT #1

Here's the po of the predicate variable as requested by commenter:

(lldb) po predicate
date >= CAST(406616400.676014, "NSDate") AND date <= CAST(406702799.677098, "NSDate")
(lldb)

EDIT #2

(lldb) po [[_cachedDaysArray firstObject] date]
2013-11-18 05:00:00 +0000
(lldb) 

EDIT #3

Martin R. got me on the right track with his comment on how startDate was being calculated. Now that I have changed that, it's working. I am using a method to calculate startDate . Previously, I was using NSUIntegerMax to indicate the components, now I am breaking them out specifically:

-(NSDate *)pastDate:(NSInteger)daysPast
{
    // Get the appropriate calendar
    NSCalendar *calendar = [NSCalendar currentCalendar];

    NSDateComponents *startComponents = [calendar components:(NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitYear|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond) fromDate:[NSDate date]];
    [startComponents setHour: 00];
    [startComponents setMinute: 00];
    [startComponents setSecond: 00];
    startComponents.day = startComponents.day - daysPast;

    return [calendar dateFromComponents:startComponents];
}

From the output of the predicate

date >= CAST(406616400.676014, "NSDate") AND date <= CAST(406702799.677098, "NSDate")

one can see that startDate is not exactly "2013-11-20 05:00:00 +0000" (UTC) or "2013-11-20 00:00:00" (EST) but 0.676014 seconds later . Therefore, if the date of the object in _cachedDaysArray falls exactly on midnight of 2013-11-20, it does not match the predicate.

It seems that the calculation of startDate and endDate does not exactly what you expect.

You should convert your dates to do not include time, before you pass it to your predicate. Try this:

-(NSDate*)dateNoTime:(NSDate*)myDate
{

NSDateComponents *comp = [[NSCalendar currentCalendar] components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate: myDate];
return [[NSCalendar currentCalendar] dateFromComponents:comp];

}

Try this:

predicate =[NSPredicate predicateWithBlock:^BOOL(TRDay *evaluatedObject, NSDictionary *bindings) {
    return evaluatedObject.date.timeIntervalSince1970 >= startDate.timeIntervalSince1970 && evaluatedObject.date.timeIntervalSince1970 <= endDate.timeIntervalSince1970;
}];

I tried something like that:

NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSDateComponents *components3 = [[NSDateComponents alloc] init];
[components3 setYear:2013];
[components3 setMonth:11];
[components3 setDay:20];
NSDate *startDate = [gregorianCalendar dateFromComponents:components3];

[components3 setHour:23];
[components3 setMinute:59];
[components3 setSecond:59];

NSDate *endDate = [gregorianCalendar dateFromComponents:components3];
[gregorianCalendar release];

NSDictionary* dict = [NSDictionary dictionaryWithObjects:@[startDate] forKeys:@[@"date"]];
NSArray* mainArray = [NSArray arrayWithObjects:dict, nil];

NSPredicate* pred = [NSPredicate predicateWithFormat:@"(date >= %@) AND (date =< %@)", startDate, endDate];
NSArray* filteredArray = [mainArray filteredArrayUsingPredicate:pred];

And it works as expected. Basically it means you have error somewhere else.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM