Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSDate best practice in creating a date with no time element

I am writing an app which uses core-data to store my data. Included in this is a date field of which I am only interested in the date not the time. I need to select records based on the date (not time) and so I have created a category on NSDate to return a date, normalised to a set time as follows:

+ (NSDate *)dateWithNoTime:(NSDate *)dateTime {
if( dateTime == nil ) {
    dateTime = [NSDate date];
}
NSDateComponents* comps = [[NSCalendar currentCalendar] components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:dateTime];
NSDate *dateOnly = [[NSCalendar currentCalendar] dateFromComponents:comps];
[dateOnly dateByAddingTimeInterval:(60.0 * 60.0 * 12.0)];           // Push to Middle of day.
return dateOnly;

}

I then use this when I add data to the core-data store (I have a setter which uses this method to set the primitive date value) and then I use this method to create a date that I use to compare the dates when performing a fetch request. So in theory this should always work - ie pick out the dates I'm looking for.

I'm slightly nervous though as I'm not totally sure what effect changing time-zone or locale will have. Will it still work ?

What is considered best practice when storing and searching on a date only when you aren't interested in the time.

Cheers.

EDIT

After reading the discussion recommended I think that I should modify my code to be as follows. The thinking being that if I ensure that I push it to a specific calendar system and a specific timezone (UTC) then the dates should always be the same regardless of where you are when you set the date and when you read the date. Any comments on this new code appreciated.

+ (NSDate *)dateWithNoTime:(NSDate *)dateTime {
if( dateTime == nil ) {
    dateTime = [NSDate date];
}

NSCalendar       *calendar   = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[calendar setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease];
components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit
                         fromDate:dateTime];

NSDate *dateOnly = [calendar dateFromComponents:components];

[dateOnly dateByAddingTimeInterval:(60.0 * 60.0 * 12.0)];           // Push to Middle of day.
return dateOnly;

}

like image 239
SimonB Avatar asked Dec 01 '10 15:12

SimonB


2 Answers

You have a few issues to deal with here. First, as you noted, timezones. You also need to worry about daylight savings, which change the concept of “midday.”

Take a look at this discussion on CocoaDev where an Apple Engineer gives some answers and discusses some best practices.

like image 102
Count Chocula Avatar answered Nov 09 '22 04:11

Count Chocula


A simpler solution is to use a predicate that looks for dates within a certain range.

Use NSCalendarComponent to create a start date and an end date for you "day" and then include those in the predicate.

NSPredicate *p=[NSPredicate predicateWithFormat:@"$@ <= date <= $@",startDate,endDate];

That will provide the maximum flexibility without to much complexity. Date and time programing is deceptively complex. Be prepared to do some work.

like image 41
TechZen Avatar answered Nov 09 '22 03:11

TechZen