使用NSPredicate过滤包含具有NSDate字段的对象的NSArray

时间:2013-11-20 16:47:25

标签: ios objective-c nsdate nspredicate

我有一个对象数组,每个对象都包含一个名为date的字段。我想使用NSPredicate创建此数组的子集,其中我只提取属于特定日期范围的对象。这是我正在使用的代码:

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

我遇到的问题是startDateendDate是同一天,但是时间不同。这是XCode调试器中的变量视图

enter image description here

正如您所看到的,我在数组中有一个date字段等于startDate的对象,但您也可以看到_daysArray在过滤器之后不包含任何元素执行。

当日期不是同一天时,此功能正常。

非常感谢任何帮助。

编辑#1

以下是评论者要求的po变量的predicate

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

编辑#2

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

编辑#3

Martin R.让我走上正确的轨道,评论startDate的计算方法。现在我已经改变了,它正在发挥作用。我正在使用一种方法来计算startDate。以前,我使用NSUIntegerMax来表示组件,现在我将它们特别分解:

-(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];
}

4 个答案:

答案 0 :(得分:2)

从谓词的输出

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

可以看到startDate 完全“2013-11-20 05:00:00 +0000”(UTC)或 “2013-11-20 00:00:00”(EST)但 0.676014秒后。 因此,如果_cachedDaysArray中对象的日期完全落在 2013-11-20的午夜,它匹配谓词。

似乎startDateendDate的计算没有 正是你所期待的。

答案 1 :(得分:1)

在将日期传递给谓词之前,您应该将日期转换为不包括时间。 试试这个:

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

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

}

答案 2 :(得分:1)

试试这个:

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

答案 3 :(得分:0)

我尝试过类似的东西:

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];

它按预期工作。基本上它意味着你在其他地方有错误。