2014-01-22 2 views
3

Я ищу, чтобы отобразить количество месяцев от объекта NSDate.NSDateComponent Rounding

//Make Date Six Months In The Future 
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; 
NSDateComponents *sixMonthsFromNow = [[NSDateComponents alloc] init]; 
[sixMonthsFromNow setMonth:6]; 
NSDate *finishDate = [calendar dateByAddingComponents:sixMonthsFromNow toDate:[NSDate date] options:0]; //Six Months Time 

//Display 
NSCalendarUnit requiredFormat = NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit; 
NSDateComponents *dateComponents = [calendar components:requiredFormat fromDate:[NSDate date] toDate:finishDate options:0]; 

NSLog(@"%d months %d days %d hours %d minutes %d seconds", [dateComponents month], [dateComponents day], [dateComponents hour], [dateComponents minute], [dateComponents second]); 

Это выходы: 5 months 29 days 23 hours 59 minutes 59 seconds

Который является большим, но я только хочу, чтобы отобразить количество месяцев.

Если я ограничиваю только месяцы:

//Make Date Six Months In The Future 
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; 
NSDateComponents *sixMonthsFromNow = [[NSDateComponents alloc] init]; 
[sixMonthsFromNow setMonth:6]; 
NSDate *finishDate = [calendar dateByAddingComponents:sixMonthsFromNow toDate:[NSDate date] options:0]; //Six Months Time 

//Display 
NSCalendarUnit requiredFormat = NSMonthCalendarUnit; 
NSDateComponents *dateComponents = [calendar components:requiredFormat fromDate:[NSDate date] toDate:finishDate options:0]; 
NSLog(@"%d months", [dateComponents month]); 

Воспроизводит: 5 months

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

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

Моя конечная цель заключается не только ограничить округление эффект месяцев, это должно быть в состоянии закруглить часов до нескольких дней, если я только поставки: NSDayCalendarUnit

+1

Причина, по которой вывод не совсем «6 месяцев» является то, что вы называете '[дата NSDate]' дважды, и, возможно, разница миллисекунды между датой, используемой для вычисления 'finishDate' и дату используется для вычисления разницы 'dateComponents'. –

+0

И как бы вы определили «округление до месяца», если месяц может иметь 28, 29, 30 или 31 день? –

+0

Ярмарка, сохраняя ссылку на исходную дату, действительно правильно вычисляет дату, но это было действительно на самом деле для примера. Это может быть любая дата, которая не может пройти шесть месяцев. Округление - это действительно то, с чем мне нужна помощь, но похоже, что он должен определяться пользователем. Приветствия для поиска. –

ответ

2

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

@interface NSCalendar (MyCategory) 
-(NSInteger)roundedUnit:(NSCalendarUnit)unit fromDate:(NSDate *)fromDate toDate:(NSDate *)toDate; 
@end 

@implementation NSCalendar (MyCategory) 
-(NSInteger)roundedUnit:(NSCalendarUnit)unit fromDate:(NSDate *)fromDate toDate:(NSDate *)toDate 
{ 
    // Number of units between the two dates: 
    NSDateComponents *comps = [self components:unit fromDate:fromDate toDate:toDate options:0]; 
    NSInteger value = [comps valueForComponent:unit]; 

    // Add (value) units to fromDate: 
    NSDate *date1 = [self dateByAddingComponents:comps toDate:fromDate options:0]; 

    // Add (value + 1) units to fromDate: 
    [comps setValue:(value + 1) forComponent:unit]; 
    NSDate *date2 = [self dateByAddingComponents:comps toDate:fromDate options:0]; 

    // Now date1 <= toDate < date2. Check which one is closer, 
    // and return the corresponding value: 
    NSTimeInterval diff1 = [toDate timeIntervalSinceDate:date1]; 
    NSTimeInterval diff2 = [date2 timeIntervalSinceDate:toDate]; 
    return (diff1 <= diff2 ? value : value + 1); 
} 
@end 

и вы используйте его как

NSInteger months = [calendar roundedUnit:NSMonthCalendarUnit fromDate:[NSDate date] toDate:finishDate]; 

Код использует вспомогательные методы для NSDateComponents из https://github.com/henrinormak/NSDateComponents-HNExtensions/blob/master/README.md:

- (void)setValue:(NSInteger)value forComponent:(NSCalendarUnit)unit; 
- (NSInteger)valueForComponent:(NSCalendarUnit)unit; 

Этих методы являются новыми в OS X 10.9, но не доступны в 7 прошивке.

0

Как другой комментатор отметил, что вы звоните NSDate дважды , а вторая дата немного позже первой. Используйте один и тот же объект даты дважды, и вы не получите ошибок округления.

А как округлить:

Я не думаю, что есть какие-либо округление, встроенный в NSCalendars методы календарно расчета.

Вам нужно решить, что это значит для вас. Вы можете обойтись на полпути. В этом случае вы можете задать календарь, сколько дней в текущем месяце, используя метод rangeOfUnit: inUnit: forDate, а затем добавить половину этого количества дней до конечной даты, а затем запросить месяц. Если вы хотите только «округлить» в течение недели, добавьте только неделю до даты окончания, прежде чем запрашивать количество разниц в месяцах.

+0

В любом случае округление до месяца будет неоднозначным. Пример: это 15 дней с 20 января по 4 февраля 2014 года. Если это округлено (потому что январь имеет 31 день и 15 <31/2) или округлен (потому что февраль имеет 28 дней и 15> 28/2)? –

+0

@MartinR это, вероятно, причина, почему нет встроенной функции, поскольку вы правильно сказали ее «двусмысленный» - я думаю, мне нужно подумать о некоторых правилах округления. –

0

У меня аналогичная проблема:

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 параметра форматтер на фиксированное значение:

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 перед вызовом dateByAddingComponents:

[sixMonthsFromNow setMonth: 6]; 
[sixMonthsFromNow setSecond: 10];