Как мне разбить NSTimeInterval на год, месяцы, дни, часы, минуты и секунды на iPhone?

У меня есть временной интервал, охватывающий годы, и мне нужны все временные компоненты от года до секунд.

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

Это кажется запутанным, и я читал, что всякий раз, когда вы делаете что-то, что выглядит запутанным, вероятно, есть встроенный метод.

Есть?

Я интегрировал второй метод Алекса в свой код.

Это в методе, вызываемом UIDatePicker в моем интерфейсе.

NSDate *now = [NSDate date];
NSDate *then = self.datePicker.date;
NSTimeInterval howLong = [now timeIntervalSinceDate:then];

NSDate *date = [NSDate dateWithTimeIntervalSince1970:howLong];
NSString *dateStr = [date description];
const char *dateStrPtr = [dateStr UTF8String];
int year, month, day, hour, minute, sec;

sscanf(dateStrPtr, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &sec);
year -= 1970;

NSLog(@"%d years\n%d months\n%d days\n%d hours\n%d minutes\n%d seconds", year, month, day, hour, minute, sec);

Когда я устанавливаю для выбора даты дату на 1 год и 1 день в прошлом, я получаю:

1 год 1 месяц 1 день 16 часов 0 минут 20 секунд

что составляет 1 месяц и 16 часов отдыха. Если я установил выбор даты на 1 день в прошлом, я отключился на ту же сумму.

Обновление: у меня есть приложение, которое вычисляет ваш возраст в годах с учетом дня рождения (установленного с помощью UIDatePicker), но оно часто отключается. Это доказывает, что была неточность, но я не могу понять, откуда она взялась, а вы?


person willc2    schedule 06.08.2009    source источник
comment
Если вы установите для выбора даты один день назад, вы отключитесь на месяц и шестнадцать часов?   -  person Alex Reynolds    schedule 06.08.2009
comment
да, один месяц и шестнадцать часов отдыха.   -  person willc2    schedule 07.08.2009
comment
Если в результате получается стабильный перерыв в один месяц и шестнадцать часов, независимо от того, какое значение вы выберете, вы либо вычтите его из переменных со сдвигом, либо вам нужно оценить, откуда эта ошибка. Я предполагаю, что что-то не так с вашим интервалом, если вы всегда не на столько.   -  person Alex Reynolds    schedule 08.08.2009


Ответы (7)


Краткое описание

  1. Просто еще один подход к завершению ответа JBRWilkinson, но с добавлением кода. Он также может предложить решение комментария Алекса Рейнольдса.

  2. Используйте метод NSCalendar:

    • (NSDateComponents *)components:(NSUInteger)unitFlags fromDate:(NSDate *)startingDate toDate:(NSDate *)resultDate options:(NSUInteger)opts

    • «Возвращает как объект NSDateComponents с использованием указанных компонентов разницу между двумя предоставленными датами». (Из документации API).

  3. Создайте 2 NSDate, отличием которых является NSTimeInterval, который вы хотите разбить. (Если ваш NSTimeInterval получен из сравнения 2 NSDate, вам не нужно делать этот шаг, и вам даже не нужен NSTimeInterval, просто примените даты к методу NSCalendar).

  4. Получите ваши котировки от NSDateComponents

Образец кода

// The time interval 
NSTimeInterval theTimeInterval = ...;

// Get the system calendar
NSCalendar *sysCalendar = [NSCalendar currentCalendar];

// Create the NSDates
NSDate *date1 = [[NSDate alloc] init];
NSDate *date2 = [[NSDate alloc] initWithTimeInterval:theTimeInterval sinceDate:date1]; 

// Get conversion to months, days, hours, minutes
NSCalendarUnit unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit;

NSDateComponents *breakdownInfo = [sysCalendar components:unitFlags fromDate:date1  toDate:date2  options:0];
NSLog(@"Break down: %i min : %i hours : %i days : %i months", [breakdownInfo minute], [breakdownInfo hour], [breakdownInfo day], [breakdownInfo month]);
person Albaregar    schedule 06.09.2009
comment
у этого метода есть проблема перехода на летнее время, вы сразу столкнетесь с проблемой 1 часа. - person hokkuk; 10.05.2012
comment
NSHourCalendarUnit | NSMinuteCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit устарело в IOS8, используйте вместо этого NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitDay | NSCalendarUnitMonth - person Amr Lotfy; 05.04.2016

Этот код учитывает время перехода на летнее время и другие возможные неприятности.

NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorianCalendar components: (NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit )
                                                    fromDate:startDate
                                                      toDate:[NSDate date]
                                                     options:0];


NSLog(@"%ld", [components year]);
NSLog(@"%ld", [components month]);
NSLog(@"%ld", [components day]);
NSLog(@"%ld", [components hour]);
NSLog(@"%ld", [components minute]);
NSLog(@"%ld", [components second]);
person vikingosegundo    schedule 17.02.2013

Начиная с iOS8 и выше вы можете использовать NSDateComponentsFormatter

Он имеет методы для преобразования разницы во времени в удобную для пользователя отформатированную строку.

NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull;

NSLog(@"%@", [formatter stringFromTimeInterval:1623452]);

Это дает результат - 2 недели, 4 дня, 18 часов, 57 минут, 32 секунды.

person Omkar    schedule 19.10.2016

Преобразуйте интервал в NSDate, используя +dateWithIntervalSince1970, извлеките из него компоненты даты, используя метод NSCalendar -componentsFromDate.

Справочник по SDK

person JBRWilkinson    schedule 06.08.2009
comment
+1. Чтобы быть более подробным для OP, вы не можете точно преобразовать временной интервал в эти компоненты, вы должны знать, откуда вы начинаете (таким образом, dateWithIntervalFrom1970), потому что, например, 29 дней - это месяц, если вы находитесь в феврале. 1-е (кроме, конечно, бисекстильных лет), но не 1-го января ... - person fraca7; 06.08.2009

Это работает для меня:

    float *lenghInSeconds = 2345.234513;
    NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:lenghInSeconds];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];


    [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];

    [formatter setDateFormat:@"HH:mm:ss"];
    NSLog(@"%@", [formatter stringFromDate:date]); 
    [formatter release];

Основное отличие здесь в том, что вам нужно настроить часовой пояс.

person doogilasovich    schedule 06.04.2010

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

+(NSString *)TimeRemainingUntilDate:(NSDate *)date {

    NSTimeInterval interval = [date timeIntervalSinceNow];
    NSString * timeRemaining = nil;

    if (interval > 0) {

        div_t d = div(interval, 86400);
        int day = d.quot;
        div_t h = div(d.rem, 3600);
        int hour = h.quot;
        div_t m = div(h.rem, 60);
        int min = m.quot;

        NSString * nbday = nil;
        if(day > 1)
            nbday = @"days";
        else if(day == 1)
            nbday = @"day";
        else
            nbday = @"";
        NSString * nbhour = nil;
        if(hour > 1)
            nbhour = @"hours";
        else if (hour == 1)
            nbhour = @"hour";
        else
            nbhour = @"";
        NSString * nbmin = nil;
        if(min > 1)
            nbmin = @"mins";
        else
            nbmin = @"min";

        timeRemaining = [NSString stringWithFormat:@"%@%@ %@%@ %@%@",day ? [NSNumber numberWithInt:day] : @"",nbday,hour ? [NSNumber numberWithInt:hour] : @"",nbhour,min ? [NSNumber numberWithInt:min] : @"00",nbmin];
    }
    else
        timeRemaining = @"Over";

    return timeRemaining;
}
person Nicolas Manzini    schedule 08.11.2011

Вот еще одна возможность, несколько более чистая:

NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSString *dateStr = [date description];
const char *dateStrPtr = [dateStr UTF8String];

// format: YYYY-MM-DD HH:MM:SS ±HHMM
int year, month, day, hour, minutes, seconds;
sscanf(dateStrPtr, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minutes, &seconds);
year -= 1970;
person Alex Reynolds    schedule 06.08.2009
comment
Для временного интервала в 1 секунду (одну секунду назад) возвращает год -1, месяц 12, день 31, час 16, минуту 0, секунду 1. - person willc2; 28.08.2009