How to skip time in NSDate? - objective-c

I want to get difference between dates skipping time means if one date is 13 Jan 2012 - 11 pm and other date is 14 Jan 2012 - 12 am,then difference should be 1 day not 0 day.I mean I want difference between date only, 14 Jan 2012 - 13 Jan 2012, skipping time. I know I can use NSDate api to calculate difference but the problem is it consider time also.So I thought while calculating difference I will skip time but I do not know how to skip time because if I use NSDateFormatter it will return string not date.Please help me what I can do?
Thanks in advance!

What you need to do is get the NSDateComponents of each date first. Once you do that you can compare the 'day' component to get you difference.
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[cal setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
NSDate *date1 = ....;
NSDate *date2 = ....;
NSDateComponents *comps1 = [cal components:NSDayCalendarUnit|NSYearCalendarUnit|NSMonthCalendarUnit fromDate:date1];
NSDateComponents *comps2 = [cal components:NSDayCalendarUnit|NSYearCalendarUnit|NSMonthCalendarUnit fromDate:date2];
date1 = [cal dateFromComponents:comps1];
date2 = [cal dateFromComponents:comps2];
NSDateComponents *diffComps = [cal components:NSDayCalendarUnit fromDate:date1 toDate:date2 options:0];
NSLog(#"Days diff = %d", diffComps.day);
The date API can be kind of weird to wrap your head around, but once you do it is quite powerful.

Related

NSDate intervals for yesterday

I need to filter search results based on values that were added yesterday. I have seen plenty on finding yesterday using:
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:[[NSDate alloc] init]];
[components setHour:-24];
[components setMinute:0];
[components setSecond:0];
NSDate *yesterday = [cal dateByAddingComponents:components toDate:[NSDate date] options:0];
predicate = [NSPredicate predicateWithFormat:#"created_at >= %#", yesterday];
But this finds 24 hours since this exact moment in time. I need to filter yesterday as 12:01am-12:00pm. So the actual 24 hour period that was yesterday.
I'm guessing that I need to do something along the lines of:
1. Take the current date
2. Find the time from the current date to 12:01am of the same day
3. Then subtract 24 hours from that date
I feel confident I can do #3 (and #1 of course), but I'm not sure how to go about #2. I maybe over thinking it but I can't seem to grasp how to say: "Ok, it's 8:03am, I need to remove 8 hours and 2 minutes which will put me at 12:01am".
Start with some date of today, for example "now":
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
Subtract one day to get some date of yesterday:
NSDateComponents *minusOneDay = [[NSDateComponents alloc] init];
[oneDay setDay:-1];
NSDate *nowMinusOneDay = [cal dateByAddingComponents:minusOneDay toDate:now options:0];
Compute start and end date of the "day calendar unit" that contains yesterday's date:
NSDate *startOfYesterday;
NSTimeInterval lengthOfYesterday;
[cal rangeOfUnit:NSDayCalendarUnit startDate:&startOfYesterday interval:&lengthOfYesterday forDate:nowMinusOneDay];
NSDate *endOfYesterday = [startOfYesterday dateByAddingTimeInterval:lengthOfYesterday];
This should work even if a daylight savings time transition occurs between today and yesterday.
Generally one should avoid to use explicit time intervals such as "24 hours", because not every day has that length.

Wrong Days Between Dates for Month of March

I have a very interesting problem: When calculating the number of days between two dates, my calculator gives the wrong results for the month of March only. I have two text fields, one for each date. If I enter 3/7/12 in date1, and 3/13/12 in date2, the result is 7, which is correct (I am counting the first day as well). But when I enter date1 = 3/7/12 and date2 = 3/14/12, the result is still 7, but it should be 8. Likewise, if I enter date1 = 3/7/12 and date2 = 3/23/12, the result should be 17, but it is 16. If I change the month to April so that date1 = 4/7/12 and date2 = 4/23/12, the result is 17. Every month is working as intended, only the month of March is giving me wrong results. Does anyone have any idea what I am missing? Is this a timezone problem? How do I fix it? Here is my code:
NSDateFormatter *dateFormatter1 = [[NSDateFormatter alloc] init];
[dateFormatter1 setDateFormat:#"MM/dd/yyyy"];
NSDate *startdate1 = [dateFormatter1 dateFromString: date1.text];
NSDateFormatter *dateFormatter2 = [[NSDateFormatter alloc] init];
[dateFormatter2 setDateFormat:#"MM/dd/yyyy"];
NSDate *enddate2 = [dateFormatter2 dateFromString: date2.text];
int start1 = [startdate1 timeIntervalSince1970];
int end2 = [enddate2 timeIntervalSince1970];
double difference12 = end2-start1;
int days12;
days12 =(int)((double)difference12/(3600.0*24.00)+1);
result12.text = [[NSString alloc] initWithFormat:#"%i", days12]
If you use NSCalendar, you won't need to deal with Daylight Savings Time calculation.
int days = [[[NSCalendar currentCalendar] components:NSDayCalendarUnit
fromDate:startDate1
toDate:endDate2
options:0] day] + 1;
Most like this is a daylight savings issue (March 11th, 2012). Set the date formatters' timezones to:
[NSTimeZone timeZoneForSecondsFromGMT:0];
This should fix the issue.
To add to others, please do not rely on number of seconds in a day being 86400 all the time, it won't. Be sure to watch Session 211 Performing Calendar Calculations from WWDC 2011.
You can think of NSDate as a point in time. If you want to calculate number of days between two dates, this is a calendar issue and there are more than one calendars (like the Mayan calendar).
What you need is NSDateComponents:
NSDate *dateToday = [NSDate date];
NSDateComponents *dateBComponent = [[NSDateComponents alloc] init];
[dateBComponent setDay:-10];
NSDate *date10DaysAgo = [[NSCalendar currentCalendar] dateByAddingComponents:dateBComponent
toDate:dateToday
options:0];
NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDateComponents *components = [calendar components:NSDayCalendarUnit
fromDate:date10DaysAgo
toDate:dateToday
options:0];
NSLog(#"Difference: %d", components.day);

Get date for every year july using NSDate

How to get date for every year of july month using NSDate .I am getting the data from the web service which I called. And now I am showing the full data in the graph which is very huge. The data which we are having is from 1998 to till now. so I want to show the data only every year of july. For this I need the help. Can anyone help me?
Well, NSDate is a specific date and time and not just month and year. If you're fine with something like every July 1 at 12 AM (or whatever time/day) then you can use NSDateComponents to set the components of the date.
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setMonth:5];
[comps setYear:2004];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [gregorian dateFromComponents:comps];
[comps release];
[gregorian release];
This is straight from the Apple documentation. If you want to get the next year, then you can use dateByAddingTimeInterval: on your NSDate to return a new date (or you can use the original NSDateComponents and setYear: in a loop).

Producing an NSDate with fixed time

I am trying to produce an NSDate with fixed hour and minutes. I need this to make an equal comparison with other date stored in CoreData.
So far I wrote this code:
NSDate date = [NSDate date];
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* components = [calendar components:flags fromDate:date];
NSDate* newDate = [calendar dateFromComponents:components];
however with a breakpoint in xcode4 I can see the values:
Printing description of date:
2012-01-10 11:20:47 +0000
Printing description of newDate:
2012-01-09 23:00:00 +0000
Why newDate is one day back in respect of date ?
/* EDIT */
I also have tried to set manually all the components, but calendar dateFromComponents always give back same one hour back date, seems ignoring the components.
components.hour=0;
components.minute=0;
components.second=0;
components.timeZone=[NSTimeZone localTimeZone];
This is the description of component after being set:
<NSDateComponents: 0x7464de0>
TimeZone: Europe/Rome (CET) offset 3600
Calendar Year: 2012
Month: 1
Day: 10
Hour: 0
Minute: 0
Second: 0
which is exactly what I would like to have, but the calculated date with this component is still
Printing description of newDate:
2012-01-09 23:00:00 +0000
I wonder why I cannot get a precise NSDate even with specifying all the components in an NSDateComponents. Just because NSCalendar is ignoring my requirements, what's the meaning of components ?
What am I doing wrong ?
I guess you are +01:00 time zone. Actually the NSDate always gives values in GMT. So if it is Jan 10th, 00:00, then at the same time GMT time is Jan 9th, 23:00.
Even, while printing the following line
2012-01-10 11:20:47 +0000,
it should have printed 1 hour less than your current time. Please check.
Use this....
NSDate *date = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setLocale:[NSLocale currentLocale]];
NSDateComponents *nowComponents = [gregorian components:NSYearCalendarUnit | NSWeekCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:date];
NSDate* newDate = [gregorian dateFromComponents:nowComponents];
NSLog(#"%#\n%#",date,newDate);
You may have problems with time zones, try setting a time zone for the calendar.

Get an array of future NSDates

I have a date picker.
After choosing a time from this I would like to get the dates of the next 64 Mondays.
How would I go about writing a method to take a date and return an NSArray of NSDates for the next 64 Mondays from that date
for e.g.
I picked time 6:45 pm from date picker then I want to fetch next 64 mondays with there time set to that time.
Example (ARC):
NSDate *pickerDate = [NSDate date];
NSLog(#"pickerDate: %#", pickerDate);
NSDateComponents *dateComponents;
NSCalendar *calendar = [NSCalendar currentCalendar];
dateComponents = [calendar components:NSWeekdayCalendarUnit fromDate:pickerDate];
NSInteger firstMondayOrdinal = 9 - [dateComponents weekday];
dateComponents = [[NSDateComponents alloc] init];
[dateComponents setDay:firstMondayOrdinal];
NSDate *firstMondayDate = [calendar dateByAddingComponents:dateComponents toDate:pickerDate options:0];
dateComponents = [[NSDateComponents alloc] init];
[dateComponents setWeek:1];
for (int i=0; i<64; i++) {
[dateComponents setWeek:i];
NSDate *mondayDate = [calendar dateByAddingComponents:dateComponents toDate:firstMondayDate options:0];
NSLog(#"week#: %i, mondayDate: %#", i, mondayDate);
}
NSLog output:
pickerDate: 2011-12-09 20:38:25 +0000
week#: 0, mondayDate: 2011-12-12 20:38:25 +0000
week#: 1, mondayDate: 2011-12-19 20:38:25 +0000
week#: 2, mondayDate: 2011-12-26 20:38:25 +0000
week#: 3, mondayDate: 2012-01-02 20:38:25 +0000
-the remaining 60 here-
Start with the NSDate from the picker, and keep adding 24*60*60 seconds to it until it's a Monday. Add the resulting date to the result. Continue adding 7*24*60*60 seconds to the last date you added and pushing the result onto the return list until you have all 64 Mondays. Here is how you tell if a NSDate falls on Monday:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents =[gregorian components:NSWeekdayCalendarUnit fromDate:dateOfInterest];
NSInteger weekday = [weekdayComponents weekday];
if (weekday == 2) ... // 2 represents Monday
EDIT: DaveDeLong pointed out a deficiency in the above algorithm: it will shift the time two times on the days of changing to daylight savings time. Instead of counting seconds manually, use this code to add a day to NSDate:
NSDate *currentDate = [NSDate date];
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:1]; // Add 1 when searching for the next Monday; add 7 when iterating 63 times
NSDate *date = [gregorian dateByAddingComponents:comps toDate:currentDate options:0];
[comps release];
You can use NSCalendar to determine what day of the week today (at the chosen time) is; bump it up to get to the next Monday, and then bump that by by 7 days 63 times to get the Mondays you seem to want.