117

Soundcloud's API gives the duration of it's tracks as milliseconds. JSON looks like this:

"duration": 298999

I've tried many functions I found on here to no avail. I'm just looking for something to convert that number to something like looks like this:

4:59

Here's one that got close, but doesn't work. It doesn't stop the seconds at 60. It goes all the way to 99 which makes no sense. Try entering "187810" as a value of ms, for example.

var ms = 298999,
min = Math.floor((ms/1000/60) << 0),
sec = Math.floor((ms/1000) % 60);

console.log(min + ':' + sec);

Thanks for your help!

If you could add in support for hours, too, I would be grateful.

JAL
  • 41,701
  • 23
  • 172
  • 300
ElliotD
  • 1,279
  • 2
  • 10
  • 9
  • What do you mean, "*it goes all the way to 99*"? Your script is working for me, properly outputting `4:58`. Please provide an input that leads to the unexpected output. – Bergi Jan 22 '14 at 21:34
  • 187810 is an example of it not working. It's goes all the way to 99. There are not 99 seconds in a minute. – ElliotD Jan 22 '14 at 21:39
  • That yields `3:7` (maybe should be `3:07`) for me. Where do you see 99 in there? – Bergi Jan 22 '14 at 21:40
  • I don't know, I copied this code, Bergi. – ElliotD Jan 22 '14 at 21:41
  • From where? It would be nice to reference the origins :-) – Bergi Jan 22 '14 at 21:42
  • 2
    Here, added Math.floor to it since I copied. http://stackoverflow.com/questions/5588465/javascript-parse-time-minutesseconds-from-miliseconds – ElliotD Jan 22 '14 at 21:59
  • Thanks. As you can see in the comments, `<< 0` just did the same as your `Math.floor`… – Bergi Jan 22 '14 at 22:01
  • Great, suppose either works. Math.floor seemed to be what most people were using. – ElliotD Jan 22 '14 at 22:02

15 Answers15

254
function millisToMinutesAndSeconds(millis) {
  var minutes = Math.floor(millis / 60000);
  var seconds = ((millis % 60000) / 1000).toFixed(0);
  return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
}

millisToMinutesAndSeconds(298999); // "4:59"
millisToMinutesAndSeconds(60999);  // "1:01"

As User HelpingHand pointed in the comments the return statement should be:

return (
  seconds == 60 ?
  (minutes+1) + ":00" :
  minutes + ":" + (seconds < 10 ? "0" : "") + seconds
);
Andrea Tulimiero
  • 17,693
  • 5
  • 21
  • 28
maerics
  • 151,642
  • 46
  • 269
  • 291
  • 9
    Let it be known that this solution sometimes results in sixty seconds in the second place when nearing conversion to the next minute (ex. `1:60`). I stop this from happening by adding an inline if statement in the last line: `return (seconds == 60 ? (minutes+1) + ":00" : minutes + ":" + (seconds < 10 ? "0" : "") + seconds);` You can see it in action [here](https://github.com/LNFWebsite/Streamly) – HelpingHand Dec 05 '15 at 02:08
  • 1
    That's a good point by HelpingHand - you can reproduce the issue by running `millisToMinutesAndSeconds(299900)` – SubJunk Jun 15 '17 at 08:34
  • 8
    @maerics Shouldn't `((millis % 60000) / 1000).toFixed(0)` be `Math.floor((millis % 60000) / 1000)` so `seconds` is a number instead of a string when it is being compared to another number. – Courtney Pattison Jan 21 '18 at 22:23
  • return should be return minutes + ":" + seconds.ToString("00"), where seconds should be (Math.Round(millis % 60000 / 1000, 0, MidpointRounding.ToEven); – Circus Ranger Jul 09 '19 at 18:24
  • Proposal of renaming for this function: `formatToISODuration` ... don't bother with the millisecond in the name, the parameter does the job. And always write user-PoV names, no inner technical details. Plus "And" in a function name is always a bad idea. – Sandburg Jan 05 '21 at 15:22
  • Please don't do this. Use native JS date/time functions. No matter what language you're programming in, if you *ever* find yourself doing manual temporal conversions/manipulations, stop. It's almost always been solved already. Please see my answer. – Madbreaks Sep 10 '21 at 17:02
26

I'm not really sure why these answers are all so complex. The Date class gives you what you need:

const date = new Date(298999);

alert(`${date.getMinutes()}:${date.getSeconds()}`);

Although the above meets op's requirements, adding this updated version in response to @ianstarz comment regarding timezone independence:

const d = new Date(Date.UTC(0,0,0,0,0,0,298999)),
  // Pull out parts of interest
  parts = [
    d.getUTCHours(),
    d.getUTCMinutes(),
    d.getUTCSeconds()
  ],
  // Zero-pad
  formatted = parts.map(s => String(s).padStart(2,'0')).join(':');

document.write(formatted);
Madbreaks
  • 19,094
  • 7
  • 58
  • 72
  • Looks like OP is indeed only asking for seconds and minutes. This solution would not work for hours and days however. `date.getHours() // => 16` (in my timezone) – ianstarz Sep 10 '21 at 14:45
  • 1
    @ianstarz updated my solution to make it more universally applicable – Madbreaks Sep 10 '21 at 16:41
  • Should probably be higher up the list, However if the seconds are < 10, then it would output something like `4:5` – Tunn Oct 20 '21 at 00:58
  • 1
    @Tunn updated with a more complete example – Madbreaks Dec 09 '21 at 21:38
  • 1
    @Madbreaks you might want to do it for your first example too, otherwise the simplicity is misleading – Tunn Dec 11 '21 at 22:56
  • 1
    This should be the accepted answer as it is simpler, less error prone than other solutions and uses the language's built-in resources – Eduardo Matsuoka Jan 24 '23 at 14:53
22

With hours, 0-padding minutes and seconds:

var ms = 298999;
var d = new Date(1000*Math.round(ms/1000)); // round to nearest second
function pad(i) { return ('0'+i).slice(-2); }
var str = d.getUTCHours() + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds());
console.log(str); // 0:04:59

Matt
  • 20,108
  • 1
  • 57
  • 70
  • In some use cases it might be better to drop the second (2nd) row (rounding to the nearest second). For example, if we want to know the full seconds left, this would return "4:58". – b00t Mar 25 '20 at 07:51
  • presumably if you're over 60 minutes you'd want to add `getUTCHours`? – Thomas Aug 04 '20 at 23:12
  • @Madbreaks if you're suggesting `pad` is reinventing the wheel, note the answer is from 2014 yet your use of `padStart` is part of EMCAScript 2017. – Matt Sep 10 '21 at 18:21
  • @Matt my bad. I deleted my previous comment. I do suggest using native JS classes/functions however when possible. – Madbreaks Sep 12 '21 at 00:02
6

Here's my contribution if looking for

h:mm:ss

instead like I was:

function msConversion(millis) {
  let sec = Math.floor(millis / 1000);
  let hrs = Math.floor(sec / 3600);
  sec -= hrs * 3600;
  let min = Math.floor(sec / 60);
  sec -= min * 60;

  sec = '' + sec;
  sec = ('00' + sec).substring(sec.length);

  if (hrs > 0) {
    min = '' + min;
    min = ('00' + min).substring(min.length);
    return hrs + ":" + min + ":" + sec;
  }
  else {
    return min + ":" + sec;
  }
}
HelpingHand
  • 1,045
  • 11
  • 26
4

Best is this!

function msToTime(duration) {
var milliseconds = parseInt((duration%1000))
    , seconds = parseInt((duration/1000)%60)
    , minutes = parseInt((duration/(1000*60))%60)
    , hours = parseInt((duration/(1000*60*60))%24);

hours = (hours < 10) ? "0" + hours : hours;
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;

return hours + ":" + minutes + ":" + seconds + "." + milliseconds;
}

It will return 00:04:21.223 You can format this string then as you wish.

user3156040
  • 723
  • 5
  • 5
  • 1
    No need to reinvent the wheel. Look at the `Date` class and the methods it offers you. The "best" is always to use native solutions. – Madbreaks Jun 26 '20 at 17:19
  • @Madbreaks I'm not sure how you would do pure formatting like this with date since you would have to deal with timezones. Can you provide and example of what you mean? – ianstarz Sep 09 '21 at 21:52
  • 1
    @ianstarz I added an answer. – Madbreaks Sep 10 '21 at 03:09
3

There is probably a better way to do this, but it gets the job done:

var ms = 298999;
var min = ms / 1000 / 60;
var r = min % 1;
var sec = Math.floor(r * 60);
if (sec < 10) {
    sec = '0'+sec;
}
min = Math.floor(min);
console.log(min+':'+sec);

Not sure why you have the << operator in your minutes line, I don't think it's needed just floor the minutes before you display.

Getting the remainder of the minutes with % gives you the percentage of seconds elapsed in that minute, so multiplying it by 60 gives you the amount of seconds and flooring it makes it more fit for display although you could also get sub-second precision if you want.

If seconds are less than 10 you want to display them with a leading zero.

rgrwkmn
  • 159
  • 6
3

Event though ,oment.js does not provide such functionality, if you come here and you are already using moment.js, try this:

function formatDuration(ms) {
  var duration = moment.duration(ms);
  return Math.floor(duration.asHours()) + moment.utc(duration.asMilliseconds()).format(":mm:ss");
}

You will get something like x:xx:xx.

In the case you may want to skip the hour, when the duration is only < 60minutes.

function formatDuration(ms) {
  var duration = moment.duration(ms);
  if (duration.asHours() > 1) {
    return Math.floor(duration.asHours()) + moment.utc(duration.asMilliseconds()).format(":mm:ss");
  } else {
    return moment.utc(duration.asMilliseconds()).format("mm:ss");
  }
}

This workaround in moment was introduced in this Issue.

Robin Wieruch
  • 14,900
  • 10
  • 82
  • 107
0
function msToHMS( ms ) {
  // 1- Convert to seconds:
  var seconds = ms / 1000;

  // 2- Extract hours:
  var hours = parseInt( seconds / 3600 ); // 3,600 seconds in 1 hour
  seconds = seconds % 3600; // seconds remaining after extracting hours

  // 3- Extract minutes:
  var minutes = parseInt( seconds / 60 ); // 60 seconds in 1 minute

  // 4- Keep only seconds not extracted to minutes:
  seconds = seconds % 60;

  //alert( hours+":"+minutes+":"+seconds);
  hours = (hours < 10) ? "0" + hours : hours;
  minutes = (minutes < 10) ? "0" + minutes : minutes;
  seconds = (seconds < 10) ? "0" + seconds : seconds;
  var hms = hours+":"+minutes+":"+seconds;
  return hms;
}
Purefan
  • 1,498
  • 24
  • 44
0

If you do not need support for hours, there's a clever little way of doing this with momentjs and dayjs.

dayjs(ms).format("mm:ss")

or

moment(ms).format("mm:ss")

A millisecond timestamp passed to MomentJS and DayJS will be interpreted as the number of milliseconds since the Unix Epoch, therefore any time duration not affected by timezone (ie any number of milliseconds less than one hour) will be interpreted correctly.

colemerrick
  • 1,118
  • 12
  • 17
0
// ✅ You can use a Quick one-liner hack
 const ms = 54000000;
console.log(new `enter code here`Date(ms).toISOString().slice(11, 19)); // ️ 15:00:00

// ------------------------------------------------

// ✅ Or create a reusable function
function padTo2Digits(num) {
  return num.toString().padStart(2, '0');
}

function convertMsToTime(milliseconds) {
  let seconds = Math.floor(milliseconds / 1000);
  let minutes = Math.floor(seconds / 60);
  let hours = Math.floor(minutes / 60);

  seconds = seconds % 60;
  minutes = minutes % 60;

  // ️ If you don't want to roll hours over, e.g. 24 to 00
  // ️ comment (or remove) the line below
  // commenting next line gets you `24:00:00` instead of `00:00:00`
  // or `36:15:31` instead of `12:15:31`, etc.
  hours = hours % 24;

  return `${padTo2Digits(hours)}:${padTo2Digits(minutes)}:${padTo2Digits(
    seconds,
  )}`;
}

console.log(convertMsToTime(54000000)); // ️ 15:00:00 (15 hours)
console.log(convertMsToTime(86400000)); // ️ 00:00:00 (24 hours)
console.log(convertMsToTime(36900000)); // ️ 10:15:00 (10 hours, 15 minutes)
console.log(convertMsToTime(15305000)); // ️ 04:15:05 (4 hours, 15 minutes, 5 seconds)
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 06 '23 at 19:46
-1

Just works:

const minute = Math.floor(( milliseconds % (1000 * 60 * 60)) / (1000 * 60));

const second = Math.floor((ms % (1000 * 60)) / 1000);
-1
const Minutes = ((123456/60000).toFixed(2)).replace('.',':');

//Result = 2:06

We divide the number in milliseconds (123456) by 60000 to give us the same number in minutes, which here would be 2.0576.

toFixed(2) - Rounds the number to nearest two decimal places, which in this example gives an answer of 2.06.

You then use replace to swap the period for a colon.

-1

My solution: Input: 11381 (in ms) Output: 00 : 00 : 11.381

 timeformatter(time) {
    console.log(time);

    let miliSec = String(time%1000);
    time = (time - miliSec)/1000;
    let seconds = String(time%60);
    time = (time - seconds)/60;
    let minutes = String(time%60);
    time = (time-minutes)/60;
    let hours = String(time)

    while(miliSec.length != 3 && miliSec.length<3 && miliSec.length >=0) {
        miliSec = '0'+miliSec;
    }
    while(seconds.length != 2 && seconds.length<3 && seconds.length >=0) {
        seconds = '0'+seconds;
    }
    while(minutes.length != 2 && minutes.length<3 && minutes.length >=0) {
        minutes = '0'+minutes;
    }
    while(hours.length != 2 && hours.length<3 && hours.length >=0) {
        hours = '0'+hours;
    }
    return `${hours}  : ${minutes} : ${seconds}.${miliSec}`
}
sau0409
  • 153
  • 1
  • 5
-2

this code will do a better job if you want to show hours, and centiseconds or miliseconds after seconds like 1:02:32.21 and if used in a cell phone the timer will show correct timing even after screen lock.

<div id="timer" style="font-family:monospace;">00:00<small>.00</small></div>

<script>
var d = new Date();
var n = d.getTime();
var startTime = n;

var tm=0;
function updateTimer(){
  d = new Date();
  n = d.getTime();
  var currentTime = n;  
  tm = (currentTime-startTime);
  
  //tm +=1; 
  // si el timer cuenta en centesimas de segundo
  //tm = tm*10;
  
  var hours = Math.floor(tm / 1000 / 60 / 60);
  var minutes = Math.floor(tm / 60000) % 60;
  var seconds =  ((tm / 1000) % 60);
  // saca los decimales ej 2 d{0,2}
  var seconds = seconds.toString().match(/^-?\d+(?:\.\d{0,-1})?/)[0];
  var miliseconds = ("00" + tm).slice(-3);
  var centiseconds;

  
  // si el timer cuenta en centesimas de segundo
  //tm = tm/10;


  centiseconds = miliseconds/10;
  centiseconds = (centiseconds).toString().match(/^-?\d+(?:\.\d{0,-1})?/)[0];

  minutes = (minutes < 10 ? '0' : '') + minutes;
  seconds = (seconds < 10 ? '0' : '') + seconds;
  centiseconds = (centiseconds < 10 ? '0' : '') + centiseconds;
  hours = hours + (hours > 0 ? ':' : '');
  if (hours==0){
    hours='';
  }

  document.getElementById("timer").innerHTML = hours + minutes + ':' + seconds + '<small>.' + centiseconds + '</small>';
}

var timerInterval = setInterval(updateTimer, 10);
// clearInterval(timerInterval);
</script>
  • Your answer is not entirely bad. It's mostly off-topic. The question was about formatting, not about displaying and also not about updating time. There are also some problems in the code. The code is not really re-usable because of lack of separation (mixed formatting with updating). You are also calculating `seconds` twice and using inefficient ways to do the calculations (RegExp)... – Nux Oct 18 '18 at 21:12
  • hi Nux thanks, I understand, just came across this issue when needed to display time in a web app served by an arduino, the js code runs at client side only once, it doesnt mean to scale. The time calculation in the top anwer doesnt actually works. secconds/100 dont match with seconds at runtime, and if the browser looses focus the timer looses the count. If you try my code it covers all needs about calculation, formating, and diplaying, of course if you can make it re-usable and performant, please send me the snippet. Thanks! – Ricardo Lerch Oct 21 '18 at 16:15
-2
const millisToMinutesAndSeconds(millis)=> {
      const minutes=Math.floor(millis/60/1000) % 60;
      const seconds=Math.floor(millis/1000) % 60;
      return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
}