4

I have these two NSDates:

NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:@"MM/dd/yyyy"];
NSDate *rangeStart = [df dateFromString: @"03/03/2013"];
NSDate *rangeEnd = [df dateFromString: @"10/04/2013"];

And this predicate:

request.predicate = [NSPredicate predicateWithFormat:@"createdDate >= %@ AND createdDate <= %@", rangeStart, rangeEnd];

But even though the second part of the predicate specifically uses a <= it is returning objects up until the day before (that is 10/03/2013).

I also tried constructing the predicate like this:

NSPredicate *dateStartPredicate = [NSPredicate predicateWithFormat:@"createdDate >= %@", rangeStart];
NSPredicate *dateEndPredicate = [NSPredicate predicateWithFormat:@"createdDate <= %@", rangeEnd];
NSPredicate *finalPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:dateStartPredicate, dateEndPredicate, nil]]; 

But I'm getting the same results. Am I doing something wrong? Is this actually the expected behavior? If it is, how can set the rangeEnd to the next day?

Thanks

Natan R.
  • 5,141
  • 1
  • 31
  • 48
Jan
  • 2,462
  • 3
  • 31
  • 56
  • This worked for me. NSPredicate *dateStartPredicate = [NSPredicate predicateWithFormat:@"timeStamp >= %@", pastDate]; NSPredicate *dateEndPredicate = [NSPredicate predicateWithFormat:@"timeStamp <= %@", nowDate]; NSPredicate *finalPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:dateStartPredicate, dateEndPredicate, nil]]; – Matthew Ferguson Jun 27 '18 at 00:52

2 Answers2

7

NSDate objects include the time as well. When you pass a date with no time to the dateFromString: method, the midnight of the corresponding day is assumed, i.e. only the items that happen on midnight would return true in the less than or equal expression.

There are two common ways of solving it:

  • Add one day to rangeEnd, and use "less than" instead of "less than or equal", or
  • Add the time to the rangeEnd date (this is not ideal, because you would either need to specify the time in a long string, or miss the items that happened on the last second of the day).

Here is how to use the first approach:

request.predicate = [NSPredicate
    predicateWithFormat:@"createdDate >= %@ AND createdDate < %@"
    , rangeStart
    , [rangeEnd dateByAddingTimeInterval:60*60*24]
];
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Thanks for the description, didn't know this. But did you see the end of my question? `If it is, how can set the rangeEnd to the next day?`could you elaborate on that please? Thanks – Jan Oct 04 '13 at 01:49
2

Remember that even though the name of the class is NSDate, these objects represent specific points in time (not an entire day). rangeEnd is set to midnight of October 4th. Since midnight is the first instant of that day, only events at midnight of that day will be included in your results. Move rangeEnd to the next day as shown below and you should get the results you expect.

NSCalendar* calendar = [NSCalendar autoupdatingCurrentCalendar];
NSDateComponents* components = [[NSDateComponents alloc] init];
components.day = 1;
NSDate* newDate = [calendar dateByAddingComponents:components toDate:rangeEnd options: 0];
Jonathan Arbogast
  • 9,620
  • 4
  • 35
  • 47
  • Thanks for the description, didn't know this. But did you see the end of my question? `If it is, how can set the rangeEnd to the next day?`could you elaborate on that please? Thanks – Jan Oct 04 '13 at 01:49
  • 1
    @Jan This method takes into account leap seconds and all sorts of things where a day might not necessarily equal 24*60*60 seconds. – Jonathan Arbogast Oct 04 '13 at 01:59
  • Thanks a lot. I ended up using @dasblinkenlight answer because of simplicity and he answered first. But I did upvote your answer though. Thanks! – Jan Oct 04 '13 at 02:34