Метод NSDateComponentsFormatter stringFromDate:ToDate: каждую секунду возвращает другое значение

Я пытаюсь получить количество дней между сегодняшней датой и будущей датой. Я вызываю следующее каждую секунду:

NSDateComponents *components = [[NSDateComponents alloc] init];
[components setYear:2025];
[components setMonth:1];
[components setDay:1];

NSDate *currentDate = [NSDate date];
NSDate *futureDate = [[NSCalendar currentCalendar] dateFromComponents:components];

NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorNone;
formatter.allowedUnits = NSCalendarUnitDay;
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated;
NSString *remainingDaysAsString = [formatter stringFromDate:currentDate toDate:futureDate];

Я печатаю переменную Остаток DaysAsString каждую секунду, и она всегда отличается на день.

3,551d
3,551d
3,550d
3,550d
3,551d
3,550d
3,551d

Кто-нибудь испытывал нечто подобное? Это похоже на ошибку в классе?

3 ответа

Решение

Это странно и похоже на ошибку. Результат NSDateComponentsFormatter правильно (в моем тесте), если вы указываете все единицы с точностью до секунд:

formatter.allowedUnits = NSCalendarUnitDay + NSCalendarUnitHour + NSCalendarUnitMinute  + NSCalendarUnitSecond;

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

NSDateComponents *comp = [[NSCalendar currentCalendar] components:NSCalendarUnitDay
                                                      fromDate:currentDate
                                                        toDate:futureDate
                                                       options:0];
NSInteger days = comp.day;

У меня та же проблема:

let fmt1 = NSDateComponentsFormatter()

fmt1.allowedUnits = .CalendarUnitYear |
    .CalendarUnitMonth |
    .CalendarUnitDay

let dc1 = NSDateComponents()
dc1.month = 1
dc1.day = 15

// This occasionally outputs "1 month, 14 days" instead of
// "1 month, 15 days" when the formatter is constrained to
// years, months and days. If formatter is allowed to use
// all units, the output is "1 month, 14 days, 23 hours,
// 59 minutes, 59 seconds".
fmt1.stringFromDateComponents(dc1)

Это можно исправить, указав положительное количество секунд и объединив его с ограничением разрешенных единиц и / или настройкой maximumUnitCount параметр formatter для фиксированного значения:

let fmt2 = NSDateComponentsFormatter()

fmt2.allowedUnits = .CalendarUnitYear |
    .CalendarUnitMonth |
    .CalendarUnitDay

fmt2.maximumUnitCount = 2
fmt2.collapsesLargestUnit = true

let dc2 = NSDateComponents()
dc2.month = 1
dc2.day = 15
dc2.second = 10

// This always outputs "1 month, 15 days"
fmt2.stringFromDateComponents(dc2)

Так что, в вашем случае, просто назначьте некоторое положительное количество секунд для NSDateComponents:

[components setYear:2025];
[components setMonth:1];
[components setDay:1];
[components setSecond:10];

Это предотвратит ошибки округления.

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

NSDate * date1; 
NSDate * date2;
//assuming both vars contain a date
NSTimeInterval duration = [date2 timeIntervalSince1970] - [date1 timeIntervalSince1970];
NSLog("@days between %@ and %@: %f", date1, date2, duration / (24 * 60 * 60));
Другие вопросы по тегам