105

I've got an amount of seconds that passed from a certain event. It's stored in a NSTimeInterval data type.

I want to convert it into minutes and seconds.

For example I have: "326.4" seconds and I want to convert it into the following string: "5:26".

What is the best way to achieve this goal?

Thanks.

Manoj Rlogical
  • 239
  • 1
  • 4
Ilya Suzdalnitski
  • 52,598
  • 51
  • 134
  • 168

12 Answers12

183

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.
Albaregar
  • 5,480
  • 4
  • 20
  • 9
  • 4
    Definitively is a lot of workaround just to do a little conversion, but it feels confortable, modular and really flexible, vote up for this sample code @Albaregar. – Rigo Vides Jun 19 '10 at 17:27
  • 2
    +1 For a good thorough answer, however this code is quite expensive. – Nick Weaver May 16 '12 at 07:52
  • 1
    +1 ... one caveat. Be careful using this style of code if you need to round up fractional seconds. The default behaviour of -[NSCalendar components:fromDate:toDate:options:] can mess up in the scenario where the toDate component is 31.5 seconds (for example) ahead of the fromDate - the components returned will have 31 in the seconds field. There's an optional parameter of NSWrapCalendarComponents, but from the (limited) experimentation I've done with this, it wraps to the nearest second rather than rounding up or down. Your mileage may vary. – David Doyle Apr 09 '13 at 09:55
  • `%ld` if you are in 64 bit machine. – Anoop Vaidya Jul 25 '13 at 14:25
  • 5
    People who find this (like Nick Weaver) are going to think that code looks like it would be slow. It isn't. I'm doing a timer app and I just tested Albaregar's version against something in the style of Brian Ramsey (and others)... and surprisingly, even polling 100 times a second (which is tremendous overkill) on a relatively slow device (4S) there was less than a 1% difference in processor utilization. – mmc Jan 08 '14 at 13:50
147

pseudo-code:

minutes = floor(326.4/60)
seconds = round(326.4 - minutes * 60)
Brian Ramsay
  • 7,536
  • 8
  • 41
  • 52
  • 91
    I think it's also worth mentioning that an NSTimerInterval is just a typedef for double. – Armentage Apr 10 '10 at 14:02
  • 4
    See Albaregar's answer - it is a better way of doing this (more code but more flexible, maintainable) – benvolioT Sep 12 '10 at 16:49
  • 1
    That code is very expensive, however. For a quick conversion of a duration in seconds, this answer is better. – SpacyRicochet Jul 04 '12 at 14:28
  • 2
    Please note that this answer will give you stuff like 1:60. Look at StratFan's answer which is closer to the truth. But remove floor and round and you should be home free. – InvulgoSoft Jul 17 '12 at 10:31
  • Mick MacCallum's answer below at the time of writing this comment seems to be most updated and made by the way of using native API. – Tomasz Nazarenko Feb 25 '17 at 08:53
44

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

bruce1337
  • 330
  • 6
  • 16
Prasad
  • 440
  • 4
  • 5
29

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);
StratFan
  • 361
  • 3
  • 4
27

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#>) {
    // ...
}
Mick MacCallum
  • 129,200
  • 40
  • 280
  • 281
  • This is the proper way to create the string, adequately localized – SwiftArchitect Oct 10 '17 at 18:41
  • 1
    This answer is way more "Objective-C" and useful than the accepted answer. You don't need `switch` or `if else` statements with this. `NSDateComponentsFormatter` takes care of it all. Hours, Minutes, Seconds printed without any worry. – rustyMagnet Apr 21 '20 at 10:02
17

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];
}
Yang Meyer
  • 5,409
  • 5
  • 39
  • 51
7

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")
Jochen Bedersdorfer
  • 4,093
  • 24
  • 26
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);

Outputs:

timeLater: 22:27
timeLeft: 1 hours 29 minutes  59 seconds
polarware
  • 2,499
  • 1
  • 24
  • 18
5

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.

3

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

Community
  • 1
  • 1
SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179
0

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)
SilentDirge
  • 827
  • 9
  • 17
0

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"
SPatel
  • 4,768
  • 4
  • 32
  • 51