NSDateComponents - метод дня, возвращающий неправильный день - PullRequest
11 голосов
/ 31 июля 2011

Кажется, я не могу понять, почему день не изменится, когда я подойду к 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

Спасибо!

Ответы [ 2 ]

34 голосов
/ 31 июля 2011

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

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

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

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

«День» - это относительная величина. Относительно многих вещей, таких как:

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

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

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

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

Как правило, большинство календарей основаны на солнечной энергии, что означает, что одна орбита вокруг Солнца эквивалентна одному "году" в этом календаре. Но это действительно усложняет ситуацию, потому что число вращений, которые Земля совершает («день») на своей орбите вокруг Солнца, не является целым (целым) числом. Это что-то вроде 365.24 вращения. Введите високосные дни! Но только в определенных календарях! В других календарях у нас целые високосные месяцы! Или високосные секунды. Или високосные часы. Или прыжок, сколько угодно времени. И даже не заводи меня на часовые пояса.

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

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

ВОТ:

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

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

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

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

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

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

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

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

ЭТО СЛОЖНАЯ ТЕМА

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

20 голосов
/ 31 июля 2011

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

Более правильный способ итерации по дням (для видео 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);
}
...