Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
555 views
in Technique[技术] by (71.8m points)

iphone - NSDateComponents - day method returning wrong day

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(@"
Date: %@
Day: %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!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

  1. The planet earth. A martian day is not the same thing as an earth day
  2. The calendar of the person doing the calculation. Not all calendars measure time in the same way.

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:

  • The current year is 2011. But is it? "2011" is relative to the Gregorian calendar. What if you're not using the Gregorian calendar?
  • The current year is 23. But that's only if you're using the Japanese calendar and specifically using the Japanese era names.
  • The current year is 5771. But that's only if you're using the Hebrew calendar. And the turnover for the next year (5772) is not the same turnover as the next Gregorian year.
  • The current year is (probably) 1432. If you're a Muslim.
  • The current year is 4344, if you're Korean.

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:

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:

  • Get the appropriate calendar object. For almost everything you'll ever do, this is probably [NSCalendar currentCalendar].
  • Using the calendar, you say "i want such-and-such 'date components' from this absolute point in time"

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:

THIS IS A DIFFICULT TOPIC

The sooner you learn to do things correctly, the happier you and your code will be. :)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...