How to write test cases in `ava` for below code snippet - javascript

const Duration = minutes => {
const oneMinute = 1;
const minutesInAnHour = 60;
if (minutes <= oneMinute) {
return "in just 1 minute";
}
if (minutes < minutesInOneHour) {
return "in just" + " " + minutes.toFixed(0) + " " + "minutes";
}
if (minutes === minutesInOneHour) {
return "in just 1 hour.";
}
const hours = Math.floor(minutes / 60);
const minutesValue = Math.ceil(minutes % 60);
const hourString = hours === 1 ? "hour" : "hours";
const minuteString = minutesValue === 1 ? `1 min` : minutesValue > 0 ? `${minutesValue.toFixed(0)} mins` : "";
return `${hours} ${hourString}${minuteString ? ` ${minuteString}` : ""}`;
} module.exports = { Duration }
I am new to writing test cases in ava. Above is a code snippet for example. How can we write test cases in ava for this.

AVA assumes you are using ESM (ECMAScript modules); see: "Create your test file".
Note: You have a typo. Change all occurrences of minutesInOneHour to minutesInAnHour.
First, you will need to install ava.
npm i -D ava # or
yarn add -D ava
Next, you need to add a test script to your package.json.
{
"scripts": {
"test": "ava"
}
}
Now, create a test file that ends in .test.js e.g. Time.test.js. These tests should be placed in a directory called __tests__. This is the standard when it comes to JavaScript unit tests.
Here is an example tree of a project:
+ project/
+ dist/
- index.css
- index.html
- index.js
+ node_modules/
> ...
+ src/
+ __tests__
+ Time.test.mjs
+ main.js
+ Time.mjs
- package.json
src/__tests__/Time.test.mjs
import test from "ava";
import { Duration } from "../Time.mjs";
test("Test Duration valid", (t) => {
t.is(Duration(1), "in just 1 minute", "testing 1 minute");
t.is(Duration(30), "in just 30 minutes", "testing 30 minute2");
t.is(Duration(60), "in just 1 hour.", "testing 1 hour");
});
test("Test Duration invalid", (t) => {
t.not(Duration(1), "in just 1 minutes", "This should fail...");
});
src/Time.mjs
const Duration = (minutes) => {
const oneMinute = 1;
const minutesInAnHour = 60;
if (minutes <= oneMinute) {
return "in just 1 minute";
}
if (minutes < minutesInAnHour) {
return "in just " + minutes.toFixed(0) + " minutes";
}
if (minutes === minutesInAnHour) {
return "in just 1 hour.";
}
const hours = Math.floor(minutes / 60);
const minutesValue = Math.ceil(minutes % 60);
const hourString = hours === 1 ? "hour" : "hours";
const minuteString =
minutesValue === 1
? `1 min`
: minutesValue > 0
? `${minutesValue.toFixed(0)} mins`
: "";
return `${hours} ${hourString}${minuteString ? ` ${minuteString}` : ""}`;
};
export { Duration };
Finally, run the script:
npm run test # or
yarn test
The results of the tests should be printed:
$ yarn test
yarn run v1.22.19
$ ava
✔ Test Duration valid
✔ Test Duration invalid
─
1 test passed
Done in 3.04s.

Related

Human readable duration format - Codewars

It's a task to build a String with time values and units, separated with ", "
link to Codewars
I'm stuck at the end. I wrote this code:
function formatDuration (seconds) {
// return now if seconds = 0
if (seconds == 0) {
return "now";
}
// count values of each unit
let Y = Math.floor(seconds / 31536000);
let D = Math.floor(seconds / 86400 - Y * 365);
let H = Math.floor(seconds / 3600 - D * 24 - Y * 8760);
let M = Math.floor(seconds / 60 - H * 60 - D * 1440 - Y * 525600);
let S = seconds - M * 60 - H * 3600 - D * 86400 - Y * 31536000;
// build time values + units
let YY = Y + " year";
if (Y != 1) {
YY += "s"
}
let DD = D + " day";
if (D != 1) {
DD += "s"
}
let HH = H + " hour";
if (H != 1) {
HH += "s"
}
let MM = M + " minute";
if (M != 1) {
MM += "s"
}
let SS = S + " second";
if (S != 1) {
SS += "s"
}
let timeDigits = [S, M, H, D, Y];
let timeWhole = [SS, MM, HH, DD, YY];
// check which units are not 0's
let notZeros = [];
for (let i in timeDigits) {
if (timeDigits[i] != 0) {
notZeros += i;
}
}
// iterate notZeros through timeWhole and build the answer
let format = "";
for (let j = 0; j < notZeros.length; j++) {
if (notZeros.length != 1) {
// if it's the last unit
if (j == notZeros[0]) {
format = " and " + timeWhole[notZeros[j]] + format;
}
// if it's another unit
else if (j != notZeros[notZeros.length-1]){
format = ", " + timeWhole[notZeros[j]] + format;
}
// if it's the first unit
else {
format = timeWhole[notZeros[j]] + format;
}
}
// if it's a single unit
else {
format = timeWhole[notZeros[j]];
}
}
return format;
}
It works in most cases. on 100/110 tests made by Codewars results are green:
Test Passed: Value == '6 years, 192 days, 13 hours, 3 minutes and 54 seconds'
But on 10 of them I get this:
Expected: '4 years, 68 days, 3 hours and 4 minutes', instead got: ', 4 years, 68 days and 3 hours, 4 minutes'
Expected: '97 days, 10 hours and 26 seconds', instead got: ', 97 days, 10 hours and 26 seconds'
Expected: '33 days, 59 minutes and 55 seconds', instead got: ', 33 days, 59 minutes and 55 seconds'
Expected: '114 days, 46 minutes and 34 seconds', instead got: ', 114 days, 46 minutes and 34 seconds'
Problem is the ", " in the beginning of these, but I can't find any regularity in it.
Also on the failed test with Years unit, Hours and Minutes are shuffled.
I think an easier approach would be, instead of the whole last section, construct an array of strings - eg ['6 years', '192 days', ...] - then pop the last off, join the rest by a comma, then add on and to the final one (if there's any of them other than the final one):
const Y = Math.floor(seconds / 31536000);
const D = Math.floor(seconds / 86400 - Y * 365);
const H = Math.floor(seconds / 3600 - D * 24 - Y * 8760);
const M = Math.floor(seconds / 60 - H * 60 - D * 1440 - Y * 525600);
const S = seconds - M * 60 - H * 3600 - D * 86400 - Y * 31536000;
const plural = num => num === 1 ? '' : 's';
const YY = Y ? Y + ' year' + plural(Y) : null;
const DD = D ? D + ' day' + plural(D) : null;
const HH = H ? H + ' hour' + plural(H) : null;
const MM = M ? M + ' minute' + plural(M) : null;
const SS = S ? S + ' second' + plural(S) : null;
const nonNullValues = [YY, DD, HH, MM, SS].filter(Boolean);
const last = nonNullValues.pop();
return nonNullValues.length === 0
? last
: nonNullValues.join(', ') + ' and ' + last;
function formatDuration (seconds) {
if(!seconds) return 'now';
const units = {
year : 24 * 60 * 60 * 1 * 365,
day : 24 * 60 * 60 * 1,
hour : 60 * 60 * 1,
minute: 60 * 1,
second: 1
}
const sequence = ['year','day','hour','minute','second']
const calculateTime = (s) => {
let remaningTime = s
return sequence.reduce((acc,key) => {
if(remaningTime >= units[key]) {
acc[key] = Math.floor(remaningTime/units[key])
remaningTime = (remaningTime % units[key])
}
return acc
},{})
}
const displayUnit = (num,str) => `${num} ${str}` + ((num == 1) ? '' : 's')
const displayString = (timeObj) => {
let result = ''
let lastResult = ''
let objKeys = Object.keys(timeObj)
if (objKeys.length <= 1) return displayUnit(timeObj[objKeys[0]],objKeys[0]);
objKeys.forEach(v => {
if (result && lastResult) {
result += `, ${lastResult}`
} else if(lastResult) {
result += `${lastResult}`
}
lastResult = displayUnit(timeObj[v] , v)
})
return `${result} and ${lastResult}`
}
const obj = calculateTime(seconds)
return displayString(obj)
}

How to inverse this command?

I'm making a voice ranking system on Discord, for now everything is good, but I'm stuck in something.
I made this command, it will let you able to get the Xp amount from a level, and it converts the Xp to time, like:
The levels system is based on that code:
module.exports = {
name: 'lvl',
execute(message, args) {
const start = 216000 //From lvl1 to lvl2
const coe = 125 //Coefficient
const lvls = 2 //The number of lvls in each stage
function msToTime(duration) { //ms to time, usage: msToTime(xp + '00'), 1 xp = 0.1 sec
var milliseconds = parseInt((duration % 1000) / 100),
seconds = Math.floor((duration / 1000) % 60),
minutes = Math.floor((duration / (1000 * 60)) % 60),
hours = Math.floor((duration / (1000 * 60 * 60)));
hours = (hours < 10) ? "0" + hours : hours;
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;
return hours + "h & " + minutes + "m & " + seconds + "s";
}
function Stage(lvl) { //Get the stage number from a lvl, stages are used to divide lvls
const a = + lvl - 1
const b = Math.floor((a/2) + 1)
const c = b.toString()
console.log(c)
return c;
}
function Percentage(stage) { //Get the lvls inflation percentage, each stage the percentage will be larger
const a = stage * coe
const b = a.toString()
console.log(b)
return b;
}
function Xp(lvl) { //Get the Xp amount from a lvl
const a = (Percentage(Stage(lvl)) * (lvl - 1))
const b = start + (start * a / 100)
const c = b.toString()
console.log(c)
return c;
}
message.channel.send('Xp: ' + Xp(args[0]) + ', Time: ' + msToTime(Xp(args[0]) + '00')) //Send the Xp amount and the time
}
};
But now, I don't know how to inverse it to get the level from Xp, for example:
You type .xp 486000, it will give you lvl 2.
So anyone has an idea? ~and thx.
Edit: (I removed the stages function)
module.exports = {
name: 'lvl',
execute(message, args) {
const start = 216000 //From lvl1 to lvl2
const coe = 65 //Coefficient
function msToTime(duration) { //ms to time, usage: msToTime(xp + '00'), 1 xp = 0.1 sec
var milliseconds = parseInt((duration % 1000) / 100),
seconds = Math.floor((duration / 1000) % 60),
minutes = Math.floor((duration / (1000 * 60)) % 60),
hours = Math.floor((duration / (1000 * 60 * 60)));
hours = (hours < 10) ? "0" + hours : hours;
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;
return hours + "h & " + minutes + "m & " + seconds + "s";
}
function Percentage(lvl) { //Get the lvls inflation percentage
const a = lvl * coe
const b = a.toString()
console.log(b)
return b;
}
function Xp(lvl) { //Get the Xp amount from a lvl
const a = (Percentage(lvl) * (lvl - 1))
const b = start + (start * a / 100)
const c = b.toString()
console.log(c)
return c;
}
message.channel.send('Xp: ' + Xp(args[0]) + ', Time: ' + msToTime(Xp(args[0]) + '00')) //Send the Xp amount and the time
}
};
I am attempting this question because I am curious how the algebraic equations will be. So first of all it will NOT work because Math.floor is a lossy function. If lvl is even the answer is correct otherwise it MAY not be.
So, let's make some algebraic equations.
stage = st, coe = c, lvl = l, start = s, percentage = p, xp = x
stage is:
Percentage is:
XP is:
Solving for Lvl:
So, you can probably make one JS function out of this, but it will not be correct as I said, Math.floor is a lossy function.
You can check it out here:
https://www.mathpapa.com/algebra-calculator.html?q=x%3Ds%5Cleft(1%2B%5Cfrac%7B%5Cleft%5B%5Cleft(%5Cfrac%7B%5Cleft(l-1%5Cright)%7D%7B2%7D%2B1%5Cright)c%5Cleft(l-1%5Cright)%5Cright%5D%7D%7B100%7D%5Cright)
EDIT:
Without the Stage function, the equation will be:
You could define an object with the specific threshold at which a user levels up. Maybe level 1 is 0 seconds, level 2 is 1 hour, level 3 is 5 hours etc, you'd do it like this:
let levels = {
"1": 0
"2": 1 * 60 * 60 * 1000
"3": 5 * 60 * 60 * 1000
}
Then you could loop through the user's input and check whether it's higher than each level's threshold. Currently your XP is their time in ms. So if they say 10800000 XP (3 hours), a function like this would return that their level is "2":
function checkLevel(xp) => {
let outLevel = "0";
for (level in levels) {
let levelXp = levels[level];
if (xp > levelXp) outLevel = level;
else break;
}
return outLevel;
}
If you don't want level XP hardcoded, you could make use of a function that takes in a level and returns the XP required using whatever algorithm you want (ie, your Xp() function). You could do something like this:
function checkLevel(xp) => {
let checkLevel = 0;
let outLevel = 0;
while (true) {
let levelXp = levelToXp(checkLevel);
if (xp > levelXp) outLevel = level;
else return outLevel.toString();
checkLevel++;
}
}
This is it:
function Lvl(xp) { //Get the lvl from Xp
const a = ((start*coe) + Math.sqrt((start*coe)*((start*coe)-(400*start)+(400*xp)))) / (2*start*coe)
const b = a.toString()
console.log(a)
return b;
}

javascript timer/duration giving wrong calculation after while

couple of days ago, I found a javascript code which I thought that I can use it to calculate the login time for users.. but after tested it in my application, I found that there's a difference in time after while. So, I decided to use an chrome extension called "Staying Alive for Google Chrome", it worked perfectly "or that I was hoping to.. and to be honest, it kinda good", but after that I found the same problem as picture attached.
and here's code for javscript I found:
/*===============*/
var seconds = 0;
var minutes = 0;
var hours = 0;
var t;
function add() {
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
}
}
$('#totalTime').html ( "" + (hours ? (hours > 9 ? hours : "0" + hours) : "00") + ":" + (minutes ? (minutes > 9 ? minutes : "0" + minutes) : "00") + ":" + (seconds > 9 ? seconds : "0" + seconds) );
timer();
}
function timer() {
t = setTimeout(add, 1000);
}
/*===============*/
So, I hope that someone can help me, how to make timer run decently, or has a better solution, because I have more than 6 timers in this page..
P.S. this application coded using php and javascript and I've an ajax call every 5000 milliseconds running in same page.
P.P.S. one of my friends suggested webworkers , but I really don't know a lot about it.
thanks a lot
This behaviour is expected due to the fact JavaScript is executed in a single thread in the browser. Basically, the delay amount of the setTimeout and setInterval functions should be considered a minimum.
For example, if you would execute a function resulting in heavy DOM manipulation, this could postpone the execution of the callback.
For more information on this topic, read John Resig's post on the the execution order of JavaScript timers.
Your friend's suggestion could provide a solution as WebWorkers are executed in a different thread and hence are less subjective to blocking DOM manipulation. However, I would suggest you'd handle the time calculation on the server side. For example by creating an entry for the stream in a database containing at least the start date and time of the stream. Then either:
Let your JavaScript application poll the server to sync the time
Alternatively and preferably, set up a streaming connection using a WebSocket.
The best way to measure time is to save the value of the system clock (provided by Date-Object) at start and subtract it from the current timestamp.
An example:
const totalTime = document.querySelector('#totalTime');
const startTime = Date.now();
function updateClock() {
// JS Date counts in ms, divide by 1000 to get seconds
// |0 does the same as Math.floor(value)
const elapsedTime = (Date.now() - startTime) / 1000 |0;
const seconds = elapsedTime % 60;
const minutes = Math.floor(elapsedTime / 60) % 60;
const hours = Math.floor(elapsedTime / 3600) % 24;
totalTime.textContent =
(hours < 10 ? '0' : '') + hours + ':' +
(minutes < 10 ? '0' : '') + minutes + ':' +
(seconds < 10 ? '0' : '') + seconds;
}
window.setInterval(updateClock, 1000);
<div id="totalTime"></div>
EDIT 1 "from the one who asked":
I did some edits to your code cos I needed to add some options (reset, pause and resume).
I thought it could help any one, so I post it...
class Clock {
constructor(element, offset = 0) {
if(!(element && element instanceof HTMLElement))
throw new Error('Parameter element must be a valid HTML Element');
this._startTime = 0;
this._offset = offset;
this._interval = null;
this._element = element;
}
tick() {
const elapsedTime = (Date.now() - this._startTime) / 1000 |0;
const seconds = elapsedTime % 60;
const minutes = elapsedTime / 60 % 60 |0;
const hours = elapsedTime / 3600 % 24 |0;
this._element.textContent =
(hours < 10 ? '0' : '') + hours + ':' +
(minutes < 10 ? '0' : '') + minutes + ':' +
(seconds < 10 ? '0' : '') + seconds;
}
pause () {
if(this._interval) {
this._offset = Date.now() - this._startTime;
window.clearInterval(this._interval);
this._interval = null;
}
}
resume () {
if(!this._interval) {
this._startTime = Date.now() - this._offset;
this._interval = setInterval(this.tick.bind(this), 1000);
this.tick();
}
}
reset () {
this.pause();
this._offset = 0;
this._element.textContent = '00:00:00';
}
};
let clock = new Clock(document.querySelector('#totalTime'), 30000);
document.querySelector('#pause').addEventListener('click', () => { clock.pause() });
document.querySelector('#resume').addEventListener('click', () => { clock.resume() });
document.querySelector('#reset').addEventListener('click', () => { clock.reset() });
clock.resume();
<div id="totalTime"></div>
<button id="pause">Pause</button>
<button id="resume">Resume</button>
<button id="reset">Reset</button>

How to convert seconds to minutes and hours in javascript

I have a count of seconds stored in variable seconds. I want to convert for example 1439 seconds to 23 minutes and 59 seconds. And if the time is greater than 1 hour (for example 9432 seconds), to 2 hours, 37 minutes and 12 seconds.
How can I achieve this?
I'm thinking of:
var sec, min, hour;
if(seconds<3600){
var a = Math.floor(seconds/60); //minutes
var b = seconds%60; //seconds
if (b!=1){
sec = "seconds";
}else{
sec = "second";
}
if(a!=1){
min = "minutes";
}else{
min = "minute";
}
$('span').text("You have played "+a+" "+min+" and "+b+" "+sec+".");
}else{
var a = Math.floor(seconds/3600); //hours
var x = seconds%3600;
var b = Math.floor(x/60); //minutes
var c = seconds%60; //seconds
if (c!=1){
sec = "seconds";
}else{
sec = "second";
}
if(b!=1){
min = "minutes";
}else{
min = "minute";
}
if(c!=1){
hour = "hours";
}else{
hour = "hour";
}
$('span').text("You have played "+a+" "+hour+", "+b+" "+min+" and "+c+" "+sec+".");
}
But that's a lot of code, and it has to be calculated each second. How can I shrink this up?
I think you would find this solution very helpful.
You modify the display format to fit your needs with something like this -
function secondsToHms(d) {
d = Number(d);
var h = Math.floor(d / 3600);
var m = Math.floor(d % 3600 / 60);
var s = Math.floor(d % 3600 % 60);
var hDisplay = h > 0 ? h + (h == 1 ? " hour, " : " hours, ") : "";
var mDisplay = m > 0 ? m + (m == 1 ? " minute, " : " minutes, ") : "";
var sDisplay = s > 0 ? s + (s == 1 ? " second" : " seconds") : "";
return hDisplay + mDisplay + sDisplay;
}
The builtin JavaScript Date object can simplify the required code
toTime(seconds) {
var date = new Date(null);
date.setSeconds(seconds);
return date.toISOString().substr(11, 8);
}
You can try this, i have used this successfully in the past
You should be able to add the minutes and seconds on easily
function secondsToTime(secs)
{
var hours = Math.floor(secs / (60 * 60));
var divisor_for_minutes = secs % (60 * 60);
var minutes = Math.floor(divisor_for_minutes / 60);
var divisor_for_seconds = divisor_for_minutes % 60;
var seconds = Math.ceil(divisor_for_seconds);
var obj = {
"h": hours,
"m": minutes,
"s": seconds
};
return obj;
}
Fiddle
You can change the object to
var obj = {
"h": hours + " hours",
"m": minutes + " minutes",
"s": seconds + " seconds"
};
I'm probably a bit late but you can achieve this kind of things using
https://momentjs.com/
myVar = moment(myVar).format('HH:mm');
moment provides A LOT of format for hours / dates etc.
A low fat way to do this is:
function seconds_to_days_hours_mins_secs_str(seconds)
{ // day, h, m and s
var days = Math.floor(seconds / (24*60*60));
seconds -= days * (24*60*60);
var hours = Math.floor(seconds / (60*60));
seconds -= hours * (60*60);
var minutes = Math.floor(seconds / (60));
seconds -= minutes * (60);
return ((0<days)?(days+" day, "):"")+hours+"h, "+minutes+"m and "+seconds+"s";
}
Thus
> seconds_to_days_hours_mins_secs_str(9432+60*60*24)
'1 days, 2h, 37m and 12s'
This is easy to understand and extend as needed.
Try this, Convert SEC to H:M:S.
function convertTime(sec) {
var hours = Math.floor(sec/3600);
(hours >= 1) ? sec = sec - (hours*3600) : hours = '00';
var min = Math.floor(sec/60);
(min >= 1) ? sec = sec - (min*60) : min = '00';
(sec < 1) ? sec='00' : void 0;
(min.toString().length == 1) ? min = '0'+min : void 0;
(sec.toString().length == 1) ? sec = '0'+sec : void 0;
return hours+':'+min+':'+sec;
}
I found Wilson Lee's and Brian's code super useful! Here is how I adapted their code:
function formatTime(serverTimeinSeconds, elementId)
{ /* This converts seconds into days, hours, minutes and seconds timestring.
Requires JQuery if elementId argument is provided */
seconds = Math.floor(Number(serverTimeinSeconds));
days = Math.floor(seconds / (24*60*60));
seconds -= Math.floor(days * (24*60*60));
hours = Math.floor(seconds / (60*60));
seconds -= Math.floor(hours * (60*60));
minutes = Math.floor(seconds / (60));
seconds -= Math.floor(minutes * (60));
dDisplay = days > 0 ? days + (days == 1 ? ' day, ' : ' days, ') : '';
hDisplay = hours > 0 ? hours + (hours == 1 ? ' hour, ' : ' hours, ') : '';
mDisplay = minutes > 0 ? minutes + (minutes == 1 ? ' minute, ' : ' minutes, ') : '';
sDisplay = seconds > 0 ? seconds + (seconds == 1 ? ' second' : ' seconds') : '';
if (elementId != null) {
if (serverTimeinSeconds < 60) {
$(elementId).css('font-size', '15px');
$(elementId).html(sDisplay);
}
if (serverTimeinSeconds >= 60 && serverTimeinSeconds < 3600) {
$(elementId).css('font-size', '15px');
$(elementId).html(mDisplay + sDisplay);
}
if (serverTimeinSeconds >= 3600 && serverTimeinSeconds < 86400) {
$(elementId).css('font-size', '12px');
$(elementId).html(hDisplay + mDisplay + sDisplay);
}
if (serverTimeinSeconds >= 86400 && serverTimeinSeconds !== Infinity) {
$(elementId).css('font-size', '8px');
$(elementId).html(dDisplay + hDisplay + mDisplay + sDisplay);
}
}
return dDisplay + hDisplay + mDisplay + sDisplay;
}
Please install moment js after that import it,
import moment from 'moment'
let dateForm = (arg) => {
return moment.unix(arg).utc().format('H [hours,] m [minutes and] s [seconds]');
}
console.log(dateForm(11));
// 0 hours, 0 minutes and 11 seconds
console.log(dateForm(16060));
// 1 hours, 0 minutes and 0 seconds
const formatter = (seconds = 0) => {
const d = Number(secondsAmount);
const h = Math.floor(d / 3600);
const m = Math.floor((d % 3600) / 60);
const s = Math.floor((d % 3600) % 60);
const hDisplay = h > 0 ? `${h.toString().length > 1 ? `${h}` : `${0}${h}`}` : '00';
const mDisplay = m > 0 ? `${m.toString().length > 1 ? `${m}` : `${0}${m}`}` : '00';
const sDisplay = s > 0 ? `${s.toString().length > 1 ? `${s}` : `${0}${s}`}` : '00';
return `${hDisplay}:${mDisplay}:${sDisplay}`;
};
Will return this format human readable format 00:00:00
Built off R4nc1d's answer:
function secondsToTime(secs){
var h = Math.floor(secs / (60 * 60));
var divisor_for_minutes = secs % (60 * 60);
var m = Math.floor(divisor_for_minutes / 60);
var divisor_for_seconds = divisor_for_minutes % 60;
var s = Math.ceil(divisor_for_seconds);
return `${h?`${h}:`:""}${m?`${m}:${s}`:`${s}s`}`
}
This will return a human readable answer which looks like this. I used this for displaying the length of music tracks
time = secondsToTime(5)
console.log(time) // 5s
time = secondsToTime(50)
console.log(time) // 50s
time = secondsToTime(500)
console.log(time) // 8:20
time = secondsToTime(5000)
console.log(time) // 1:23:20
#pkerckhove has already mentioned moment as a great library to work with dates and times, and you can also use moment to directly format the seconds into OP's desired format, i.e.:
import moment from 'moment'
const myVar = 1439
console.log(
moment.unix(myVar).utc().format('H [hours,] m [minutes and] s [seconds]')
)
Will result in: 0 hours, 23 minutes and 59 seconds and,
import moment from 'moment'
const myVar = 9432
console.log(
moment.unix(myVar).utc().format('H [hours,] m [minutes and] s [seconds]')
)
Will result in: 2 hours, 37 minutes and 12 seconds
One way of doing it:
const formatDuration = totalSeconds => {
const hours = Math.floor(totalSeconds / 3600)
const minutes = Math.floor((totalSeconds % 3600) / 60)
const seconds = totalSeconds - hours * 3600 - minutes * 60
return [`${hours}h`, `${minutes}m`, `${seconds}s`]
.filter(item => item[0] !== '0')
.join(' ')
}
This method also works with a negative amount of time:
function CalculateTime(sec){
if(sec >= 0){
var h = Math.floor(sec / 3600);
var m = Math.floor(sec % 3600 / 60);
var s = Math.floor(sec % 3600 % 60);
}
else{
var h = Math.ceil(sec / 3600);
var m = Math.ceil(sec % 3600 / 60);
var s = Math.ceil(sec % 3600 % 60);
}
var hDisplay = h !== 0 ? h + (h == 1 ? " hour, " : " hours") + (m != 0 || s > 0 ? ", ":"") : "";
var mDisplay = m !== 0 ? m + (m == 1 ? " minute, " : " minutes") + (s != 0 ? " ":""): "";
var sDisplay = s !== 0 ? s + (s == 1 ? " second" : " seconds") : "";
return hDisplay + mDisplay + sDisplay;
}
for having the result with the time format " 00:00:00 " I added some changes to it.
function secondsToHms(seconds) {
let d = Number(seconds);
if(d <= 0){
return '00:00:00'
}else{
let h = Math.floor(d / 3600);
let m = Math.floor(d % 3600 / 60);
let s = Math.floor(d % 3600 % 60);
let hDisplay = h <= 9 ? '0'+ h+':' : h+ ":";
let mDisplay = m <= 9 ? '0'+ m+':' : m+ ":";
let sDisplay = s <= 9 ? '0'+ s : s;
return hDisplay + mDisplay + sDisplay;
}}
Try this :D
secondsToHms(d) {
d = Number(d);
var h = Math.floor(d / 3600);
var m = Math.floor(d % 3600 / 60);
var s = Math.floor(d % 3600 % 60);
var hDisplay = h > 0 ? h + (h == 1 ? "" : "") : "";
var mDisplay = m > 0 ? m + (m == 1 ? "" : "") : "";
var sDisplay = s > 0 ? s + (s == 1 ? "" : "") : "";
if (hDisplay != "") {
return (hDisplay.length > 1 ? hDisplay : '0' + hDisplay) + ":" + (mDisplay.length > 1 ? mDisplay : '0' + mDisplay) + ":" + (sDisplay.length > 1 ? sDisplay : '0' + sDisplay);
}
else if (mDisplay != "") {
return (mDisplay.length > 1 ? mDisplay : '0' + mDisplay) + ":" + (sDisplay.length > 1 ? sDisplay : '0' + sDisplay);
}
else if (sDisplay != "") {
return "00:" + (sDisplay.length > 1 ? sDisplay : '0' + sDisplay);
}
return "00:00"
}
Using the popular date-fns library
import { format, setSeconds, startOfDay } from 'date-fns'
export const hourMinSec = (secs: number, showHour = false): string => {
const tmpDate: Date = startOfDay(new Date())
const date: Date = setSeconds(tmpDate, secs)
const hour: number = date.getHours()
const hasHour: boolean = !!hour
if (hasHour && !showHour) console.warn('hourMinSec is hiding a non zero hour')
const strFormat: string = showHour ? 'H:mm:ss' : 'm:ss'
return format(date, strFormat)
}
OR the same code with a more functional approach (adding lodash flow in the mix)
import { setSeconds, startOfDay } from 'date-fns/fp'
import { format } from 'date-fns'
import { flow } from 'lodash-es'
export const hourMinSec = (secs: number, showHour = false): string => {
const date: Date = flow(startOfDay, setSeconds(secs))(new Date())
const hour: number = date.getHours()
const hasHour: boolean = !!hour
if (hasHour && !showHour) console.warn('hourMinSec is hiding a non zero hour')
const strFormat: string = showHour ? 'H:mm:ss' : 'm:ss'
return format(date, strFormat)
}
Usage:
hourMinSec(100) // 1:40
hourMinSec(3700) // 1:40 // will warn in logs that a non zero hour is hidden
hourMinSec(100, true) // 0:01:40
hourMinSec(3700, true) // 1:01:40
This met my needs but you could adapt this by changing the showHour = false parameter to be a strFormat = 'm:ss' parameter instead to support more flexible formatting.
const minutes = Math.floor(duration / 60);
const seconds = Math.floor(duration - minutes * 60);
const time = `${minutes < 10 ? `0${minutes}` : minutes}
:${seconds < 10 ? `0${seconds}` : seconds}`; // result: 02:23
useCountDown Hook
// useCountDown.js
import { useEffect, useState } from "react"
const useCountDown = (minutes) => {
const [seconds, setSeconds] = useState(minutes * 60)
useEffect(() => {
const interval = setInterval(() => {
setSeconds(seconds - 1)
}, 1000)
return () => clearInterval(interval)
}, [seconds])
return getReturnValues2(seconds)
}
const getReturnValues2 = (countDown) => {
const minutes = Math.floor(countDown / 60)
const seconds = countDown % 60
return `${padTo2Digits(minutes)}:${padTo2Digits(seconds)}`
}
function padTo2Digits(num) {
return num.toString().padStart(2, "0")
}
export default useCountDown
How to use?
//React Component
import useCountDown from '../hooks/useCountDown'
function App() {
const countDown = useCountDown(5) // 5 Minutes
return (
<h1> {countDown} </h1> // MM:SS
)
}
You can tweak as per your needs.
Convert to H:M
Number(moment.duration(Number(37320), 'seconds').hours()+'.'+moment.duration(Number(37320),'seconds').minutes())

Convert seconds to days, hours, minutes and seconds

I have a Javascript timing event with an infinite loop with a stop button.
It will display numbers when start button is click.Now I want this numbers converted to something like 4 hours, 3 minutes , 50 seconds
var c = 0;
var t;
var timer_is_on = 0;
function timedCount() {
document.getElementById('txt').value = c;
c = c + 1;
t = setTimeout(function() {
timedCount()
}, 1000);
}
function doTimer() {
if (!timer_is_on) {
timer_is_on = 1;
timedCount();
}
}
function stopCount() {
clearTimeout(t);
timer_is_on = 0;
}
$(".start").on("click", function() {
//var start = $.now();
//alert(start);
//console.log(start);
doTimer();
$(".end").show();
$(".hide_div").show();
});
$(".end").on("click", function() {
stopCount();
});
.hide_div {
display: none;
}
.end {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="start">Start</p>
<p class="end">End</p>
<p class="hide_div">
<input type="text" id="txt" />//display numbers eg 12345
</p>
How to convert numbers like 123456 to 1 day, 4 hours, 40 min, 45 seconds?
I suggest doing this way!:
function secondsToDhms(seconds) {
seconds = Number(seconds);
var d = Math.floor(seconds / (3600*24));
var h = Math.floor(seconds % (3600*24) / 3600);
var m = Math.floor(seconds % 3600 / 60);
var s = Math.floor(seconds % 60);
var dDisplay = d > 0 ? d + (d == 1 ? " day, " : " days, ") : "";
var hDisplay = h > 0 ? h + (h == 1 ? " hour, " : " hours, ") : "";
var mDisplay = m > 0 ? m + (m == 1 ? " minute, " : " minutes, ") : "";
var sDisplay = s > 0 ? s + (s == 1 ? " second" : " seconds") : "";
return dDisplay + hDisplay + mDisplay + sDisplay;
}
Use Math like this way, Second param in parseInt is for base, which is optional
var seconds = parseInt(123456, 10);
var days = Math.floor(seconds / (3600*24));
seconds -= days*3600*24;
var hrs = Math.floor(seconds / 3600);
seconds -= hrs*3600;
var mnts = Math.floor(seconds / 60);
seconds -= mnts*60;
console.log(days+" days, "+hrs+" Hrs, "+mnts+" Minutes, "+seconds+" Seconds");
Your given seconds 123456 would be 1 days, 10 Hrs, 17 Minutes, 36 Seconds not 1 days, 4 Hrs, 40 Minutes, 45 Seconds
function countdown(s) {
const d = Math.floor(s / (3600 * 24));
s -= d * 3600 * 24;
const h = Math.floor(s / 3600);
s -= h * 3600;
const m = Math.floor(s / 60);
s -= m * 60;
const tmp = [];
(d) && tmp.push(d + 'd');
(d || h) && tmp.push(h + 'h');
(d || h || m) && tmp.push(m + 'm');
tmp.push(s + 's');
return tmp.join(' ');
}
// countdown(3546544) -> 41d 1h 9m 4s
// countdown(436654) -> 5d 1h 17m 34s
// countdown(3601) -> 1h 0m 1s
// countdown(121) -> 2m 1s
My solution with map() and reduce():
const intervalToLevels = (interval, levels) => {
const cbFun = (d, c) => {
let bb = d[1] % c[0],
aa = (d[1] - bb) / c[0];
aa = aa > 0 ? aa + c[1] : '';
return [d[0] + aa, bb];
};
let rslt = levels.scale.map((d, i, a) => a.slice(i).reduce((d, c) => d * c))
.map((d, i) => ([d, levels.units[i]]))
.reduce(cbFun, ['', interval]);
return rslt[0];
};
const TimeLevels = {
scale: [24, 60, 60, 1],
units: ['d ', 'h ', 'm ', 's ']
};
const secondsToString = interval => intervalToLevels(interval, TimeLevels);
If you call secondsToString(123456), you can get "1d 10h 17m 36s "
Here is my solution, a simple function that will round to the nearest second!
var returnElapsedTime = function(epoch) {
//We are assuming that the epoch is in seconds
var hours = epoch / 3600,
minutes = (hours % 1) * 60,
seconds = (minutes % 1) * 60;
return Math.floor(hours) + " hours, " + Math.floor(minutes) + " minutes, " + Math.round(seconds) + " seconds";
}
Came up with my own variation to some of the solutions suggested in this thread.
if (!Number.prototype.secondsToDHM) {
Number.prototype.secondsToDHM = function() {
const secsPerDay = 86400;
const secsPerHour = 3600;
const secsPerMinute = 60;
var seconds = Math.abs(this);
var minus = (this < 0) ? '-' : '';
var days = Math.floor(seconds / secsPerDay);
seconds = (seconds % secsPerDay);
var hours = Math.floor(seconds / secsPerHour);
seconds = (seconds % secsPerHour);
var minutes = Math.floor(seconds / secsPerMinute);
seconds = (seconds % secsPerMinute);
var sDays = new String(days).padStart(1, '0');
var sHours = new String(hours).padStart(2, '0');
var sMinutes = new String(minutes).padStart(2, '0');
return `${minus}${sDays}D ${sHours}:${sMinutes}`;
}
}
var a = new Number(50000).secondsToDHM();
var b = new Number(100000).secondsToDHM();
var c = new Number(200000).secondsToDHM();
var d = new Number(400000).secondsToDHM();
console.log(a);
console.log(b);
console.log(c);
console.log(d);
This answer builds upon on Andris' approach to this question, but it doesn't have trailing commas if lesser units are not present.
It also borrows from this answer dealing with joining array values only if truthy:
https://stackoverflow.com/a/19903063
I'm not a javascript god and it's probably horribly over-engineered, but hopefully readable and correct!
function sformat(s) {
// create array of day, hour, minute and second values
var fm = [
Math.floor(s / (3600 * 24)),
Math.floor(s % (3600 * 24) / 3600),
Math.floor(s % 3600 / 60),
Math.floor(s % 60)
];
// map over array
return $.map(fm, function(v, i) {
// if a truthy value
if (Boolean(v)) {
// add the relevant value suffix
if (i === 0) {
v = plural(v, "day");
} else if (i === 1) {
v = plural(v, "hour");
} else if (i === 2) {
v = plural(v, "minute");
} else if (i === 3) {
v = plural(v, "second");
}
return v;
}
}).join(', ');
}
function plural(value, unit) {
if (value === 1) {
return value + " " + unit;
} else if (value > 1) {
return value + " " + unit + "s";
}
}
console.log(sformat(60)); // 1 minute
console.log(sformat(3600)); // 1 hour
console.log(sformat(86400)); // 1 day
console.log(sformat(8991)); // 2 hours, 29 minutes, 51 seconds
If you needed to convey the duration more 'casually' in words, you could also do something like:
var remaining_duration = sformat(117);
// if a value is returned, add some prefix and suffix
if (remaining_duration !== "") {
remaining_duration = "about " + remaining_duration + " left";
}
$(".remaining_duration").text(remaining_duration);
// returns 'about 1 minute, 57 seconds left'
I further tweaked the code by Svetoslav as follows:
function convertSecondsToReadableString(seconds) {
seconds = seconds || 0;
seconds = Number(seconds);
seconds = Math.abs(seconds);
const d = Math.floor(seconds / (3600 * 24));
const h = Math.floor(seconds % (3600 * 24) / 3600);
const m = Math.floor(seconds % 3600 / 60);
const s = Math.floor(seconds % 60);
const parts = [];
if (d > 0) {
parts.push(d + ' day' + (d > 1 ? 's' : ''));
}
if (h > 0) {
parts.push(h + ' hour' + (h > 1 ? 's' : ''));
}
if (m > 0) {
parts.push(m + ' minute' + (m > 1 ? 's' : ''));
}
if (s > 0) {
parts.push(s + ' second' + (s > 1 ? 's' : ''));
}
return parts.join(', ');
}
Short answer:
var s = (Math.floor(123456/86400) + ":" + (new Date(123456 * 1000)).toISOString().substr(11, 8)).split(":");
console.log(`${s[0]} days, ${s[1]} hours, ${s[2]} minutes, ${s[3]} seconds` )
Edit:
Let me break it down in parts :
Math.floor(123456/86400)
86400 is the the total seconds in a day (60 seconds * 60 minutes * 24 hours). Dividing the inputted seconds by this value gives us number of days. We just need the whole part so we use Math.floor because the fractional piece is handled by this part:
(new Date(123456 * 1000)).toISOString().substr(11, 8)
the explanation can be found here:
Convert seconds to HH-MM-SS with JavaScript?
It just outputs hh:mm:ss, no days. So the first part and this part is a perfect combination
We concatenate using a colon (:) as a separator. The string looks like this:
'1:10:17:36'
We split it into an array with .split(":");. Then finally, we format the elements of the array for the desired output.
I've tweaked the code that Andris posted https://stackoverflow.com/users/3564943/andris
// https://stackoverflow.com/questions/36098913/convert-seconds-to-days-hours-minutes-and-seconds
function app_ste_36098913_countdown_seconds_to_hr(seconds) {
seconds = seconds || 0;
seconds = Number(seconds);
seconds = Math.abs(seconds);
var d = Math.floor(seconds / (3600*24));
var h = Math.floor(seconds % (3600*24) / 3600);
var m = Math.floor(seconds % 3600 / 60);
var s = Math.floor(seconds % 60);
var parts = new Array();
if (d > 0) {
var dDisplay = d > 0 ? d + ' ' + (d == 1 ? "day" : "days") : "";
parts.push(dDisplay);
}
if (h > 0) {
var hDisplay = h > 0 ? h + ' ' + (h == 1 ? "hour" : "hours") : "";
parts.push(hDisplay)
}
if (m > 0) {
var mDisplay = m > 0 ? m + ' ' + (m == 1 ? "minute" : "minutes") : "";
parts.push(mDisplay)
}
if (s > 0) {
var sDisplay = s > 0 ? s + ' ' + (s == 1 ? "second" : "seconds") : "";
parts.push(sDisplay)
}
return parts.join(', ', parts);
}
You will probably find using epoch timestamps more straightforward: As detailed in Convert a Unix timestamp to time in JavaScript, the basic method is like so:
<script>
// Create a new JavaScript Date object based on the timestamp
// multiplied by 1000 so that the argument is in milliseconds, not seconds.
var date1 = new Date();
alert ('easy trick to waste a few seconds...' + date1);
// var date = date2 - date1;
// Hours part from the timestamp
var hours1 = date1.getHours();
// Minutes part from the timestamp
var minutes1 = "0" + date1.getMinutes();
// Seconds part from the timestamp
var seconds1 = "0" + date1.getSeconds();
var date2 = new Date();
// Hours part from the timestamp
var hours2 = date2.getHours();
// Minutes part from the timestamp
var minutes2 = "0" + date2.getMinutes();
// Seconds part from the timestamp
var seconds2 = "0" + date2.getSeconds();
// Will display time in 10:30:23 format
// var formattedTime = hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
var elapsedHrs = hours2 - hours1;
var elapsedMin = minutes2.substr(-2) -minutes1.substr(-2);
var elapsedSec = seconds2.substr(-2) - seconds1.substr(-2);
var elapsedTime = elapsedHrs + ' hours, ' + elapsedMin + ' minutes, ' + elapsedSec + ' seconds';
alert ('time between timestamps: ' + elapsedTime);
</script>
Be warned that this script needs some work since for now it will give negative values for things like date1 = 12:00:00 and date2 = 12:00:05, but I'll leave that to you fo now.
You should rewrite your code to take a timestamp ( var x = new Date(); ) at the start of your timer and one whenever you are done/want to check elapsed time, and subtract the two before parsing out elapsed seconds, minutes, hours etc as required.
This is my take at the question, even if it is an old topic.
You can use a loop to compute everything for you :
function time_remaining(date1, date2) {
let seconds = (date2 - date1) / 1000
let units = ["years", "days", "h", "min", "s"]
let limit_units = [365, 24, 60, 60, 1]
const reducer = (accumulator, curr) => accumulator * curr;
let time = []
for (let i = 0; i < units.length; i++) {
let divisor = limit_units.slice(i).reduce(reducer)
let value = Math.floor(seconds / divisor)
seconds = seconds - value * divisor
time.push(value)
}
return clean_time(time, units)
}
// at this point, you have your answer. However,
// we can improve the result by removing all none
// significative null units (i.e, if your countdown is
// only about hours, minutes and seconds, it is not
// going to include years and days.)
function clean_time(time, units) {
time = time.reverse()
while (time[time.length - 1] == 0) {
time.pop()
}
return [time.reverse(), units.slice(-time.length)]
}
let date1 = Date.parse("2023-07-09T17:50:33")
console.log(time_remaining(Date.now(), date1))

Categories