NSDate dateByAddingTimeInterval subtracting days incorrectly changes time - iphone

Am I missing something about how NSDate dateByAddingTimeInterval works with days? Here is my code for subtracting 33 days from a specific date (AIDate). AIDate has a time value of 00:00 PST.
activityOne.ActivityDateTime = [AIDate dateByAddingTimeInterval:-33*24*60*60];
If the AIDate equals 4-9-2013 00:00 PST, the above code will subtract 33 days and 1 hour which works out to 3-6-2013 23:00 PST.

24*60*60 means 24 hours, and not 1 day.
There is a huge difference between those two when your calculation crosses the change from or to daylight saving time.
Coincidentally 33 days ago there was no daylight saving time in your timezone.
The correct way to do this is to use NSDateComponents. Like this:
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *offset = [[NSDateComponents alloc] init];
[offset setDay:-33];
NSDate *offsetedDate = [calendar dateByAddingComponents:offset toDate:[NSDate date] options:0];

NSDateComponents *dc = [[NSCalendar currentCalendar] components:NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit|NSQuarterCalendarUnit fromDate:yourDate];
[dc setDay:dc.day - 33];
NSDate *noticeDate = [[NSCalendar currentCalendar] dateFromComponents:dc];

Related

NSDate - Getting Days of the Month (Not Days of the year)?

Using NSDateComponents I know how to get the day component, but this gives me a value from 1 - 365, right? I need to get the days 1-30 of a specific month, how can I?
For example, I have an NSDate which might be 16th May. I want to be able to get the day, so it returns 16. Being the 16th day of that month (not the 16th day of the year).
Thanks.
EDIT:
I have this:
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps = [cal components:NSDayCalendarUnit fromDate:[[NSUserDefaults standardUserDefaults] objectForKey:#"dateSaved"]];
However comps.day returns 1 when it should equal what the date is saved as, say 4.
The day value of NSDateComponents gives you the day of month according to the given calendar:
NSDate *date = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps = [gregorian components:NSDayCalendarUnit fromDate:date];
NSLog(#"%# / %d", date, comps.day);
gives
2012-02-04 15:27:36.682 testapp[1533:f803] 2012-02-04 14:27:36 +0000 / 4
although February, 4th would be the 35th day of the year.

Date in CocoaTouch

I want to find the current date and the date that 10 days before from current date.I know how to find the current date.please anybody help me to find the date which 10 days before from current date..
Thanks in advance.
You can use NSDateComponents to subtract the days from the current date.
NSDate *today = [NSDate date];
NSDateComponents *sub_date = [[NSDateComponents alloc] init];
[sub_date setDay:-10];
NSDate *tenDaysAgo = [[NSCalendar currentCalendar] dateByAddingComponents:sub_date
toDate:today
options:0];
[sub_date release];
NSLog(#"Ten Days Ago: %#", tenDaysAgo);
You can use dateWithTimeInterval:sinceDate: to subtract 10 days from the current date.
Code example:
NSDate *todayDate = [NSDate date];
NSDate *tenDaysAgoDate = [NSDate dateWithTimeInterval:-864000 sinceDate:todayDate];
The 864000 represents the seconds in ten days, the negative sets the calculation to days AGO, instead of forward.

iPhone: How to get date and time from number of seconds?

I have time in seconds 1303093926 and I want to get date and time like
Sat, 09 Oct 2010 18:14:50 +0000
NSDate has a conversion method:
double seconds = 1303093926;
NSTimeInterval timeInterval = (NSTimeInterval)seconds;
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
You can then use the NSDateFormatter class to convert the NSDate to a string in the format you want.
NSDate has several methods for creating date objects from second values. NSDate has four methods for doing exactly what you want:
+ (id)dateWithTimeInterval:(NSTimeInterval)seconds sinceDate:(NSDate *)date: If your number of seconds is based on some arbitrary date, you would use this method.
+ (id)dateWithTimeIntervalSince1970:seconds: If your number of seconds is based on the time since 1970, then you would use this method.
+ (id)dateWithTimeIntervalSinceNow:(NSTimeInterval)seconds: If the number of seconds is relative to the current time, use this. (Negative seconds are for dates prior to "now".)
+ (id)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)seconds: If you know that your number of seconds is relative to the predefined "reference date", (1 January 2001, GMT) then you should use this method.
Another approach, is to use NSDateComponents to build your date object. From the NSDateComponents documentation:
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:6];
[comps setMonth:5];
[comps setYear:2004];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [gregorian dateFromComponents:comps];
You can do the same with your number of seconds, using basic arithmetic to convert the seconds into years, months, days, hours, minutes and seconds.

iPhone SDK, how to get NSDate object of coming friday's 20:00?

Does anyone know how to get the NSDate of the coming friday's 20:00 ?
Yes, this article teaches you how to get the current week's Sunday.
I quickly adapted it to get friday at 20:00 (Assuming a Gregorian calendar)
NSDate *today = [[NSDate alloc] init];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
// Get the weekday component of the current date
NSDateComponents *weekdayComponents = [gregorian components:NSWeekdayCalendarUnit fromDate:today];
/*
Create a date components to represent the number of days to add to the current date.
The weekday value for Friday in the Gregorian calendar is 6, so add the difference between 6 and today to get the number of days to add.
Actually, on Saturday that will give you -1, so you want to subtract from 13 then take modulo 7
*/
NSDateComponents *componentsToAdd = [[NSDateComponents alloc] init];
[componentsToAdd setDay: (13 - [weekdayComponents weekday]) % 7];
NSDate *friday = [gregorian dateByAddingComponents:componentsToAdd toDate:today options:0];
/*
friday now has the same hour, minute, and second as the original date (today).
To normalize to midnight, extract the year, month, and day components and create a new date from those components.
*/
NSDateComponents *components =
[gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
fromDate: friday];
// And to set the time to 20:00
[components setHour:22];
friday = [gregorian dateFromComponents:components];

iPhone - get number of days between two dates

I'm writing a GTD app for the iPhone. For the due tasks, I want to display something like "Due tomorrow" or "Due yesterday" or "Due July 18th". Obviously, I need to display "Tomorrow" even if the task is less than 24 hours away (e.g. the user checks at 11pm on Saturday and sees there's a task on Sunday at 8am). So, I wrote a method to get the number of days in between two dates. Here's the code...
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd-HH-mm"];
NSDate *nowDate = [dateFormatter dateFromString:#"2010-01-01-15-00"];
NSDate *dueDate = [dateFormatter dateFromString:#"2010-01-02-14-00"];
NSLog(#"NSDate *nowDate = %#", nowDate);
NSLog(#"NSDate *dueDate = %#", dueDate);
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *differenceComponents = [calendar components:(NSDayCalendarUnit)
fromDate:nowDate
toDate:dueDate
options:0];
NSLog(#"Days between dates: %d", [differenceComponents day]);
... and here's the output:
NSDate *nowDate = 2010-01-01 15:00:00 -0700
NSDate *dueDate = 2010-01-02 14:00:00 -0700
Days between dates: 0
As you can see, the method returns incorrect results. It should have returned 1 as the number of days between the two days. What am I doing wrong here?
EDIT: I wrote another method. I haven't done extensive unit tests, but so far it seems to work:
+ (NSInteger)daysFromDate:(NSDate *)fromDate inTimeZone:(NSTimeZone *)fromTimeZone untilDate:(NSDate *)toDate inTimeZone:(NSTimeZone *)toTimeZone {
NSCalendar *calendar = [NSCalendar currentCalendar];
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
[calendar setTimeZone:fromTimeZone];
NSDateComponents *fromDateComponents = [calendar components:unitFlags fromDate:fromDate];
[calendar setTimeZone:toTimeZone];
NSDateComponents *toDateComponents = [calendar components:unitFlags fromDate:toDate];
[calendar setTimeZone:[NSTimeZone defaultTimeZone]];
NSDate *adjustedFromDate = [calendar dateFromComponents:fromDateComponents];
NSDate *adjustedToDate = [calendar dateFromComponents:toDateComponents];
NSTimeInterval timeIntervalBetweenDates = [adjustedToDate timeIntervalSinceDate:adjustedFromDate];
NSInteger daysBetweenDates = (NSInteger)(timeIntervalBetweenDates / (60.0 * 60.0 * 24.0));
NSDateComponents *midnightBeforeFromDateComponents = [[NSDateComponents alloc] init];
[midnightBeforeFromDateComponents setYear:[fromDateComponents year]];
[midnightBeforeFromDateComponents setMonth:[fromDateComponents month]];
[midnightBeforeFromDateComponents setDay:[fromDateComponents day]];
NSDate *midnightBeforeFromDate = [calendar dateFromComponents:midnightBeforeFromDateComponents];
[midnightBeforeFromDateComponents release];
NSDate *midnightAfterFromDate = [[NSDate alloc] initWithTimeInterval:(60.0 * 60.0 * 24.0)
sinceDate:midnightBeforeFromDate];
NSTimeInterval timeIntervalBetweenToDateAndMidnightBeforeFromDate = [adjustedToDate timeIntervalSinceDate:midnightBeforeFromDate];
NSTimeInterval timeIntervalBetweenToDateAndMidnightAfterFromDate = [adjustedToDate timeIntervalSinceDate:midnightAfterFromDate];
if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0) {
// toDate is before the midnight before fromDate
timeIntervalBetweenToDateAndMidnightBeforeFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0;
if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0)
daysBetweenDates -= 1;
}
else if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0) {
// toDate is after the midnight after fromDate
timeIntervalBetweenToDateAndMidnightAfterFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0;
if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0)
daysBetweenDates += 1;
}
[midnightAfterFromDate release];
return daysBetweenDates;
}
From the docs for components:fromDate:toDate:options::
The result is lossy if there is not a small enough unit requested to hold the full precision of the difference.
Since the difference is less than a full day, it correctly returns a result of 0 days.
If all you care about is tomorrow or yesterday vs. a specific date, then you can save yourself a lot of work and just test whether the dates are only one calendar day apart.
To do that, compare the dates to find which is earlier and which is later (and if they compare equal, bail out with that result), then test whether 1 day after the earlier date produces a date with the same year, month, and day-of-month as the later date.
If you really do want to know exactly how many calendar days there are from one date to the other:
Send the calendar a components:fromDate: message to get the year, month, and day-of-the-month of the first date.
Same as #1, but for the second date.
If the two dates are in the same year and month, subtract one day-of-month from the other and pass to abs (see abs(3)) to take the absolute value.
If they are not in the same year and month, test whether they are in adjacent months (e.g., December 2010 to January 2011, or June 2010 to July 2010). If they are, add the number of days in the earlier date's month (which you can obtain by sending the calendar a rangeOfUnit:inUnit:forDate: message, passing NSDayCalendarUnit and NSMonthCalendarUnit, respectively) to the day-of-month of the later date, then compare that result to the earlier date's day-of-month.
For example, when comparing 2010-12-31 to 2011-01-01, you would first determine that these are in adjacent months, then add 31 (number of days in 2010-12) to 1 (day-of-month of 2011-01-01), then subtract 31 (day-of-month of 2010-12-31) from that sum. Since the difference is 1, the earlier date is one day before the later date.
When comparing 2010-12-30 to 2011-01-02, you would determine that they are in adjacent months, then add 31 (days in 2010-12) to 2 (day-of-month of 2011-01-02), then subtract 30 (day-of-month of 2010-12-30) from that sum. 33 minus 30 is 3, so these dates are three calendar days apart.
Either way, I strongly suggest writing unit tests at least for this code. I've found that date-handling code is among the most likely to have subtle bugs that only manifest, say, twice a year.
One thing you might try is using rangeOfUnit: to zero out hours, minutes and seconds from the start and end dates.
NSCalendar *calendar = [NSCalendar currentCalendar];
NSCalendarUnit range = NSDayCalendarUnit;
NSDateComponents *comps = [[NSDateComponents alloc] init];
NSDate *start = [NSDate date];
NSDate *end;
[comps setDay:1];
[calendar rangeOfUnit:range startDate:&start interval:nil forDate:start];
end = [calendar dateByAddingComponents:comps toDate:start options:0];
In this example start will be 2010-06-19 00:00:00 -0400, end will be 2010-06-20 00:00:00 -0400. I'd imagine this would work better with NSCalendar's comparison methods, although I haven't tested it myself.
I am using this piece of code, it is working very well:
- (NSInteger)daysToDate:(NSDate*)date
{
if(date == nil) {
return NSNotFound;
}
NSUInteger otherDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:date];
NSUInteger currentDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:self];
return (otherDay-currentDay);
}
Here is the function I've used in the past
its defined in a category on NSDate
- (int) daysToDate:(NSDate*) endDate
{
//dates needed to be reset to represent only yyyy-mm-dd to get correct number of days between two days.
NSDateFormatter *temp = [[NSDateFormatter alloc] init];
[temp setDateFormat:#"yyyy-MM-dd"];
NSDate *stDt = [temp dateFromString:[temp stringFromDate:self]];
NSDate *endDt = [temp dateFromString:[temp stringFromDate:endDate]];
[temp release];
unsigned int unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps = [gregorian components:unitFlags fromDate:stDt toDate:endDt options:0];
int days = [comps day];
[gregorian release];
return days;
}
-(NSInteger)daysBetweenTwoDates:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime
{
NSDate *fromDate;
NSDate *toDate;
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate
interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate
interval:NULL forDate:toDateTime];
NSDateComponents *difference = [calendar components:NSDayCalendarUnit
fromDate:fromDate toDate:toDate options:0];
return [difference day];
}