如何将 NSTimeInterval (秒)转换为分钟

我得到了从某个事件传递过来的大量 seconds,它存储在一个 NSTimeInterval数据类型中。

我想把它转换成 minutesseconds

例如,我有: “326.4”秒,我想把它转换成以下字符串: “5:26”。

实现这一目标的最佳途径是什么?

谢谢。

176741 次浏览

pseudo-code:

minutes = floor(326.4/60)
seconds = round(326.4 - minutes * 60)

Since it's essentially a double...

Divide by 60.0 and extract the integral part and the fractional part.

The integral part will be the whole number of minutes.

Multiply the fractional part by 60.0 again.

The result will be the remaining seconds.

Brief Description

  1. The answer from Brian Ramsay is more convenient if you only want to convert to minutes.
  2. If you want Cocoa API do it for you and convert your NSTimeInterval not only to minutes but also to days, months, week, etc,... I think this is a more generic approach
  3. Use NSCalendar method:

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

    • "Returns, as an NSDateComponents object using specified components, the difference between two supplied dates". From the API documentation.

  4. Create 2 NSDate whose difference is the NSTimeInterval you want to convert. (If your NSTimeInterval comes from comparing 2 NSDate you don't need to do this step, and you don't even need the NSTimeInterval).

  5. Get your quotes from NSDateComponents

Sample Code

// 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];

Known issues

  • Too much for just a conversion, you are right, but that's how the API works.
  • My suggestion: if you get used to manage your time data using NSDate and NSCalendar, the API will do the hard work for you.

Forgive me for being a Stack virgin... I'm not sure how to reply to Brian Ramsay's answer...

Using round will not work for second values between 59.5 and 59.99999. The second value will be 60 during this period. Use trunc instead...

 double progress;


int minutes = floor(progress/60);
int seconds = trunc(progress - minutes * 60);

Brian Ramsay’s code, de-pseudofied:

- (NSString*)formattedStringForDuration:(NSTimeInterval)duration
{
NSInteger minutes = floor(duration/60);
NSInteger seconds = round(duration - minutes * 60);
return [NSString stringWithFormat:@"%d:%02d", minutes, seconds];
}

All of these look more complicated than they need to be! Here is a short and sweet way to convert a time interval into hours, minutes and seconds:

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;

Note when you put a float into an int, you get floor() automatically, but you can add it to the first two if if makes you feel better :-)

    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);

Outputs:

timeLater: 22:27
timeLeft: 1 hours 29 minutes  59 seconds

Here's a Swift version:

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)
}

Can be used like this:

let (d,h,m,s) = durationsBySecond(seconds: duration)
println("time left: \(d) days \(h) hours \(m) minutes \(s) seconds")

If you're targeting at or above iOS 8 or OS X 10.10, this just got a lot easier. The new NSDateComponentsFormatter class allows you to convert a given NSTimeInterval from its value in seconds to a localized string to show the user. For example:

Objective-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 also allows for this output to be in longer forms. More info can be found in NSHipster's NSFormatter article. And depending on what classes you're already working with (if not NSTimeInterval), it may be more convenient to pass the formatter an instance of NSDateComponents, or two NSDate objects, which can be done as well via the following methods.

Objective-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#>) {
// ...
}

How I did this in Swift (including the string formatting to show it as "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)

Swift 2 version

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"

Remember that the original question is about a string output, not pseudo-code or individual string components.

I want to convert it into the following string: "5:26"

Many answers are missing the internationalization issues, and most doing the math computations by hand. All just so 20th century...

Do not do the Math yourself (Swift 4)

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

5:26


Leverage on libraries

If you really want individual components, and pleasantly readable code, check out SwiftDate:

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

5


Credits to @mickmaccallum and @polarwar for adequate usage of DateComponentsFormatter