NSDateComponents - метод дня, возвращающий неправильный день

Я не могу понять, почему день не меняется, когда я добираюсь до 6 ноября 2011 года. Все, что я делаю, повторяется через дни. Любая помощь будет оценена по достоинству. Вот мой код:

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);
}

Это мой вывод:

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

Спасибо!

Ответ 1

6 ноября - это день, когда время изменилось из-за Дня спасения, так что день на самом деле длится 25 часов, и, вероятно, из-за этого получается неправильный результат (в общем, добавление временных интервалов к дате ненадежно из-за неравномерности календаря и bahaviour может зависеть от многих параметров: текущего календаря, часового пояса, настроек локали и т.д.).

Более правильный путь к итерации через дни (для каждого видео wwdc11 "Выполнение расчётов календаря" (ссылка iTunes)) заключается в добавлении соответствующего количества компонентов даты в начало дата на каждой итерации:

...
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);
}

Ответ 2

Добро пожаловать в чудесный мир вычислений даты!

@Владымир прав. 6-го ноября происходит ~ 90 000 секунд, потому что в этот день (в США по григорианскому календарю), что переход на летнее время вступает в силу. Примите его ответ; это правильный.

Этот вопрос должен рассказать вам немного больше, потому что это сложная тема.

An NSDate представляет собой абсолютную точку во времени. Эта точка неизменна. Он будет существовать независимо от того, существуют ли люди и их измерения времени.

"День" - относительное значение. Это касается многих вещей, таких как:

  • Планета Земля. Марсианский день - это не то же самое, что земной день.
  • Календарь человека, выполняющего расчет. Не все календари измеряют время таким же образом.

Таким образом, невозможно взять относительное значение и добавить его к абсолютному значению без дополнительного контекста. Вы не представляете, что относительное значение относительно. Вы делаете предположение, что каждый использует ту же точку отсчета, что и вы, и это предположение неверно. Например:

  • В текущем году 2011 год. Но так ли? "2011" относится к григорианскому календарю. Что делать, если вы не используете григорианский календарь?
  • В текущем году 23 года. Но это только в том случае, если вы используете японский календарь и конкретно используете японские названия эпохи.
  • Текущий год - 5771. Но это только если вы используете еврейский календарь. И оборот на следующий год (5772) - это не тот оборот, что и следующий григорианский год.
  • Текущий год (возможно) 1432. Если вы мусульманин.
  • Текущий год - 4344, если вы корейский.

И так далее и т.д.

Как правило, большинство календарей основаны на солнечной энергии, что означает, что одна орбита вокруг Солнца эквивалентна одному "году" в этом календаре. Но это делает вещи ДЕЙСТВИТЕЛЬНО сложными, потому что число вращений Земли ( "день" ) на своей орбите вокруг Солнца не является целым (целым) числом. Это примерно 365,24 оборота. Введите високосные дни! Но только в определенных календарях! В других календарях у нас целые високосные месяцы! Или прыжок секунд. Или високосный час. Или что-то вроде того, что вам нужно. И даже не заводи меня в часовые пояса.

Итак, как вы справляетесь с этим?

Простой ответ: ВЫ НЕ. Люди гораздо умнее, чем вы или я уже написали код для обработки всего этого для нас.

ВОТ:

An NSCalendar инкапсулирует все правила и положения для расчета времени (на Земле) в соответствии с этой системой измерения. NSDateComponents инкапсулирует относительный характер времени измерения.

Итак, у вас есть абсолютный момент времени (NSDate), и вы хотите узнать, какой день недели ему соответствует. Вот что вы делаете:

  • Получить соответствующий объект календаря. Для почти всего, что вы когда-либо сделаете, это, вероятно, [NSCalendar currentCalendar].
  • Используя календарь, вы говорите: "Я хочу, чтобы такие-то" компоненты даты "были из этого абсолютного момента времени"

Календарь будет сбивать и записывать и возвращать NSDateComponents, который обозначает любую информацию, которую вы ищете.

Или скажите, что у вас есть абсолютный момент времени, и вы хотите знать, "какой абсолютный момент времени через неделю?". Кто знает, как долго проходит неделя? Я не. Обычно это 7 дней, но это не обязательно. Это то, с чем я знаком. Но я уверен, что есть календари, которые основаны на не-7-дневных неделях. Таким образом, вы создаете объект NSDateComponents, накладываете его на значение "1 неделя" и говорите "календарь: у меня есть эта дата и эта относительная сумма. Добавьте их и сообщите мне новую дату", и она это делает.

< обновление >

Однако добавление компонентов даты к датам также может быть проблематичным. Например, что, если вы хотите найти последний день каждого месяца в этом году? (Мы возьмем григорианский календарь здесь) Итак, вы начинаете с NSDate, который выпадает на 31 января, и вы добавляете к нему "1 месяц". Что вы получаете? 28 февраля! Итак, вы добавляете к этому месяц. Что вы получаете? 28 марта! Это не конец месяца!

Способ исправления заключается в том, чтобы всегда вычислять новую дату с исходной даты. Поэтому вместо того, чтобы добавлять 1 месяц к каждой следующей дате, вы добавляете "n" месяцев к исходной дате. И это будет работать правильно.

</обновление >

Вы можете видеть, как это сложная тема. Если вы этого не сделали, вот последний намек:

ЭТО ТРУДНАЯ ТЕМА

Чем скорее вы научитесь делать все правильно, тем счастливее вы и ваш код.:)