1st June 2018

← BackConverting time into a human-readable format presents a few tricky problems. You can’t simply divide by a number and get your answer because time, unlike money for example, is non-decimal; there are 60 seconds in a minute and 60 minutes in an hour and not 100. Then, on top of that, the number of hours in a day, days in a week and days and weeks in a year would seem incredibly arbitrary if it weren’t for the fact that we’ve known them all of our lives.

Converting seconds into minutes, or hours, or days, or years isn’t difficult, but doing them all at the same time is. Luckily, modulo operation provides us with a solution; it allow us to find out what the remainder is from any division. In JavaScript, like most other programming languages, the % symbol is the modulo operator.

```
// If we shared 5 sweets between 2 people they could have 2 each but there would be something
// left over because 2 isn't a factor of 5 (it doesn't fit exactly)
console.log(2 / 5);
// returns 2.5
// The modulo operation tells us there is one left over
console.log(2 % 5);
// returns 1
```

Let’s say we have 90 seconds and we want to convert it to minutes and seconds. We can divide by 60 and use Math.floor to round it down to find the number of whole minutes. We can then use modulo operation to find the remaining number of seconds.

```
// Find the number of whole minutes
console.log(Math.floor(90 / 60));
// returns 1
// The modulo operation will give us the remaining seconds
console.log(90 % 60);
// returns 30
```

So, using the modulo operator, we can quite easily convert seconds into hours, minutes and seconds with the following function:

```
function convertSeconds(seconds) {
const minutes = seconds / 60; // For some units it will be easier to work in minutes
const convertedTime = [
{
unit: 'hour',
value: Math.floor(minutes / 60) // We use Math.floor to round down to the nearest whole number
},
{
unit: 'minute',
value: Math.floor(minutes % 60)
},
{
unit: 'second',
value: Math.floor(seconds % 60)
}
];
return convertedTime; // Return the array of objects
}
const convertedTime = convertSeconds(3660);
/* returns:
[
{unit: "hour", value: 1},
{unit: "minute", value: 1},
{unit: "second", value: 0}
]
*/
```

We now have an object with the individual units for the time. The next problem is how to present it in a way that is human-readable. The simplest solution would be to make a string with all the units like so:

```
console.log(`${convertedTime.hours.value} hours ${convertedTime.minutes.value} minutes ${
convertedTime.seconds.value
} seconds`); // returns "1 hours 1 minutes 0 seconds"
```

Immediately we can see that this isn’t quite what we want. There are a few problems:

-If there is only one of a unit then it should be singular and not plural (we should see “1 hour” and not “1 hours”)

-If there are none of a unit then it should be ommitted (we shouldn’t see 0 seconds at all)

-It would also be nice if the units were separated with commas if there are more than 2 with the last unit preceded by “and”

convertedTime is an array of objects. We can loop through this array and return a new array with JavaScript’s Array.prototype.reduce method. We’ll loop through each object in the array and if the object has a value we’ll return a string which has the value and the unit. If the value is greater than 1 then we’ll add an “s” to the end of the unit to make it plural.

```
function getStrings(convertedTime) {
timeStrings = convertedTime.reduce((result, time) => {
if (time.value) {
if (time.value > 1) {
result.push(`${time.value} ${time.unit}s`);
} else {
result.push(`${time.value} ${time.unit}`);
}
}
return result;
}, []);
return timeStrings;
}
const timeStrings = getStrings(convertedTime);
/* returns:
["1 hour", "1 minute"]
*/
```

With the previous function we now have an array of strings for each of the units. The last step is to combine the strings and add the appropriate punctuation and grammar. In order to solve this problem we can create another function to pass the array through which we will call “applyDelimiters”. A delimeter is a character or sequence of characters used to separate values. Normally a delimiter will be the same character throughout, as in the case of CSV documents where each value is separated by a comma. However, in our case we want to use commas to separate some values with the final value separated by the word “and”. For this we can use a good old-fashioned for loop. We know that we want every unit except the second-to-last one to be followed by a comma. We want the second-to-last unit to be followed by the word ‘and’.

```
function applyDelimiters(timeStrings) {
let timeString = '';
for (var i = 0; i < timeStrings.length; i++) {
if (i < timeStrings.length-2) {
timeString += `${timeStrings[i]}, `;
} else if(i === timeStrings.length-2) {
timeString += `${timeStrings[i]} and `;
} else {
timeString += timeStrings[i];
}
}
return timeString;
}
const readableTime = applyDelimiters(timeStrings);
/* returns:
"1 hour and 1 minute"
*/
```

There we have it. With 3 functions we can convert time in seconds into something human-readable. We can also easily add days and years to the convertedTime array in the convertSeconds function if we wish.

If you’d like to use a module that handles all this for you then you can install my npm module for which this code is the basis, or you can view the source code. You can also read about this project in my readable-seconds project page.

21st August 2021

If you're still serving images in long-time standard formats such as JPEG and PNG then a Google PageSpeed Insights analysis of your site will identify an opportunity to improve your page loading times by serving images in "next-gen" formats. There are several so-called "next-gen" image formats which offer superior compression to JPEG and PNG but in this post we will… Continue reading »

25th January 2021

What do WebSockets do? Hypertext Transfer Protocol (HTTP) is unidrectional, meaning requests can only be made by the client. This is a limitation which means that a certain class of web application cannot be built using HTTP alone. WebSockets are a separate communication protocol, fully compatible with HTTP, which allow for bidirectional communication between a client and a server. A protocol… Continue reading »