I can't seem to figure out why the day doesn't change when I get to the 6th of November, 2011. All I'm doing is iterating through the days. Any help would be appreciated. Here is my code:
NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents* comp = [[NSDateComponents alloc] init];
[comp setDay:2];
[comp setMonth:11];
[comp setYear:2011];
NSDate* date = [calendar dateFromComponents:comp];
for (int i = 0; i < 7; i++) {
NSDate* d = [date dateByAddingTimeInterval:((3600 * 24) * i)];
NSDateComponents* dComponents = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:d];
int day = [dComponents day];
NSLog(@"\nDate: %@\nDay: %i", [d description], day);
}
This is my output:
Date: 2011-11-02 06:00:00 +0000
Day: 2
Date: 2011-11-03 06:00:00 +0000
Day: 3
Date: 2011-11-04 06:00:00 +0000
Day: 4
Date: 2011-11-05 06:00:00 +0000
Day: 5
Date: 2011-11-06 06:00:00 +0000
Day: 6
Date: 2011-11-07 06:00:00 +0000
Day: 6
Date: 2011-11-08 06:00:00 +0000
Day: 7
Thanks!
Welcome to the wonderful world of date calculations!
@Vladimir is correct. November 6th happens to have ~90,000 seconds in it because that's the day (in the US on the Gregorian calendar) that Daylight Savings Time takes effect. Accept his answer; it's the right one.
This one is to educate you a bit more, because this is a hard topic.
An NSDate
represents an absolute point in time. This point is immutable. It will exist regardless of whether humans and their time measurements exist.
A "day" is a relative value. It is relative to many things, such as:
Thus, it is impossible to take a relative value and add it to an absolute value without more context. You have no idea what the relative value is relative to. You make the assumption that everyone is using the same point of reference as you, and that assumption is wrong. For example:
And so on and so on.
As a general rule, most calendars are solar based, meaning that one orbit around the sun is equivalent to one "year" in that calendar. But this makes things REALLY complicated, because the number of rotations the Earth makes (a "day") in its orbit around the Sun is not an integral (whole) number. It's something like 365.24 rotations. Enter leap days! But only in certain calendars! In other calendars, we have whole leap months! Or leap seconds. Or leap hours. Or leap-whatever-time-unit-you-wants. And don't even get me started on time zones.
So, how do you deal with this?
The simple answer: YOU DON'T. People much smarter than you or I have already written the code to handle all of this for us.
BEHOLD:
NSCalendar
NSDateComponents
An NSCalendar
encapsulates all of the rules and regulations for calculating time (on Earth) according to that system of measurement. NSDateComponents
encapsulates the relative nature of measuring time.
So you have an absolute point in time (an NSDate
), and you want to know what day of the week it corresponds to. Here's what you do:
[NSCalendar currentCalendar]
.The calendar's going to churn and burn and give you back an NSDateComponents
that signify whatever information you're looking for.
Or let's say you have an absolute point in time and you want to know "what's the absolute point in time 1 week later?". Who knows how long a week is? I don't. It's usually 7 days, but it doesn't have to be. That's just what I'm familiar with. But I'm sure there are calendars out there that are based on non-7-day weeks. So you make an NSDateComponents
object, lash it up to mean "1 week", and say "calendar: I have this date and this relative amount. Add them and tell me the new date" and it does that.
<update>
However, adding date components to dates can also be problematic. For example, what if you want to find the last day of every month this year? (We'll assume the Gregorian calendar here) So you start with an NSDate
that happens to fall on Jan 31 and you add "1 month" to it. What do you get? Feb 28! So then you add one month to that. What do you get? Mar 28! That's not the end of the month anymore!
The way to fix that is to always compute the new date from the original date. So instead of adding 1 month to each successive date, you add "n" months to the original date. And that will work correctly.
</update>
You can see how this is a difficult topic. In case you haven't, here's one last hint:
The sooner you learn to do things correctly, the happier you and your code will be. :)
November, 6 is a day when time changed due to the Day Saving Time so that day actually lasts 25 hours and incorrect result is probably comes from that (in general adding time intervals to a date is unreliable because of calendar irregularities and bahaviour may depend on many parameters: current calendar, time zone, locale settings etc).
The more correct way to iterate through days (per wwdc11 video "Performing Calendar Calculations"(iTunes link)) is to add appropriate number of date components to a starting date on each iteration:
...
NSDateComponents *addComponents = [[NSDateComponents alloc] init];
for (int i = 0; i < 7; i++) {
[addComponents setDay: i];
NSDate* d = [calendar dateByAddingComponents:addComponents toDate:date options:0];
NSDateComponents* dComponents = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:d];
int day = [dComponents day];
NSLog(@"\nDate: %@\nDay: %i", [d description], day);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With