Format a duration ( from seconds ) using date-fns

According to the example code in date-fns/date-fns#229 (comment), we can now use intervalToDuration to convert seconds (passed as an Interval) to a Duration, which can then simplify formatting as desired by the OP:

import {  intervalToDuration } from 'date-fns'

const seconds = 10000

intervalToDuration({ start: 0, end: seconds * 1000 })
// { hours: 2, minutes: 46, seconds: 40 }

So for the OP's needs:

import {  intervalToDuration } from 'date-fns'

const seconds = 1807
const duration = intervalToDuration({ start: 0, end: seconds * 1000 })
// { minutes: 30, seconds: 7 }

const formatted = `${duration.minutes}:${duration.seconds}`
// 30:7

Edit (2022-08-04): It was pointed out that the above simplistic code won't 0-pad the numbers, so you will end up with 30:7 rather than 30:07. This padding can be achieved by using String.prototype.padStart() as follows:

import {  intervalToDuration } from 'date-fns'

const seconds = 1807
const duration = intervalToDuration({ start: 0, end: seconds * 1000 })
// { minutes: 30, seconds: 7 }

const zeroPad = (num) => String(num).padStart(2, '0')

const formatted = `${zeroPad(duration.minutes)}:${zeroPad(duration.seconds)}`
// 30:07

It was also pointed out that if the Interval goes above 60 minutes it will start incrementing the hours within the Duration, which the above code wouldn't display. So here is another slightly more complex example that handles this as well as the zeroPad case:

import {  intervalToDuration } from 'date-fns'

const seconds = 1807
const duration = intervalToDuration({ start: 0, end: seconds * 1000 })
// { minutes: 30, seconds: 7 }

const zeroPad = (num) => String(num).padStart(2, '0')

const formatted = [
  duration.hours,
  duration.minutes,
  duration.seconds,
]
.filter(Boolean)
.map(zeroPad)
.join(':')
// 30:07

There is also an issue on GitHub asking how to use a custom format with formatDuration, which suggest that currently the only way to do so is by providing a custom Locale. GitHub user @marselmustafin provided an example using this workaround. Following this same pattern, we could implement the OP's desired functionality roughly as follows:

import { intervalToDuration, formatDuration } from "date-fns";

const seconds = 1807;
const duration = intervalToDuration({ start: 0, end: seconds * 1000 });
// { minutes: 30, seconds: 7 }

const zeroPad = (num) => String(num).padStart(2, "0");

const formatted = formatDuration(duration, {
  format: ["minutes", "seconds"],
  // format: ["hours", "minutes", "seconds"],
  zero: true,
  delimiter: ":",
  locale: {
    formatDistance: (_token, count) => zeroPad(count)
  }
});
// 30:07

Here's the simple implementation:

import { formatDistance } from 'date-fns'

const duration = s => formatDistance(0, s * 1000, { includeSeconds: true })

duration(50) // 'less than a minute'
duration(1000) // '17 minutes'

This is basically the same as:

import moment from 'moment'    

const duration = s => moment.duration(s, 'seconds').humanize()

duration(50) // 'a minute'
duration(1000) // '17 minutes'

You can do this using date-fns by simple modifying an intermediate helper date. Using new Date( 0 ) you'll get a date set to January 1, 1970, 00:00:00 UTC. You can then use addSeconds from date-fns to add the relevant seconds (actually you could use the native date setTime( 1000 * seconds ) for this). Formatting the minutes and seconds of this will give you your desired result.

var input = document.getElementById('seconds');
var output = document.getElementById('out');

output.innerText = formattedTime(input.value);
input.addEventListener('input', function() {
  output.innerText = formattedTime(input.value);
});

function formattedTime(seconds) {
  var helperDate = dateFns.addSeconds(new Date(0), seconds);
  return dateFns.format(helperDate, 'mm:ss');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.26.0/date_fns.min.js"></script>

<input type="number" id="seconds" value="1807">
<pre id="out"></pre>

This function will convert seconds to duration format hh:mm:ss, its analogue duration in moment.js

import { addHours, getMinutes, getHours, getSeconds } from 'date-fns';

export const convertToDuration = (secondsAmount: number) => {
    const normalizeTime = (time: string): string =>
    time.length === 1 ? `0${time}` : time;

    const SECONDS_TO_MILLISECONDS_COEFF = 1000;
    const MINUTES_IN_HOUR = 60;

    const milliseconds = secondsAmount * SECONDS_TO_MILLISECONDS_COEFF;

    const date = new Date(milliseconds);
    const timezoneDiff = date.getTimezoneOffset() / MINUTES_IN_HOUR;
    const dateWithoutTimezoneDiff = addHours(date, timezoneDiff);

    const hours = normalizeTime(String(getHours(dateWithoutTimezoneDiff)));
    const minutes = normalizeTime(String(getMinutes(dateWithoutTimezoneDiff)));
    const seconds = normalizeTime(String(getSeconds(dateWithoutTimezoneDiff)));

    const hoursOutput = hours !== '00' ? `${hours}:` : '';

    return `${hoursOutput}${minutes}:${seconds}`;
};

Tags:

Date Fns