1

I have an NSDate I want to search for in my predicate. However, the timing may not be 2009-12-06 00:00:00 +0000 but may look more like 2009-12-06 3:05:90 +0000. So how can I search for any date that fits within the bounds of that day (24 hours).

Edit:

These are the related methods to the crash:

Crash is happening in this method related to the Calendar's API framework

- (void) reactToTouch:(UITouch*)touch down:(BOOL)down
{
    ...
    if ([marks count] > 0) {
        if([[marks objectAtIndex: row * 7 + column] boolValue])
            [self.selectedImageView addSubview:self.dot];
        else
            [self.dot removeFromSuperview];
    }else{
        [self.dot removeFromSuperview];
    }
    ...
}

This graph sets the graph's date and dots for the date if there is data for that date

- (NSArray*)calendarMonthView:(TKCalendarMonthView *)monthView marksFromDate:(NSDate *)startDate toDate:(NSDate *)lastDate {    
    NSLog(@"calendarMonthView marksFromDate toDate");   

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Session" inManagedObjectContext:self.managedObjectContext];
    [request setEntity:entity];
    [request setResultType:NSDictionaryResultType];
    [request setReturnsDistinctResults:YES];
    [request setPropertiesToFetch :[NSArray arrayWithObject:@"timeStamp"]];

    NSError *error;
    NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error];
    NSArray *tempData = [objects valueForKey:@"timeStamp"];

    NSMutableArray * data1 = [NSMutableArray array];
    NSCalendar * gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
    [gregorian setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];
    NSUInteger flags = ( NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit );
    NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease];
    [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss Z"];
    [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];

    [tempData enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSDate * date = [gregorian dateFromComponents:[gregorian components:flags fromDate:obj]];
        [data1 addObject:[formatter stringFromDate:date]];
    }];
    self.data = data1;

    // Initialise empty marks array, this will be populated with TRUE/FALSE in order for each day a marker should be placed on.
    NSMutableArray *marks = [NSMutableArray array];

    // Initialise calendar to current type and set the timezone to never have daylight saving
    NSCalendar *cal = [NSCalendar currentCalendar];
    [cal setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

    NSDateComponents *comp = [cal components:(NSMonthCalendarUnit | NSMinuteCalendarUnit | NSYearCalendarUnit | NSDayCalendarUnit | NSWeekdayCalendarUnit | NSHourCalendarUnit | NSSecondCalendarUnit) fromDate:startDate];

    NSDate *d = [cal dateFromComponents:comp];

    // Init offset components to increment days in the loop by one each time
    NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
    [offsetComponents setDay:1];    


    // for each date between start date and end date check if they exist in the data array
    while (YES) {
        // Is the date beyond the last date? If so, exit the loop.
        // NSOrderedDescending = the left value is greater than the right
        if ([d compare:lastDate] == NSOrderedDescending) {
            break;
        }

        // If the date is in the data array, add it to the marks array, else don't
        if ([data containsObject:[d description]]) {
            [marks addObject:[NSNumber numberWithBool:YES]];
        } else {
            [marks addObject:[NSNumber numberWithBool:NO]];
        }

        // Increment day using offset components (ie, 1 day in this instance)
        d = [cal dateByAddingComponents:offsetComponents toDate:d options:0];
    }

    [offsetComponents release];
    [request release];
    return [NSArray arrayWithArray:marks];
}

And finally, this is the method we came up with, that pushes to the detail view when a date is selected:

- (void)calendarMonthView:(TKCalendarMonthView *)monthView didSelectDate:(NSDate *)d {
    NSLog(@"calendarMonthView didSelectDate");

    NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"];
    [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];

    NSString * dateString = [formatter stringFromDate:d];

    if ( [data containsObject:dateString] ) 
    {
        NSDate * searchDate = [formatter dateFromString:dateString];
        NSCalendar * calendar1 = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
        NSDateComponents * dateComponents = [[[NSDateComponents alloc] init] autorelease];
        [dateComponents setDay:1];

        NSDate * searchDateEnd = [calendar1 dateByAddingComponents:dateComponents toDate:searchDate options:0];

        NSFetchRequest *request = [[NSFetchRequest alloc]init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Session" inManagedObjectContext:self.managedObjectContext];
        [request setEntity:entity];
        NSPredicate * predicate1 = [NSPredicate predicateWithFormat:@"timeStamp >= %@ AND timeStamp < %@", searchDate, searchDateEnd];
        [request setPredicate: predicate1];
        [request setFetchBatchSize:20];
        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
        [request setSortDescriptors:sortDescriptors];
        NSError *error = nil; 
        NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
        NSLog(@"xxx array: %@", array);
        SessionViewController *sessionViewController = [[SessionViewController alloc] initWithNibName:@"SessionViewController" bundle:nil];
        self.selectedSession = (Session *)[array objectAtIndex:0];
        sessionViewController.selectedSession = self.selectedSession;

        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 
        [dateFormatter setDateFormat:@"MMM d, y"]; 
        NSString *dateString = [dateFormatter stringFromDate:selectedSession.timeStamp]; 
        sessionViewController.title = dateString;
        sessionViewController.hidesBottomBarWhenPushed = YES;
        [self.navigationController pushViewController:sessionViewController animated:YES];

        [sessionViewController release];
        [dateFormatter release];
        [sortDescriptor release];
        [request release];
        [sortDescriptors release];
    }
}

Edit:

When I log the the dates,

The rawDate is straight session.timeStamp and dateString has a date formatter of [dateFormatter setDateFormat:@"eeee, MMM d, y"];

2011-06-27 20:13:44.936 Curl[18714:707] dateString: Monday, Jun 27, 2011
2011-06-27 20:13:44.941 Curl[18714:707] rawString: 2011-06-27 07:05:01 +0000
2011-06-27 20:13:44.961 Curl[18714:707] dateString: Thursday, Jun 23, 2011
2011-06-27 20:13:44.965 Curl[18714:707] rawString: 2011-06-23 08:12:49 +0000
2011-06-27 20:13:44.977 Curl[18714:707] dateString: Wednesday, Jun 22, 2011
2011-06-27 20:13:44.983 Curl[18714:707] rawString: 2011-06-22 21:23:08 +0000
2011-06-27 20:13:44.993 Curl[18714:707] dateString: Tuesday, Jun 21, 2011
2011-06-27 20:13:44.997 Curl[18714:707] rawString: 2011-06-22 03:23:51 +0000
2011-06-27 20:13:45.007 Curl[18714:707] dateString: Monday, Jun 20, 2011
2011-06-27 20:13:45.011 Curl[18714:707] rawString: 2011-06-21 01:03:53 +0000
2011-06-27 20:13:45.020 Curl[18714:707] dateString: Friday, Jun 17, 2011
2011-06-27 20:13:45.024 Curl[18714:707] rawString: 2011-06-18 01:03:53 +0000
Jon
  • 4,732
  • 6
  • 44
  • 67

1 Answers1

2

Adding a day to searchDate can be done with NSDateComponents using the code below.

NSCalendar       * calendar       = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];

NSDateComponents * dateComponents = [[[NSDateComponents alloc] init] autorelease];
[dateComponents setDay:1];

NSDate * searchDateEnd = [calendar dateByAddingComponents:dateComponents
                                                   toDate:searchDate
                                                  options:0];

Add this after you get the searchDate.

You might also want to change the predicate to

NSPredicate * predicate1 = [NSPredicate predicateWithFormat:@"timeStamp >= %@ AND timeStamp < %@", searchDate, searchDateEnd];

Don't check for equality with the upper limit.

Edit

If you look at the code below,

if ([marks count] > 0) {
    if([[marks objectAtIndex: row * 7 + column] boolValue])
        [self.selectedImageView addSubview:self.dot];
    else
        [self.dot removeFromSuperview];
}

You don't have checks on the index and since this seems to represent values pertaining to days in a month, I would suggest you check if there is a valid value.

int size = [marks count];
if ((size > 0) && ((row * 7 + column) < size)) {
    if([[marks objectAtIndex: row * 7 + column] boolValue])
        [self.selectedImageView addSubview:self.dot];
    else
        [self.dot removeFromSuperview];
}
Community
  • 1
  • 1
Deepak Danduprolu
  • 44,595
  • 12
  • 101
  • 105
  • Thanks I think this worked but I'm getting error and crash `*** -[NSDateComponents release]: message sent to deallocated instance 0x5d834c0` afterwords. I'm updating question with code can you check it out? – Jon Jun 28 '11 at 00:18
  • Remove `[dateComponents release];` and `[calendar1 release];`. They are already autoreleased. Or if you want you can remove the `autorelease` message. – Deepak Danduprolu Jun 28 '11 at 00:22
  • Nevermind, I noticed you made them autorelease, thats why. I'm still getting a crash when I select some dates though. – Jon Jun 28 '11 at 00:23
  • It just crashes when I select certain dates and says `*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSArray objectAtIndex:]: index 35 beyond bounds [0 .. 34]'` It only happens sometimes though. – Jon Jun 28 '11 at 00:28
  • Can you post the crash log? What is the condition when this happens? Does an event exist for that date? Does it happen in this method? or Does it happen in another method? I don't see the possibility of accessing index `35` in this code. – Deepak Danduprolu Jun 28 '11 at 00:31
  • Updated my answer. See the edit. But why are you getting `row` and `column` that generate the wrong index? Are there empty rows on the top that you haven't accounted for? – Deepak Danduprolu Jun 28 '11 at 00:46
  • Thanks Deepak, the method with `if ([marks count] > 0)` was not written by me, but rather its part of the Tapku Library Calendar framework. – Jon Jun 28 '11 at 00:52
  • I can't be sure as I haven't used this library before. But shouldn't it be `[marks objectAtIndex:day]`? – Deepak Danduprolu Jun 28 '11 at 01:06
  • hmm, then it must be something wrong with my code, because this is a pretty popular framework and I doubt it would have something wrong in its code. – Jon Jun 28 '11 at 01:09
  • @Jon can you change `NSDateComponents *comp = [cal components:(NSMonthCalendarUnit | NSMinuteCalendarUnit | NSYearCalendarUnit | NSDayCalendarUnit | NSWeekdayCalendarUnit | NSHourCalendarUnit | NSSecondCalendarUnit) fromDate:startDate];` to `NSDateComponents *comp = [cal components:(NSMonthCalendarUnit | NSYearCalendarUnit | NSDayCalendarUnit ) fromDate:startDate];` and check. Additionally, Try logging the `startDate`, `endDate` and all the dates that you iterate in your `marksFromDate:toDate:` data source method? – Deepak Danduprolu Jun 28 '11 at 01:27
  • I just logged the `self.data` array and got `data array: ( "2011-06-11 00:00:00 +0000", "2011-06-14 00:00:00 +0000", "2011-06-16 00:00:00 +0000", "2011-06-21 00:00:00 +0000", "2011-06-23 00:00:00 +0000", "2011-06-26 00:00:00 +0000"` – Jon Jun 28 '11 at 01:50
  • I also noticed that sometimes, When I select a date, the date that it pushes to in the next view is not the same date, but this only happens sometimes. – Jon Jun 28 '11 at 02:08
  • ACTUALLY, when I comment out the code that pushes to the new view, and just do `NSLog(@"the array: %@", array);`, the output is the correct dates. I seem to only get the crash when I push the view, and it crashes at `if([[marks objectAtIndex: row * 7 + column] boolValue])` – Jon Jun 28 '11 at 02:16
  • Ok another update, I have limited the error down to this line: `[self.navigationController pushViewController:sessionViewController animated:YES];`. When I change animated from `YES` to `NO`, it works fine and there is no crash. Do you know of any way around this because it looks extremley ugly with no animation. – Jon Jun 28 '11 at 02:31
  • I am not sure if that is your problem. Since you seem to be having out of bounds problem which means that code filling `marks` isn't correct. I am thinking you are doing one less date than necessary in your `calendarMonthView:marksFromDate:toDate:` method. Hence I asked you to log the dates that are being passed as arguments - `startDate` and `lastDate`. – Deepak Danduprolu Jun 28 '11 at 02:49
  • Ok I think I may have found the problem. It seems like something is off in terms of timezone. The dots are sometimes one day off. It think we made a mistake with the date conversions and somtimes it is marking one day forward instead of the actual day. IT may be something to do with GMT or something. – Jon Jun 28 '11 at 02:51
  • Please look here http://stackoverflow.com/questions/6501032/problem-with-nsdate-formatter – Jon Jun 28 '11 at 02:53
  • Try logging the dates you are getting from your library and see if they are what you are expecting. The code we've is expecting GMT whereas the library could be providing us data in the local time zone. – Deepak Danduprolu Jun 28 '11 at 02:56
  • I mean it could be providing you 2011-06-28 00:00:00 -0800 PST. – Deepak Danduprolu Jun 28 '11 at 02:58
  • Ya, when I log the dates using a plain NSDateFormatter, they are coming up with my local time and date. But it seems as though the calendar is logging the marks on GMT time, and not local time? – Jon Jun 28 '11 at 03:03
  • Just upated in question with log – Jon Jun 28 '11 at 03:14
  • Hey Deepak, I think after we converted the numbers to remove the time, hour, and second intervals, we need to convert them back to our local time zone, so it shows up on the calendar for the local time zone's date, not the GMT date. What do you think? – Jon Jun 28 '11 at 04:20
  • Well at this point I don't know. It is a tad confusing and I can't say anything for sure without looking at the entire project which I don't have time for. I hope someone else on SO helps you figure this out. – Deepak Danduprolu Jun 28 '11 at 05:52
  • Thanks Deepak, but I'm pretty sure thats the problem if you could just help me add that line of code. I think I just need to convert the date back my local timezone after removing the hour and second intervals. So right before I do `\self.data = data1;`, I need to convert it to local time in dateformatter. – Jon Jun 28 '11 at 05:56