Code
function readableDuration(seconds) {
const locale = "en-US";
const wordMap = [
[3600, "hour"],
[60, "minute"],
[1, "second"],
];
const formatter = new Intl.ListFormat(locale, { style: "long", type: "conjunction" });
function splitInterval(value, index = 0) {
const [divisor, unit] = wordMap[index];
const mod = value % divisor;
const quot = Math.floor(value / divisor);
const remaining = value - quot * divisor;
let arr = [];
if (quot >= 1) arr.push(`${quot} ${unit}${quot !== 1 ? "s" : ""}`);
if ((quot < 1 || mod > 0) && (++index) < wordMap.length) arr.push(...splitInterval(Math.floor(remaining), index));
return arr;
}
return formatter.format(splitInterval(seconds));
}
Explanation
The readableDuration
function below accepts values in seconds (see notes below on how to change it from seconds) and returns a human readable string. The wordMap
variable contains values for how many seconds an hour, minute etc. is, and 1
for second itself.
It must be ordered from largest to smallest.
In the splitInterval
function:
- Grab the seconds equivalent and word for the current unit
- Calculate the modulus (
seconds % unit equivalent
)
- Calculate and round-off the quotient (The 1 in
1 hour and 2 minutes
)
- Get the remaining seconds after rounding off using the modulus
- Declare an array to store every unit & value in the final string
- When the quotient is greater than or equal to 1, add it to the array with the singular/plural form* of the unit
- Add a smaller unit when:
- The current unit is not the end of
wordMap
, and
- The quotient is smaller than 1 and would look strange to print with the current unit (
0.25 hours
vs 15 minutes
)
- Modulus is larger than 0 which means there are remaining seconds
- Finally use
Intl.ListFormat
to join the parts* and return the string.
Notes
- The numbers in wordMap must be relative to one value e.g. 1 second but that value can be changed freely along with the words
- The plural part in the code works by just adding
s
but you'll want to use a different method for i18n.
- Change
locale
to en-UK
if you want to remove the oxford comma