iPhone: method to calculate days till next birthday not accurate --? - iphone

Here's a puzzler. I use the following to calculate the number of days between today's date and an upcoming birthday:
-(int) daysTillBirthday: (NSDate*)aDate {
// check to see if valid date was passed in
//NSLog(#"aDate passed in is %#",aDate);
if (aDate == nil) {
//NSLog(#"aDate is NULL");
return -1; // return a negative so won't be picked in table
}
//** HOW MANY DAYS TO BDAY
NSDate *birthDay = aDate; // [calendar dateFromComponents:myBirthDay];
//NSLog(#"birthDay: %#, today: %#",birthDay, [NSDate date]);
NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDateComponents *thisYearComponents = [calendar components:NSYearCalendarUnit fromDate:[NSDate date]];
NSDateComponents *birthDayComponents = [calendar components:NSMonthCalendarUnit|NSDayCalendarUnit fromDate:birthDay];
[birthDayComponents setYear:[thisYearComponents year]];
NSDate *birthDayThisYear = [calendar dateFromComponents:birthDayComponents];
//NSLog(#"birthDayThisYear: %#",birthDayThisYear);
NSDateComponents *differenceHours = [calendar components:NSHourCalendarUnit fromDate:[NSDate date] toDate:birthDayThisYear options:0];
NSDateComponents *differenceDays = [calendar components:NSDayCalendarUnit fromDate:[NSDate date] toDate:birthDayThisYear options:0];
// NSLog(#"difference days: %i, hours %i",[differenceDays day],[differenceHours hour]);
//*** I added this to try and correct the "error" ***
if ([differenceDays day] == 0) { // is it today, or tomorrow?
if (([differenceHours hour] <= 0) && ([differenceHours hour] >= -24)) { // must be today
//NSLog(#"TODAY");
return (0);
[calendar release];
}else if (([differenceHours hour] >= 0) && ([differenceHours hour] <= 24)) {
//NSLog(#"TOMORROW");
return (1);
[calendar release];
}
}
if ([differenceDays day] < 0) {
// this years birthday is already over. calculate distance to next years birthday
[birthDayComponents setYear:[thisYearComponents year]+1];
birthDayThisYear = [calendar dateFromComponents:birthDayComponents];
differenceDays = [calendar components:NSDayCalendarUnit fromDate:[NSDate date] toDate:birthDayThisYear options:0];
}
return ([differenceDays day]);
[calendar release];
}
Everything works, but the results are not accurate! I often find that birthdays that are close to today, but one day apart, result in [differenceDays day] being the same! i.e. if today is 6/6/2011 and I have two birthdays, one on 6/7/2011 and another 6/8/2011, then they are both shown as 1 day away!
Anyone have any better methods for accurately calculating this, or can spot the problem?
Many thanks.

NSCalendar provides a much easier way to do this:
NSDate *birthday = ...; // the birthday
NSDate *today = [NSDate date];
NSCalendar *c = [NSCalendar currentCalendar];
NSInteger birthdayDayOfYear = [c ordinalityOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:birthday];
NSInteger todayDayOfYear = [c ordinalityOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:today];
NSInteger different = birthdayDayOfYear - todayDayOfYear;
Basically, we're figuring out how far into the year today and the target date are (ie, today [5 Jun] is the 156th day of the year), and then subtract them to figure out how many days are in between them.
This method, of course, relies on the assumption that the target date is in the same year as the current date. I think it'd be fairly easy to work around that, however.
Another, even easier way to do this that will account for multi-year differences is like this:
NSDateComponents *d = [[NSCalendar currentCalendar] components:NSDayCalendarUnit fromDate:today toDate:birthday options:0];
NSInteger difference = [d day];
If you need to make sure that the birthday is in the future, that's easily accomplished as well:
NSDateComponents *year = [[[NSDateComponents alloc] init] autorelease];
NSInteger yearDiff = 1;
NSDate *newBirthday = birthday;
while([newBirthday earlierDate:today] == newBirthday) {
[year setYear:yearDiff++];
newBirthday = [[NSCalendar currentCalendar] dateByAddingComponents:year toDate:birthday options:0];
}
//continue on with the 2-line calculation above, using "newBirthday" instead.
update I updated the loop above to always increment from the original date n years at a time, instead of year-by-year. If someone is born on 29 Feb, incrementing by one year would yield 1 Mar, which would be wrong once you got to a leap year again. By jumping from the original date each time, we don't have this issue.

I do the exact same thing in one of my apps. Here is how I do it:
//This is the date your going to - in your case the birthday - note the format
NSString *myDateAsAStringValue = #"20110605";
// Convert string to date object
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyyMMdd"];
NSDate *newDate = [dateFormat dateFromString:myDateAsAStringValue];
NSDateComponents *dateComp = [[NSDateComponents alloc] init];
NSCalendar *Calander = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps=[[NSDateComponents alloc] init];
unsigned int unitFlags = NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
dateComp = [Calander components:unitFlags fromDate:[NSDate date]];
[dateFormat setDateFormat:#"dd"];
[comps setDay:[[dateFormat stringFromDate:[NSDate date]] intValue]];
[dateFormat setDateFormat:#"MM"];
[comps setMonth:[[dateFormat stringFromDate:[NSDate date]] intValue]];
[dateFormat setDateFormat:#"yyyy"];
[comps setYear:[[dateFormat stringFromDate:[NSDate date]] intValue]];
[dateFormat setDateFormat:#"HH"];
[comps setHour:05];
[dateFormat setDateFormat:#"mm"];
[comps setMinute:30];
NSDate *currentDate=[Calander dateFromComponents:comps];
dateComp = [Calander components:unitFlags fromDate:newDate];
[dateFormat setDateFormat:#"dd"];
[comps setDay:[[dateFormat stringFromDate:newDate] intValue]];
[dateFormat setDateFormat:#"MM"];
[comps setMonth:[[dateFormat stringFromDate:newDate] intValue]];
[dateFormat setDateFormat:#"yyyy"];
[comps setYear:[[dateFormat stringFromDate:newDate] intValue]];
[dateFormat setDateFormat:#"HH"];
[comps setHour:05];
[dateFormat setDateFormat:#"mm"];
[comps setMinute:30];
NSDate *reminderDate=[Calander dateFromComponents:comps];
NSTimeInterval ti = [reminderDate timeIntervalSinceDate:currentDate];
int days = ti/86400;
return days;

I think I have found a solution. Checking the output carefully, it appears to all come down to the difference in HOURS. For example: comparing today with tomorrow's date might end up being, say, 18 hours away. This results in [difference day] being set at 0 i.e. it thinks tomorrow is today because it is less than 24 hours away.
You can see the fix below. I take the number of hours e.g. 18 and divide by 24 (to get the number of days). In this case 18/24 = 0.75. I then round this up i.e. to "1." So while [difference days] thinks tomorrow is today, by rounding up the hours, you know it is in fact tomorrow.
-(int) daysTillBirthday: (NSDate*)aDate {
// check to see if valid date was passed in
//NSLog(#"aDate passed in is %#",aDate);
if (aDate == nil) {
//NSLog(#"aDate is NULL");
return -1; // return a negative so won't be picked in table
}
//** HOW MANY DAYS TO BDAY
NSDate *birthDay = aDate; // [calendar dateFromComponents:myBirthDay];
NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDateComponents *thisYearComponents = [calendar components:NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:[NSDate date]];
NSDateComponents *birthDayComponents = [calendar components:NSMonthCalendarUnit|NSDayCalendarUnit fromDate:birthDay];
NSInteger timeNow = [thisYearComponents hour];
[birthDayComponents setYear:[thisYearComponents year]];
[birthDayComponents setHour:timeNow];
NSDate *birthDayThisYear = [calendar dateFromComponents:birthDayComponents];
//NSLog(#"today %#, birthday %#",[NSDate date],birthDayThisYear);
NSDateComponents *difference = [calendar components:NSDayCalendarUnit fromDate:[NSDate date] toDate:birthDayThisYear options:0];
NSDateComponents *differenceHours = [calendar components:NSHourCalendarUnit fromDate:[NSDate date] toDate:birthDayThisYear options:0];
double daysFromHours = ((double)[differenceHours hour])/24; // calculate number of days from hours (and round up)
int roundedDaysFromHours = ceil(daysFromHours);
NSLog(#"daysFromHours %.02f, roundedDaysFromHours %i",daysFromHours,roundedDaysFromHours);
if ([difference day] < 0) {
// this years birthday is already over. calculate distance to next years birthday
[birthDayComponents setYear:[thisYearComponents year]+1];
birthDayThisYear = [calendar dateFromComponents:birthDayComponents];
difference = [calendar components:NSDayCalendarUnit fromDate:[NSDate date] toDate:birthDayThisYear options:0];
}
//NSLog(#"%i days until birthday", [difference day]);
return (roundedDaysFromHours);
[calendar release];
}

Related

How to subtract 30 minutes from a date

I am getting date value date: 2013-06-07 18:30:00 +0000. I am trying to subtract 30 minutes from this, but it not giving proper time.
I've tried this
NSTimeInterval secondsPerHour = 60*30;
NSDate *newDate = [date dateByAddingTimeInterval:-secondsPerHour];
Reason : The negative of a unsigned int is a positive number. This should work:
NSDate *newDate = [date dateByAddingTimeInterval:-(NSTimeInterval)((u_int32_t)30*60)];
EDIT : Alternative use NSDateComponents.
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
[dateComponents setMinute:-30];
NSDate *halfHourAgo = [[NSCalendar currentCalendar] dateByAddingComponents:dateComponents toDate:yourDateHere options:0];
NSLog(#"\ncurrentDate: %# \n halfHourAgo: %#", yourDateHere, halfHourAgo);
in Swift 5
let minus30Min = Date().addingTimeInterval(-30 * 60)
you can use:
NSDate *minusOneHr = [[NSDate date] dateByAddingTimeInterval:-30*60];
You could try NSDateComponents, something like that:
NSDate* yourDate = [NSDate dateWithTimeIntervalSinceNow:0]; // your date, to substract 30 mins
NSDateComponents *dc = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit) fromDate:yourDate];
[dc setMinute:[dc minute] - 30];
NSDate* date = [[NSCalendar currentCalendar] dateFromComponents:dc]; // 30 mins before now
Or that:
NSDate* yourDate = [NSDate dateWithTimeIntervalSinceNow:0]; // your date, to substract 30 mins
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setMinute:-30];
NSDate* date = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:yourDate options:0]; // 30 mins before now
Copy paste this code in viewDidLoad and check the results first.
NSDate *today = [[NSDate alloc] init];
NSLog(#"Today %#", today);
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setMinute:-30]; // If you want to change Month,Year,Day,Hour etc. Just change the "setMinute" to setMonth,setYear,setDay,setHour
NSDate *dateComp = [gregorian dateByAddingComponents:offsetComponents toDate:today options:0];
NSLog(#"Desired Day %#", dateComp);
let dateMinusThirtyMinutes = Date().subtract(30.minutes)

Check if NSDate is in this week or next week

there is a way to check if an NSDate is this week or is next week?
i know that today is:
[NSDate date]
and then how i can do?
Use NSDateComponents, something like this:
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *today = [NSDate date];
NSDateComponents *todaysComponents =
[gregorian components:NSWeekCalendarUnit fromDate:date];
NSUInteger todaysWeek = [todaysComponents week];
NSDate *anotherDate = [NSDate date];
NSDateComponents *otherComponents =
[gregorian components:NSWeekCalendarUnit fromDate:anotherDate];
NSUInteger anotherWeek = [otherComponents week];
if(todaysWeek==anotherWeek){
NSLog(#"another date is this week");
}else if(todaysWeek+1==anotherWeek){
NSLog(#"another date is next week")
}
You can also use other components like month or year to be completely sure.
NOTE: Don't use timeIntervals. By using NSDateComponents you ignore the hour, minutes and seconds. I think you want that.
pass the 2 dates to this method:
- (BOOL) isSameWeekAsDate: (NSDate *) aDate andDate:(NSDate *) bDate
{
NSDateComponents *components1 = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit | NSWeekdayOrdinalCalendarUnit) fromDate:aDate];
NSDateComponents *components2 = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit | NSWeekdayOrdinalCalendarUnit) fromDate:bDate];
if ([components1 week] != [components2 week]) return NO;
//return (abs([self timeIntervalSinceDate:aDate]) < 604800); // ops, forgot to change "self" with parameter "bDate":
return (abs([bDate timeIntervalSinceDate:aDate]) < 604800);
}
EDIT:
call it with 2 dates of different years:
[components setDay:31];
[components setMonth:12];
[components setYear:2010];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date1 = [gregorian dateFromComponents:components];
// second date (SATURDAY -of the same week, other year...)
[components setDay:1];
[components setMonth:1];
[components setYear:2011];
NSDate *date2 = [gregorian dateFromComponents:components];
if ([self isSameWeekAsDate:date1 andDate:date2]) {
NSLog(#"Same Week!");
}else{
NSLog(#"OTHER WEEK!");
}
I'am adding my solution (implemented as an NSDate category), which doesn't use week, which is deprecated in iOS7, iOS8
- (NSDate *)firstDateOfWeek {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startOfWeek;
[calendar rangeOfUnit:NSCalendarUnitWeekOfYear startDate:&startOfWeek interval:NULL forDate:self];
return startOfWeek;
}
- (BOOL)isSameWeekWithDate:(NSDate *)date {
if (ABS(self.timeIntervalSince1970 - date.timeIntervalSince1970) > (7 * 24 * 60 * 60)) {
return NO;
}
return ([[self firstDateOfWeek] timeIntervalSince1970] == [[date firstDateOfWeek] timeIntervalSince1970]);
}
- (BOOL)isThisWeek {
return [self isSameWeekWithDate:[NSDate new]];
}
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:NSWeekCalendarUnit fromDate:yourDate];
NSInteger week = [components week]; // here your have a Integer with the weeknr of yourDate
components = [cal components:NSWeekCalendarUnit fromDate:[NSDate date]];
weekToday = [components week]; // here your have a Integer with the weeknr of today
The rest you should be able to do.
It might get another brainwork when it comes to the last week in year.
Clear?
NOTE: this calculates the idea of "week" using a Sunday-Saturday concept of week.
To calculate the current day of the week use the following from here:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents =[gregorian components:NSWeekdayCalendarUnit fromDate:dateOfInterest];
NSInteger weekday = [weekdayComponents weekday];
// weekday 1 = Sunday for Gregorian calendar
[gregorian release];
The rest should be pretty trivial. Get the current date and the date from your NSDate. Find the start and end dates using the day and the date (if today is monday then date - 1 day is the first day of the week etc). Figure out which week the date is in.
For iOS 7 and above, you have to replace NSWeekCalendarUnit with NSCalendarUnitWeekOfYear
- (NSInteger)thisW:(NSDate *)date
{
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *todaysComponents = [gregorian components:NSCalendarUnitWeekOfYear fromDate:[NSDate date]];
NSUInteger todaysWeek = [todaysComponents weekOfYear];
NSDateComponents *otherComponents = [gregorian components:NSCalendarUnitWeekOfYear fromDate:date];
NSUInteger datesWeek = [otherComponents weekOfYear];
//NSLog(#"Date %#",date);
if(todaysWeek==datesWeek){
//NSLog(#"Date is in this week");
return 1;
}else if(todaysWeek+1==datesWeek){
//NSLog(#"Date is in next week");
return 2;
} else {
return 0;
}
}
Forget about date components, you have to tweak lots of things (check if it's the last week of the year, check if Sunday is the beginning of the week...) and is prone to errors and spaghetti code.
This solves your issue and is extended to detect "last week", "previous weeks", "this week", "next week" and "later weeks"
-(EventWeekRange)numberOfWeeksFromTodayToEvent:(NSDate *)eventDate {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSComparisonResult comparison = [calendar compareDate:[NSDate date] toDate:eventDate toUnitGranularity:NSCalendarUnitWeekOfYear];
if (comparison == NSOrderedSame) {
return RangeThisWeek;
} else if (comparison == NSOrderedAscending) { // The event date is in the future
// Advance today's date one week to check if this new date is in the same week as the event
NSDate *todaysNextWeek = [[NSDate date]dateByAddingTimeInterval:60*60*24*7];
if ([calendar compareDate:todaysNextWeek toDate:eventDate toUnitGranularity:NSCalendarUnitWeekOfYear] == NSOrderedSame) {
return RangeNextWeek;
} else {
return RangeLater;
}
} else { // The event date is in the past
// Advance the event's date one week to check if this new date is in the same week as today
NSDate *eventsNextWeek = [eventDate dateByAddingTimeInterval:60*60*24*7];
if ([calendar compareDate:eventsNextWeek toDate:[NSDate date] toUnitGranularity:NSCalendarUnitWeekOfYear] == NSOrderedSame) {
return RangeLastWeek;
} else {
return RangeEarlier;
}
}
}

How to get n-th Sunday date of the month in objective c?

How to get first sunday or nth sunday or monday (any day) date in Objective C coding.
For example :- I just want to show date of friendship day in my app every year.
But, friendship comes 1st Sunday of Aug. So, date will change every year. here I need to find what is the date of 1st Sunday in Aug every year.
is there any logic to find the date of nth Sunday.
You need to create a NSDateComponents object and set the appropriate values,
In You example you would do:
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
dateComponents.year = 2011; // set the current year or whatever year you want here
dateComponents.month = 8;
dateComponents.weekday = 1; // sunday is 1, monday is 2, ...
dateComponents.weekdayOrdinal = 1; // this means, the first of whatever weekday you specified
To convert this into a NSDate-object, you just do:
//you may want to use another calendar object here
NSDate *myDate = [[NSCalendar currentCalendar] dateFromComponents:dateComponents];
[dateComponents release]; //don't forget memory management ;)
You want to use NSDateComponents in combination with NSCalendar's dateFromComponents: method:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *c = [[NSDateComponents alloc] init];
[c setYear:2012];
[c setWeekday:1]; // Sunday
[c setWeekdayOrdinal:3]; // The 3rd Sunday
for (int i = 1; i <= 12; i++) {
[c setMonth:i];
NSDate *date = [gregorian dateFromComponents:c];
NSLog(#"Date[%i]: %#",i,date);
}
[c release];
[gregorian release];
Here's an example. You can find more information in the Date and Time Programming Guide.
To find the first Sunday in August:
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setWeekday:1]; // Sunday
[components setWeekdayOrdinal:1]; // The first Sunday in the month
[components setMonth:8]; // August
[components setYear:2011];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [gregorian dateFromComponents:components];
you check the first day of the month.
unsigned units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorian components:units fromDate:date];
Get the day using [components day]; <-1 for Sunday.After this it's simple.
You'll want to review the NSCalendar class here.
Of specific interest will be the method - (NSUInteger)ordinalityOfUnit:(NSCalendarUnit)smaller inUnit:(NSCalendarUnit)larger forDate:(NSDate *)date

How can we get Next Date From Entered Date based on given repeate period ?

I am new to iPhone.
I want to find out the next date from given date based on repeat period.
For example :
I want function as follows ...
given date : 31'May 2011 and Repeat : Monthly given as argument then the next date should be returned 31'July 2011 (as June don't have 31st day)
And function should be smart enough to to calculate next leap year day also, if given date : 29'Feb 2008 and Repeat : Yearly given as argument then the next date should be returned 29'Feb 2012 (The next leap year day)
And so on repeat option can be one of these : Daily, Weekly(On selected day of week), Monthly, Yearly, None(No repeat at all)
// start by retrieving day, weekday, month and year components for yourDate
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *todayComponents = [gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) yourDate];
NSInteger theDay = [todayComponents day];
NSInteger theMonth = [todayComponents month];
NSInteger theYear = [todayComponents year];
// now build a NSDate object for yourDate using these components
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:theDay];
[components setMonth:theMonth];
[components setYear:theYear];
NSDate *thisDate = [gregorian dateFromComponents:components];
[components release];
// now build a NSDate object for the next day
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setDay:1];
NSDate *nextDate = [gregorian dateByAddingComponents:offsetComponents toDate: yourDate options:0];
[offsetComponents release];
[gregorian release];
This is copied from How can i get next date using NSDate? and the credit goes to #Massimo Cafaro for this answer.
To get tomorrow's date use the dateByAddingTimeInterval method.
// Start with today
NSDate *today = [NSDate date];
// Add on the number of seconds in a day
NSTimeInterval oneDay = 60 * 60 * 24;
NSDate *tomorrow = [today dateByAddingTimeInterval:oneDay];
It's pretty simple to extend that to a week etc
NSTimeInterval oneWeek = oneDay * 7;
NSDate *nextWeek = [today dateByAddingTimeInterval:oneWeek];
try this :-
- (NSDate *)dateFromDaysOffset:(NSInteger)daysOffset
{
// start by retrieving day, weekday, month and year components for yourDate
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setDay:daysOffset];
NSDate *offsetDate = [gregorian dateByAddingComponents:offsetComponents toDate:self options:0];
[offsetComponents release];
[gregorian release];
return offsetDate;
}

Core Data - Predicates with NSDates

Hallo,
I am working on a Core Data app and have to do some filtering based on dates. I've run some testing and it appears that when comparing NSDates, Core Data is comparing the time component of the dates as well.
My code:
- (BOOL)hasSpeakersWithinDateRangeFrom:(NSDate *)startOfRange through:(NSDate *)endOfRange {
NSPredicate* dateRangePredicate = [NSPredicate predicateWithFormat:#"startOn <= %# && endOn >= %#", startOfRange, endOfRange];
NSSet* speakersWithinDateRange = [self.speakers filteredSetUsingPredicate:dateRangePredicate];
if ([speakersWithinDateRange count] > 0)
return YES;
else
return NO;
}
and I have a "convenience" method that is a one-line'er:
- (BOOL)hasSpeakersNow {
return [self hasSpeakersWithinDateRangeFrom:[NSDate date] through:[NSDate date]];
}
When I run some basic testing, it doesn't work as planned, and from what I can tell Core Data is comparing the time components of the NSDate objects along with the dates.
So, how can I rewrite the above to ignore time and only be sensitive to the day passed?
Thank you
You might want to get familiar with NSDateComponents and NSCalendar. What you will probably need to do is extract the components from your date and construct new NSDates using only the day, month, and year components without time components.
For the "current" day, you'll want to create a 1 day range by using today's date at midnight, and the date by adding 1 day to that. NSCalendar has methods to do some of this for you.
These docs might help:
NSCalendar Class Reference
NSDateComponents Class Reference
Date and Time Programming Guide
This is essentially the same as vikingosegundo, but both functions use the same method, and I personally prefer it for my use.
- (NSDate *)dateByMovingToBeginningOfDay
{
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents* parts = [[NSCalendar currentCalendar] components:flags fromDate:self];
[parts setHour:0];
[parts setMinute:0];
[parts setSecond:0];
return [[NSCalendar currentCalendar] dateFromComponents:parts];
}
- (NSDate *)dateByMovingToEndOfDay
{
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents* parts = [[NSCalendar currentCalendar] components:flags fromDate:self];
[parts setHour:23];
[parts setMinute:59];
[parts setSecond:59];
return [[NSCalendar currentCalendar] dateFromComponents:parts];
}
Set the startDate to time 0:00:00 and the endDate to 23:59:59
NSDate *startDate = [NSDate date]; //Now
startDate= [date dateAtMidnight]; //today at 0:00
NSDate *endDate = [NSDate date];
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:#"gregorian"];
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:1];
[comps setSecond:-1]
endDate = [cal dateByAddingComponents:comps toDate:endDate options:0];//today 23:59:59
edit
this solution uses NSDate-Category NSDateAdditions.h, provided by Three20 (I wasnt aware it isn't defined in Cocoa)
- (NSDate*)dateAtMidnight {
NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"yyyy-d-M";
NSString* formattedTime = [formatter stringFromDate:self];
NSDate* date = [formatter dateFromString:formattedTime];
[formatter release];
return date;
}