Расчет даты и времени

Cocoa

NSDate обеспечивает абсолютную шкалу и эпохи для дат и времени, которая может быть вынесена в частностный календарь ,календарные расчеты или отображена пользователю. Для выполнения календарных расчетов, обычно требуется, получить составные элементы даты, например, год, месяц и день. Вы должны использовать предоставленные методы для работы с календарными расчетами, потому что они учитывают такие случаи, как переход на летнее время, начало или прекращение перехода и високосные годы.

Добавление компонентов к дате

Вы можете использовать dateByAddingComponents:toDate:options: для добавления компонентов даты (например, часов или месяцев) к существующему дню. Вы можете предоставить столько компонентов, сколько пожелаете. Листинг 9 показывает, как вычислить дату на полтора часа вперед.

Листинг 9 дата полтора часа спустя

NSDate *today = [[NSDate alloc] init];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];

[offsetComponents setHour:1];
[offsetComponents setMinute:30];

// Вычисляем дату на полтора часа вперед.

NSDate *datenext = [gregorian dateByAddingComponents:offsetComponents toDate:today options:0];

Компоненты для добавления могут быть отрицательными. Листинг 10 показывает, как вы можете получить воскресенье на текущей неделе (с использованием григорианского календаря).

Листинг 10 воскресенье на текущей неделе

NSDate *today = [[NSDate alloc] init];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

// Получаем компонент дня недели на текущую дату

NSDateComponents *weekdayComponents = [gregorian components:NSWeekdayCalendarUnit fromDate:today];

/*
Создаем компоненту даты представляющую собой число дней, чтобы вычесть из текущей даты.
В английской локали неделя начинается с воскресенья, в русской локали это не будет работать,
 т.к. неделя начинается с понедельника

Значение дня недели для воскресенья по григорианскому календарю (не во всех локалях!) 1,
 поэтому вычитаем 1 из количества дней,
 чтобы вычесть из даты. (Если сегодня воскресенье, вычесть 0 дней).
*/

NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init];
[componentsToSubtract setDay: 0 - ([weekdayComponents weekday] - 1)];

NSDate *beginningOfWeek = [gregorian dateByAddingComponents:componentsToSubtract toDate:today options:0];

/*
Дополнительный шаг:

beginningOfWeek теперь имеет тот же час, минуты и секунды, как первоначальная дата (today).

Чтобы нормализовать до полуночи, извлекаем компоненты года, месяца, дня и создаем новую дату из этих компонентов.
*/

NSDateComponents *components = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit |
          NSDayCalendarUnit) fromDate: beginningOfWeek];

beginningOfWeek = [gregorian dateFromComponents:components];

Как уже было сказано выше воскресенье не является началом недели во всех регионах. В листинге 11 показано, как можно вычислить первый день недели (как определено локалью календаря):

Листинг 11 Получение дня в начале недели

NSDate *today = [[NSDate alloc] init];
NSDate *beginningOfWeek = nil;
BOOL ok = [gregorian rangeOfUnit:NSWeekCalendarUnit startDate:&beginningOfWeek interval:NULL forDate: today];

Определение временных различий

Есть несколько способов расчета количества времени между датами. В зависимости от контекста, в котором производится расчет, пользователь вероятно может рассчитывать на иное поведение. Какой бы расчет вы не использовали, он должен быть понятен пользователю. Поскольку Cocoa реализует классы времени в соответствии с NTP стандартом, эти методы игнорируют високосные секунды в расчетах. Вы можете использовать components:fromDate:toDate:options: для определения временной разницы между двумя датами в единицах, отличных от секунды (которые можно вычислить с помощью NSDate метода timeIntervalSinceDate:). Листинг 12 показывает, как получить число месяцев и дней между двумя датами с использованием григорианского календаря.

Листинг 12 Получение разницы между двумя датами

NSDate *startDate = ...;
NSDate *endDate = ...;// Допустим, что уже были созданы

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;

NSDateComponents *components = [gregorian components:unitFlags fromDate:startDate toDate:endDate options:0];

NSInteger months = [components month];
NSInteger days = [components day];

Этот метод обрабатывает переполнения, как и следовало ожидать. Если fromDate, и toDate: параметры лежат в диапазоне года и 3 дней друг от друга, и вы просите только дни между ними, он возвращает NSDateComponents объект со значением 368 (или 369 в високосном году) в значении дня компонента. Однако этот метод обрезает результаты расчета до мельчайших единиц. Например, если fromDate: параметр соответствует 14 января 2010 в 23:30 , а toDate: соответствует 15 января 2010 в 8:00, тоесть только 8,5 часов между двумя датами. Если вы запросите количество дней, вы получаете 0, так как 8,5 часа меньше, чем за 1 день. Могут быть ситуации, когда результат должен быть 1 днем. Вы должны решить, какое поведение пользователи ожидают в каждом конкретном случае. Если вам нужно иметь расчет, который возвращает количество дней, рассчитанных по количеству полночей между двумя датами, вы можете использовать категории NSCalendar похожие на те, что в листинге 13.

Листинг 13 Количество дней между двумя датами, как количество полночей между ними

@implementation NSCalendar (MySpecialCalculations)
-(NSInteger)daysWithinEraFromDate:(NSDate *) startDate toDate:(NSDate *) endDate
{
     NSInteger startDay=[self ordinalityOfUnit:NSDayCalendarUnit inUnit: NSEraCalendarUnit forDate:startDate];

     NSInteger endDay=[self ordinalityOfUnit:NSDayCalendarUnit inUnit: NSEraCalendarUnit forDate:endDate];

     return endDay-startDay;
}
@end

Этот подход работает и в других единицах календаря, указав другое значение NSCalendarUnit для ordinalityOfUnit: параметра. Например, можно рассчитать количество лет, на основе количества раз 1 января, 12:00 утра находящихся между ними.

Не используйте этот метод для сравнения огромных разностей, потому что это вызывает переполнение NSInteger на 32-битных платформах. Этот метод действует только, если вы остаетесь в той же эпохе (в григорианском календаре это означает, что обе даты должны быть н.э. или обе должны быть до н.э.). Если вам нужно сравнить даты по эре, вы можете использовать что-то похожее из листинга 14.

Листинг 14 Дни между двумя датами в разных эрах

@implementation NSCalendar (MyOtherMethod)
-(NSInteger) daysFromDate:(NSDate *) startDate toDate:(NSDate *) endDate
{
     NSCalendarUnit units=NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
     NSDateComponents *comp1=[self components:units fromDate:startDate];
     NSDateComponents *comp2=[self components:units fromDate endDate];
     [comp1 setHour:12];
     [comp2 setHour:12];
     NSDate *date1=[self dateFromComponents: comp1];
     NSDate *date2=[self dateFromComponents: comp2];
     return [[self components:NSDayCalendarUnit fromDate:date1 toDate:date2 options:0] day];
}
@end

Этот метод создает компоненты из указанных дат, а затем нормализует время и сравнивает две даты. Этот расчет является более дорогим, чем сравнение дат в одной эпохе. Если у вас нет необходимости пересекать границы эпохи используйте технику показанную в листинге 13.

Проверка на то, когда выпадает дата

Если вам нужно определить, приходится ли дата на текущую неделю (или любая единица в этом отношении), вы можете использовать NSCalendar метод rangeOfUnit:startDate:interval:forDate:. Листинг 15 показывает метод, который определяет, приходится ли данная дата на эту неделю. Неделя в этом случае определяется как период с воскресенья в полночь, до следующей субботы до полуночи (по григорианскому календарю).

Листинг 15 Определение наличия даты на этой неделе

-(BOOL)isDateThisWeek:(NSDate *)date {
     NSDate *start;
     NSTimeInterval extends;
     NSCalendar *cal=[NSCalendar autoupdatingCurrentCalendar];
     NSDate *today=[NSDate date];
     BOOL success= [cal rangeOfUnit:NSWeekCalendarUnit startDate:&start interval: &extends forDate:today];
     if(!success)return NO;
     NSTimeInterval dateInSecs = [date timeIntervalSinceReferenceDate];
     NSTimeInterval dayStartInSecs= [start timeIntervalSinceReferenceDate];
     if(dateInSecs > dayStartInSecs && dateInSecs < (dayStartInSecs+extends)){
          return YES;
     }
     else {
          return NO;
     }
}
 
 
homeЗаметили ошибкукарта сайта 
   Made on a Mac