Как преобразовать NSTimeInterval (секунды) в минуты

104

У меня есть некоторое количество этого seconds, полученного с определенного события. Он хранится в NSTimeIntervalтипе данных.

Я хочу преобразовать его в minutesи seconds.

Например, у меня есть: «326,4 секунды», и я хочу преобразовать его в следующую строку: «5:26».

Как лучше всего достичь этой цели?

Спасибо.

Илья Суздальницкий
источник

Ответы:

147

псевдокод:

minutes = floor(326.4/60)
seconds = round(326.4 - minutes * 60)
Брайан Рамзи
источник
90
Я думаю, также стоит упомянуть, что NSTimerInterval - это просто typedef для double.
Armentage
4
См. Ответ Альбарегара - это лучший способ сделать это (больше кода, но более гибкий, обслуживаемый)
benvolioT
1
Однако этот код очень дорогой. Этот ответ лучше для быстрого преобразования длительности в секунды.
SpacyRicochet 04
2
Обратите внимание, что этот ответ даст вам примерно 1:60. Посмотрите на ответ StratFan, который ближе к правде. Но уберите пол и круглый, и вы будете свободны дома.
InvulgoSoft
Ответ Мика МакКаллума ниже на момент написания этого комментария кажется наиболее обновленным и сделан с использованием собственного API.
Томаш Назаренко
183

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

  1. Ответ Брайана Рамзи более удобен, если вы хотите конвертировать только в минуты.
  2. Если вы хотите, чтобы Cocoa API сделал это за вас и преобразовал ваш NSTimeInterval не только в минуты, но и в дни, месяцы, недели и т. Д., Я думаю, что это более общий подход
  3. Используйте метод NSCalendar:

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

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

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

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

Образец кода

// The time interval 
NSTimeInterval theTimeInterval = 326.4;

// 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
unsigned int unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit;

NSDateComponents *conversionInfo = [sysCalendar components:unitFlags fromDate:date1  toDate:date2  options:0];

NSLog(@"Conversion: %dmin %dhours %ddays %dmoths",[conversionInfo minute], [conversionInfo hour], [conversionInfo day], [conversionInfo month]);

[date1 release];
[date2 release];

Известные проблемы

  • Слишком много для простого преобразования, вы правы, но так работает API.
  • Мое предложение: если вы привыкли управлять своими данными времени с помощью NSDate и NSCalendar, API сделает за вас тяжелую работу.
Альбарегар
источник
4
Определенно есть много обходного пути, просто чтобы сделать небольшое преобразование, но он кажется удобным, модульным и действительно гибким, проголосуйте за этот пример кода @Albaregar.
Rigo Vides
2
+1 За хороший обстоятельный ответ, но этот код довольно дорогой.
Ник Уивер
1
+1 ... один нюанс. Будьте осторожны, используя этот стиль кода, если вам нужно округлить доли секунды. Поведение по умолчанию - [NSCalendar components: fromDate: toDate: options:] может нарушиться в сценарии, где компонент toDate на 31,5 секунды (например) опережает fromDate - возвращаемые компоненты будут иметь 31 в поле секунды. Есть необязательный параметр NSWrapCalendarComponents, но из (ограниченных) экспериментов, которые я провел с этим, он переносится до ближайшей секунды, а не округляется в большую или меньшую сторону. Ваш пробег может отличаться.
Дэвид Дойл
%ldесли вы находитесь на 64-битной машине.
Anoop Vaidya
5
Люди, которые это обнаруживают (например, Ник Уивер), подумают, что код выглядит медленным. Это не так. Я делаю приложение для таймера, и я только что протестировал версию Альбарегара против чего-то в стиле Брайана Рэмси (и других) ... и, что удивительно, даже опрос 100 раз в секунду (что является огромным излишеством) на относительно медленном устройстве (4S ) разница в загрузке процессора составила менее 1%.
mmc
42

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

NSTimeInterval timeInterval = 326.4;
long seconds = lroundf(timeInterval); // Since modulo operator (%) below needs int or long

int hour = seconds / 3600;
int mins = (seconds % 3600) / 60;
int secs = seconds % 60;

Обратите внимание, когда вы помещаете float в int, вы автоматически получаете floor (), но вы можете добавить его к первым двум, если вам станет лучше :-)

Прасад
источник
Потрясающие! Я пробовал код Рамзи, хотя, похоже, он пропадает на секунду.
uplearnedu.com 02
В вашем коде отсутствует точка с запятой, но это не позволяет мне отредактировать ее, чтобы исправить.
Jeef
Я думаю, что ваш подход самый лучший - простой, выполняет свою работу и "зажигает" галочки. Отлично.
BonanzaDriver
29

Простите меня за то, что я девственница Стэка ... Я не знаю, как ответить на ответ Брайана Рамзи ...

Использование раунда не работает для вторых значений от 59,5 до 59,99999. Второе значение в этот период будет 60. Вместо этого используйте trunc ...

 double progress;

 int minutes = floor(progress/60);
 int seconds = trunc(progress - minutes * 60);
StratFan
источник
1
Собственно просто убрать пол и багажник / круглый и вовсе. :)
InvulgoSoft
@StratFan Не могли бы вы добавить часы к своему ответу?
Шмидт
27

Если вы ориентируетесь на iOS 8 или OS X 10.10 или выше, это стало намного проще. Новый NSDateComponentsFormatterкласс позволяет вам преобразовать данное NSTimeIntervalзначение из его значения за секунды в локализованную строку, чтобы показать пользователю. Например:

Цель-C

NSTimeInterval interval = 326.4;

NSDateComponentsFormatter *componentFormatter = [[NSDateComponentsFormatter alloc] init];

componentFormatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional;
componentFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorDropAll;

NSString *formattedString = [componentFormatter stringFromTimeInterval:interval];
NSLog(@"%@",formattedString); // 5:26

Swift

let interval = 326.4

let componentFormatter = NSDateComponentsFormatter()

componentFormatter.unitsStyle = .Positional
componentFormatter.zeroFormattingBehavior = .DropAll

if let formattedString = componentFormatter.stringFromTimeInterval(interval) {
    print(formattedString) // 5:26
}

NSDateCompnentsFormatterтакже позволяет использовать этот вывод в более длинных формах. Более подробную информацию можно найти в статье NSHipster NSFormatter . И в зависимости от того, с какими классами вы уже работаете (если нет NSTimeInterval), может быть удобнее передать форматеру экземпляр NSDateComponentsили два NSDateобъекта, что также можно сделать с помощью следующих методов.

Цель-C

NSString *formattedString = [componentFormatter stringFromDate:<#(NSDate *)#> toDate:<#(NSDate *)#>];
NSString *formattedString = [componentFormatter stringFromDateComponents:<#(NSDateComponents *)#>];

Swift

if let formattedString = componentFormatter.stringFromDate(<#T##startDate: NSDate##NSDate#>, toDate: <#T##NSDate#>) {
    // ...
}

if let formattedString = componentFormatter.stringFromDateComponents(<#T##components: NSDateComponents##NSDateComponents#>) {
    // ...
}
Мик МакКаллум
источник
Это правильный способ создать строку с адекватной локализацией
SwiftArchitect
1
Этот ответ гораздо более «Objective-C» и полезен, чем принятый ответ. Вам не нужны switchили if elseзаявления с этим. NSDateComponentsFormatterобо всем позаботится. Часы, минуты, секунды напечатаны без каких-либо проблем.
rustyMagnet
17

Код Брайана Рамзи, де-псевдоним:

- (NSString*)formattedStringForDuration:(NSTimeInterval)duration
{
    NSInteger minutes = floor(duration/60);
    NSInteger seconds = round(duration - minutes * 60);
    return [NSString stringWithFormat:@"%d:%02d", minutes, seconds];
}
Ян Мейер
источник
Это не локализовано
SwiftArchitect
7

Вот версия Swift:

func durationsBySecond(seconds s: Int) -> (days:Int,hours:Int,minutes:Int,seconds:Int) {
    return (s / (24 * 3600),(s % (24 * 3600)) / 3600, s % 3600 / 60, s % 60)
}

Можно использовать так:

let (d,h,m,s) = durationsBySecond(seconds: duration)
println("time left: \(d) days \(h) hours \(m) minutes \(s) seconds")
Йохен Бедерсдорфер
источник
6
    NSDate *timeLater = [NSDate dateWithTimeIntervalSinceNow:60*90];

    NSTimeInterval duration = [timeLater  timeIntervalSinceNow];

    NSInteger hours = floor(duration/(60*60));
    NSInteger minutes = floor((duration/60) - hours * 60);
    NSInteger seconds = floor(duration - (minutes * 60) - (hours * 60 * 60));

    NSLog(@"timeLater: %@", [dateFormatter stringFromDate:timeLater]);

    NSLog(@"time left: %d hours %d minutes  %d seconds", hours,minutes,seconds);

Выходы:

timeLater: 22:27
timeLeft: 1 hours 29 minutes  59 seconds
полярная посуда
источник
это действительно отличный пример, чтобы
скрыть
5

Поскольку это по сути двойная ...

Разделите на 60,0 и извлеките целую часть и дробную часть.

Неотъемлемой частью будет целое количество минут.

Снова умножьте дробную часть на 60,0.

Результатом будут оставшиеся секунды.


источник
3

Помните, что исходный вопрос касается вывода строки, а не псевдокода или отдельных строковых компонентов.

Я хочу преобразовать его в следующую строку: «5:26»

Во многих ответах отсутствуют вопросы интернационализации, и большинство из них выполняет математические вычисления вручную. Все просто так 20 век ...

Не занимайтесь математикой самостоятельно (Swift 4)

let timeInterval: TimeInterval = 326.4
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.unitsStyle = .positional
if let formatted = dateComponentsFormatter.string(from: timeInterval) {
    print(formatted)
}

5:26


Использование библиотек

Если вам действительно нужны отдельные компоненты и приятный для чтения код, посмотрите SwiftDate :

import SwiftDate
...
if let minutes = Int(timeInterval).seconds.in(.minute) {
    print("\(minutes)")
}

5


Кредиты @mickmaccallum и @polarwar за адекватное использованиеDateComponentsFormatter

SwiftАрхитектор
источник
0

Как я сделал это в Swift (включая форматирование строки, чтобы она отображалась как «01:23»):

let totalSeconds: Double = someTimeInterval
let minutes = Int(floor(totalSeconds / 60))
let seconds = Int(round(totalSeconds % 60))        
let timeString = String(format: "%02d:%02d", minutes, seconds)
NSLog(timeString)
SilentDirge
источник
0

Версия Swift 2

extension NSTimeInterval {
            func toMM_SS() -> String {
                let interval = self
                let componentFormatter = NSDateComponentsFormatter()

                componentFormatter.unitsStyle = .Positional
                componentFormatter.zeroFormattingBehavior = .Pad
                componentFormatter.allowedUnits = [.Minute, .Second]
                return componentFormatter.stringFromTimeInterval(interval) ?? ""
            }
        }
    let duration = 326.4.toMM_SS()
    print(duration)    //"5:26"
Спатель
источник