iPhone - получить количество дней между двумя датами - PullRequest
5 голосов
/ 19 июня 2010

Я пишу приложение GTD для iPhone. Для должных задач я хочу отобразить что-то вроде «Должно завтра» или «Должно вчера» или «Должно 18 июля». Очевидно, что мне нужно отобразить «Завтра», даже если до задания осталось менее 24 часов (например, пользователь проверяет в 23:00 в субботу и видит, что в воскресенье в 8:00). Итак, я написал метод, чтобы получить количество дней между двумя датами. Вот код ...

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd-HH-mm"];

NSDate *nowDate = [dateFormatter dateFromString:@"2010-01-01-15-00"];
NSDate *dueDate = [dateFormatter dateFromString:@"2010-01-02-14-00"];

NSLog(@"NSDate *nowDate = %@", nowDate);
NSLog(@"NSDate *dueDate = %@", dueDate);

NSCalendar *calendar = [NSCalendar currentCalendar];

NSDateComponents *differenceComponents = [calendar components:(NSDayCalendarUnit)
                                                     fromDate:nowDate
                                                       toDate:dueDate
                                                      options:0];

NSLog(@"Days between dates: %d", [differenceComponents day]);

... и вот вывод:

NSDate *nowDate = 2010-01-01 15:00:00 -0700
NSDate *dueDate = 2010-01-02 14:00:00 -0700
Days between dates: 0

Как видите, метод возвращает неверные результаты. Он должен был вернуть 1 как количество дней между двумя днями. Что я тут не так делаю?

РЕДАКТИРОВАТЬ: я написал другой метод. Я не проводил обширных модульных тестов, но пока, похоже, он работает:

+ (NSInteger)daysFromDate:(NSDate *)fromDate inTimeZone:(NSTimeZone *)fromTimeZone untilDate:(NSDate *)toDate inTimeZone:(NSTimeZone *)toTimeZone {

    NSCalendar *calendar = [NSCalendar currentCalendar];
    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

    [calendar setTimeZone:fromTimeZone];
    NSDateComponents *fromDateComponents = [calendar components:unitFlags fromDate:fromDate];

    [calendar setTimeZone:toTimeZone];
    NSDateComponents *toDateComponents = [calendar components:unitFlags fromDate:toDate];

    [calendar setTimeZone:[NSTimeZone defaultTimeZone]];
    NSDate *adjustedFromDate = [calendar dateFromComponents:fromDateComponents];
    NSDate *adjustedToDate = [calendar dateFromComponents:toDateComponents];

    NSTimeInterval timeIntervalBetweenDates = [adjustedToDate timeIntervalSinceDate:adjustedFromDate];
    NSInteger daysBetweenDates = (NSInteger)(timeIntervalBetweenDates / (60.0 * 60.0 * 24.0));

    NSDateComponents *midnightBeforeFromDateComponents = [[NSDateComponents alloc] init];
    [midnightBeforeFromDateComponents setYear:[fromDateComponents year]];
    [midnightBeforeFromDateComponents setMonth:[fromDateComponents month]];
    [midnightBeforeFromDateComponents setDay:[fromDateComponents day]];

    NSDate *midnightBeforeFromDate = [calendar dateFromComponents:midnightBeforeFromDateComponents];
    [midnightBeforeFromDateComponents release];

    NSDate *midnightAfterFromDate = [[NSDate alloc] initWithTimeInterval:(60.0 * 60.0 * 24.0)
                                                               sinceDate:midnightBeforeFromDate];

    NSTimeInterval timeIntervalBetweenToDateAndMidnightBeforeFromDate = [adjustedToDate timeIntervalSinceDate:midnightBeforeFromDate];
    NSTimeInterval timeIntervalBetweenToDateAndMidnightAfterFromDate = [adjustedToDate timeIntervalSinceDate:midnightAfterFromDate];

    if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0) {

        // toDate is before the midnight before fromDate

        timeIntervalBetweenToDateAndMidnightBeforeFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0;

        if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0)
            daysBetweenDates -= 1;
    }
    else if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0) {

        // toDate is after the midnight after fromDate

        timeIntervalBetweenToDateAndMidnightAfterFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0;

        if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0)
            daysBetweenDates += 1;
    }

    [midnightAfterFromDate release];

    return daysBetweenDates;
}

Ответы [ 7 ]

3 голосов
/ 19 июня 2010

Из документов на components:fromDate:toDate:options::

Результат с потерями, если недостаточно запрошенного юнита для сохранения полной точности разницы.

Так как разница меньше, чем полный день, он корректно возвращает результат 0 дней.

2 голосов
/ 22 октября 2010

Вы можете легко решить это, используя очень хороший ответ на этот связанный вопрос

2 голосов
/ 19 июня 2010

Одна вещь, которую вы можете попробовать, это использовать rangeOfUnit: для обнуления часов, минут и секунд от даты начала и окончания.

NSCalendar *calendar = [NSCalendar currentCalendar];
NSCalendarUnit range = NSDayCalendarUnit;
NSDateComponents *comps = [[NSDateComponents alloc] init];
NSDate *start = [NSDate date];
NSDate *end;

[comps setDay:1];
[calendar rangeOfUnit:range startDate:&start interval:nil forDate:start];

end = [calendar dateByAddingComponents:comps toDate:start options:0];

В этом примере начало будет 2010-06-19 00:00:00 -0400, конец будет2010-06-20 00:00:00 -0400.Я предполагаю, что это будет работать лучше с методами сравнения NSCalendar, хотя я сам не проверял это.

2 голосов
/ 19 июня 2010

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

Чтобы сделать это,сравните даты, чтобы найти, что раньше, а что позже (и если они сравниваются равными, выручить с этим результатом), то проверьте, дает ли 1 день после более ранней даты дату с тем же годом, месяцем и днем ​​рождения.месяц как более поздняя дата.


Если вы действительно хотите точно знать, сколько календарных дней от одной даты до другой:

  1. Отправьте календарь components:fromDate: сообщение для получения года, месяца и дня месяца первой даты.
  2. То же, что и № 1, но для второй даты.
  3. Если две датынаходятся в том же году и месяце, вычтите один день месяца из другого и перейдите к abs (см. abs (3) ) для получения абсолютного значения.
  4. Если они не находятся в одном и том же году и месяце, проверьте, находятся ли они в смежномнет месяцев (например, с декабря 2010 года по январь 2011 года или с июня 2010 года по июль 2010 года).Если это так, добавьте количество дней в месяце более ранней даты (которое вы можете получить, отправив в календаре сообщение rangeOfUnit:inUnit:forDate: с передачей NSDayCalendarUnit и NSMonthCalendarUnit соответственно) к последнему дню месяца.дату, затем сравните этот результат с днем ​​месяца более ранней даты.

    Например, при сравнении 2010-12-31 с 2011-01-01 вы сначала определите, что они находятся в соседних месяцах,затем прибавьте 31 (количество дней в 2010-12) к 1 (день месяца 2011-01-01), затем вычтите 31 (день месяца 2010-12-31) из этой суммы.Поскольку разница равна 1, более ранняя дата на один день раньше более поздней.

    При сравнении 2010-12- 30 с 2011-01- 02 выопределит, что они находятся в соседних месяцах, затем прибавит 31 (дней в 2010-12) к 2 (день месяца 2011-01-02), затем вычтет 30 (день месяца 2010-12-30)) из этой суммы.33 минус 30 равно 3, поэтому эти даты разделены на три календарных дня.


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

1 голос
/ 14 января 2015

Я использую этот кусок кода, он работает очень хорошо:

- (NSInteger)daysToDate:(NSDate*)date
{
    if(date == nil) {
        return NSNotFound;
    }
    NSUInteger otherDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay   inUnit:NSCalendarUnitEra forDate:date];
    NSUInteger currentDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:self];
    return (otherDay-currentDay);
}
0 голосов
/ 19 декабря 2012
 -(NSInteger)daysBetweenTwoDates:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime

{

NSDate *fromDate;
NSDate *toDate;

NSCalendar *calendar = [NSCalendar currentCalendar];

[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate
             interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate
             interval:NULL forDate:toDateTime];

NSDateComponents *difference = [calendar components:NSDayCalendarUnit
                                           fromDate:fromDate toDate:toDate options:0];

return [difference day];

}

0 голосов
/ 29 июня 2010

Вот функция, которую я использовал в прошлом определено в категории по NSDate

- (int) daysToDate:(NSDate*) endDate
{
    //dates needed to be reset to represent only yyyy-mm-dd to get correct number of days between two days.
    NSDateFormatter *temp = [[NSDateFormatter alloc] init];
    [temp setDateFormat:@"yyyy-MM-dd"];
    NSDate *stDt = [temp dateFromString:[temp stringFromDate:self]];
    NSDate *endDt =  [temp dateFromString:[temp stringFromDate:endDate]];
    [temp release]; 
    unsigned int unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;
    NSCalendar *gregorian = [[NSCalendar alloc]
                             initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *comps = [gregorian components:unitFlags fromDate:stDt  toDate:endDt  options:0];
    int days = [comps day];
    [gregorian release];
    return days;
}
...