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}`;
};