I am trying to make a small question/answer quiz game using react, and I want to show a timer that counts down every second. Each game will last 10, 15, or 30 minutes at most, so I want to show a timer that updates every second in the bottom of the screen (in big font, of course!), something like 15:00, 14:59, 14:58, and so on until it hits 00:00.
So, given a start time such as 2016-04-25T08:00:00Z, and an end time after adding 15 min of 2016-04-25T08:15:00Z, I want to start the countdown.
My issue is that I am not understanding how to use setIntervals to keep calling my method to find the remaining time.
timeLeft = Math.round(timeLeft/1000) * 1000;
const timer = new Date(timeLeft);
return timer.getUTCMinutes() + ':' + timer.getUTCSeconds();
EDIT: You've edited your question. You will need the time padding, and the method below will be faster than what you are using, but to answer your question about setInterval:
First, define your function to run your timer and decrement each time it's called:
var timeLeft; // this is the time left
var elem; // DOM element where your timer text goes
var interval = null; // the interval pointer will be stored in this variable
function tick() {
timeLeft = Math.round(timeLeft / 1000) * 1000;
const timer = new Date(timeLeft);
var time = timer.getUTCMinutes() + ':' + timer.getUTCSeconds();
elem.innerHTML = time;
timeLeft -= 1000; // decrement one second
if (timeLeft < 0) {
clearInterval(interval);
}
}
interval = setInterval(tick, 1000);
OG Answer:
No, I do not believe there is a built-in way to display time differences.
Let's say you have two date objects:
var start = Date.now();
var end = Date.now() + 15 * 60 * 1000; // 15 minutes
Then you can subtract the two Date objects to get a number of milliseconds between them:
var diff = (end - start) / 1000; // difference in seconds
To get the number of minutes, you take diff and divide it by 60 and floor that result:
var minutes = Math.floor(diff / 60);
To get the number of seconds, you take the modulus to get the remainder after the minutes are removed:
var seconds = diff % 60;
But you want these two padded by zeros, so to do that, you convert to Strings and check if they are two characters long. If not, you prepend a zero:
// assumes num is a whole number
function pad2Digits(num) {
var str = num.toString();
if (str.length === 1) {
str = '0' + str;
}
return str;
}
var time = pad2Digits(minutes) + ':' + pad2Digits(seconds);
Now you have the time in minutes and seconds.
Related
I am creating a website for students which will be used to assign exams and I am having difficulties with the timer. The one I am using is made on the frontend in javascript and whenever the page is refreshed the timer startsover. Tried to store the start and end date by converting to epoch and back to datetime but I cannot think of a way to get the timer to the frontend and start counting. The idea is to count 60 minutes and call the submit button as well as to show the countdown without the option to restart the counter.
This is how I store the start and end time in nodejs.
var myDate = new Date();
var startTimeEpoch = myDate.getTime()/1000.0;
var endTimeEpoch = startTimeEpoch + 5400 // Adding 90 minutes to the timer
var startTimeBackToDate = new Date(startTimeEpoch *1000)
var endTimeBackToDate = new Date(endTimeEpoch *1000)
This is the javascript timer I am using and I am wondering if I should use one in the first place.
function startTimer(duration, display) {
var start = Date.now(),
diff,
minutes,
seconds;
function timer() {
diff = duration - (((Date.now() - start) / 1000) | 0);
minutes = (diff / 60) | 0;
seconds = (diff % 60) | 0;
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
display.textContent = minutes + ":" + seconds;
if (diff <= 0) {
start = Date.now() + 1000;
}
}
timer();
setInterval(timer, 1000);
}
window.onload = function () {
var fiveMinutes = "<%= scenario.time %>" * 60,
display = document.querySelector('#time');
startTimer(fiveMinutes, display);
}
As a general response and with the additional information provided, i could propose a solution to make this work.
If your students all have a specific exam entity attached to them, when they register/start an exam, you could retrieve the start date of this exam(add a mongo createdAt Date field) and use it as the starting date.
If each exam has a time limit, then you could simply do the math to know how much time is left. Something that will look like this:
const getExamRemainingTime = (exam) => {
// assuming that start is a js date object
// and timeLimit is an number representing the duration hours of your exam
const { start, timeLimit } = exam;
let end = (start.getHours() + timeLimit);
end = end.setHours(end);
const remainingTime = (+end) - (+start)
if (remainingTime > 0) {
// duration not finished, exam still in progress
return new Date(remainingTime);
} else {
// exam finished
return 0;
}
}
Then in your frontend, if it's plain javascript, you need to refresh your timer component, use setInterval in last ressort because it's very heavy on performance and format the date you got the way you want to show it.
Ref: casting js Date object to timestamp - How do you get a timestamp in JavaScript?.
I don't think a timer that a student with Javascript knowledge can modify should be used for serious tests, but for anything more light-hearted it should be fine.
The best system I can think of for this would be to have the test length stored in the mongodb and when a signed-in user starts the test, have the current time logged for that user. That way, you can calculate time remaining using user.testStart + test.length - Date.now().
I am trying to convert time duration from the format of mm:ss.mss to entirely milliseconds and back.
I've already have a working function for converting from milliseconds to duration but I cannot seem to get it the other way around.
Lets say for instance that I have the duration 32:29.060, I want to convert it to milliseconds. For that I use this function:
function millisecondsToTime(ms, digits) {
digits = digits || 12;
return new Date(ms).toISOString().slice(23-digits, -1);
}
var a = millisecondsToTime(5549060, 9);
but whenever I try to convert back to time duration, I fail. I've tried parsing individually the minutes, seconds and milliseconds but it doesn't seem to work.
Here is the code that I've used for it:
var firstSplit = a.split(':')
var minutes = firstSplit[0]; //1
var secondSplit = firstSplit[1].split('.');
var seconds = secondSplit[0]; //2
var millisec = secondSplit[1]; //3
var conversion = ((+minutes) * 60 + (+seconds) * 60 + (+millisec))*1000;
I have an input bar which takes the format of mm:ss.mss and I need to convert it to milliseconds. How can I do that?
you can just return a
new Date(ms)
to get a date from ms.
And to get the same date as ms,
date.getTime() // returns ms from date object
Full example:
const ms = 5549060
const date = new Date(ms) // get a date from ms
console.log(date.getTime) // logs 5569060
If your input is a string in the format of mm:ss.mss, and you want to get a date from it, you can use moment.
const moment = require('moment')
const date = moment('22:15.143', 'mm:ss.SSS') // get date from pre specified format
You can use the string methods indexOf() and substr() to get the individual numbers out of your string and calculate the time accordingly.
I'm afraid though your millisecondsToTime() function isn't working properly.
5549060 milliseconds are roughly 92 minutes and it's returning 32:29.060
function backToTime(time) {
var index = time.indexOf(":");
var minutes = time.substr(0, index);
var seconds = time.substr(index + 1, time.indexOf(".") - (index + 1));
var milliseconds = time.substr(time.indexOf(".") + 1, time.length);
return parseInt(minutes * 60 * 1000) + parseInt(seconds * 1000) + parseInt(milliseconds);
}
console.log(backToTime("32:29.060"));
Your conversion to milliseconds is not working, this is basic math approach to both conversions:
let input = 5549060
//toDuration
let seconds = Math.floor(input / 1000);
let ms = input - seconds*1000;
let m = Math.floor(seconds / 60);
let s = seconds - m*60;
duration = m + ":" + s + "." + ms
console.log(duration)
//toMilliseconds
let holder = duration.split(":");
m = parseInt(holder[0]);
holder = holder[1].split(".");
s = parseInt(holder[0]);
ms = parseInt(holder[1]);
milliseconds = (m*60 + s)*1000 + ms
console.log(milliseconds)
If needed add check for ms length to add 0s, if you need it to have length of 3
I think your milliseconds to duration converter will be broken for durations above 60 minutes. This is because using Date the minutes field will wrap over into the minutes after 59 seconds have passed. If you want to get good support for values beyond 59 in your first field, I think maybe moving to a regex-based parser and using multiplication and addition, division and modulo to extract and reduce the fields manually might be nice. Something like this maybe:
var duration = ms => `${(ms / 60000) | 0}`.padStart(2, '0') + `:` + `${ms % 60000 / 1000 | 0}`.padStart(2, '0') + `.` + `${ms % 1000}`.padStart(3, '0')
var millisec = durat => (match => match && Number(match[1]) * 60000 + Number(match[2]) * 1000 + Number(match[3]))(/^([0-9]+)\:([0-5][0-9])\.([0-9]{3})$/.exec(durat))
You can see given the input 5549060, this function provides output 92:29.60, which is exactly 60 seconds greater than your own, and I believe to be correct. Maybe it's intentional for your usecase, but I can't imagine that being so desirable generally...
I am trying two create to separate timers. One timer counts down to a date and displays a countdown and the other counts down on an interval and resets (ie: 5 hours and resets).
The one I am having trouble with is the second option. I am trying to create a countdown that is relative to real-time and then resets once it reaches zero. So for example setting it to 2 days and 5 hours. Once this completes the clock resets to 2 days 5 hours. I am having trouble getting the clock to reset at the specified time and loop without having negative numbers. I tried this two separate ways but feel like I am over-complicating things.
The reason I use real-time is so that the clock will be the same if you open it in another tab. If I create a regular timer it will reset upon refreshing the page.
codpen
In this example I tried to reset the counter every 40 seconds but couldn't get it to work. Ultimately I want to be able to specify the date with ie: 00:12:00 (12 hours countdown) and then have it reset automatically. I just can't figure out how to maintain the counting without going to negative numbers or freezing it.
function timer() {
var currentTime = new Date()
var date = currentTime.getDate()
var hours = currentTime.getHours()
var minutes = currentTime.getMinutes()
var seconds = currentTime.getSeconds()
var daysLeft = 0;
var hoursLeft = 24 - hours;
var minsLeft = 60 - minutes;
var secsLeft = 60 - seconds;
// counter freezes at 40 seconds and hangs for 20seconds
if(secsLeft => 40) {
secsLeft = 40 - seconds
if(secsLeft < 0) {
secsLeft = 40
}
}
document.getElementById('timerUpFront').innerHTML= "<br><br><strong>Duration Countdown with Infinite Reset #2</strong><br>" + daysLeft + " days " + hoursLeft + " hours " + minsLeft + " minutes " + secsLeft + " seconds";
}
var countdownTimer = setInterval('timer()', 1000);
codpen
you can separate the timer to functions to simplify it and apply the following logic
function startTimer () {
val targetRemainedSeconds = // calculate the value
val remainedSeconds = targetRemainedSeconds
setInterval(timer(), 1000)
}
function timer () {
remainedSeconds--
if (remainedSeconds < 0) reaminedSeconds = targetReaminedSeconds // reset the timer
timerUpdate()
}
function timerUpdate() {
// use 'remainedSeconds' to update timer
}
I've a problem when running this script for my JavaScript countdown (using this plugin). What it should do is take the starting time, the current time and the end time and display the remaining time.
If I set these values with normal numbers in epoch time everything works just fine, but my question is: How do I set the current time and the start to be the real current one so that the countdown will be dynamic?
I've found this line: Math.round(new Date().getTime()/1000.0);
But I don't know how to make it work, considering I'm running this script at the bottom of my HTML file, before the </html> tag.
This is the script:
<script>
$('.countdown').final_countdown({
start: '[amount Of Time]',
end: '[amount Of Time]',
now: '[amount Of Time]'
});
</script>
This is how I tried to solve it, but it's not working:
//get the current time in unix timestamp seconds
var seconds = Math.round(new Date().getTime()/1000.0);
var endTime = '1388461320';
$('.countdown').final_countdown({
start: '1362139200',
end: endTime,
now: seconds
});
It sounds like you would like to count down from the current time to some fixed point in the future.
The following example counts down and displays the time remaining from now (whenever now might be) to some random time stamp within the next minute.
function startTimer(futureTimeStamp, display) {
var diff;
(function timer() {
// how many seconds are between now and when the count down should end
diff = (futureTimeStamp - Date.now() / 1000) | 0;
if (diff >= 0) {
display(diff);
setTimeout(timer, 1000);
}
}());
}
// wait for the page to load.
window.onload = function() {
var element = document.querySelector('#time'),
now = Date.now() / 1000,
// some random time within the next minute
futureTimeStamp = Math.floor(now + (Math.random() * 60));
// format the display however you wish.
function display(diff) {
var minutes = (diff / 60) | 0,
seconds = (diff % 60) | 0;
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
element.innerHTML = minutes + ":" + seconds;
}
startTimer(futureTimeStamp, display);
};
<span id="time"></span>
Also Math.round(new Date().getTime()/1000.0); will give you the number of seconds since the epoch, however it may be a little disingenuous to round the number. I think you would be better served by taking the floor:
var timestamp = Math.floor(Date.now() / 1000)); is probably a better option.
In addition I am not sure why you need the start time, current time and end time. In order to find the remaining number of second you just need to know when the timer should end and the current time.
I'm trying to compare to 2 dates by hour/minutes/seconds, in order to make a script to resume a script when closed. If current time is pass closed time + interval ( currently set at 30 minutes) should execute and run the script normally, if not wait till difference timeouts to execute.
Current hour/minutes/seconds is not a must but the result should be in ms interval
Example:
interval = (30 * 60 * 1000)
close time = 15:10:53
current time = 15:15:29
close time + interval = 15:40:53
first time I check if `current time` <= `close time + interval`
then calculate `difference`
`difference` = (close time + interval = 15:40:53) - (current time = 15:15:29)
Result should be setTimeout(function(){ alert("Hello"); }, time difference);
The only way I'm thinking of doing this is calculate each difference from Hour,Minutes,Seconds and then finding out the ms for setTimeout
I tried but results were weird, not something that would count as smaller than 30min
var ONE_S = 1000 ;
var timeDiff = Math.abs(closeTime - currentTime);
var diffS = Math.round(timeDiff/ONE_S)
Use Date objects and compare timestamps like so:
var interval = 30 * 60 * 1000;
var closeTime = new Date('Wed Nov 26 2015 10:17:44 GMT-0400 (AST)');
var currentTime = new Date;
var difference = (closeTime - currentTime) + interval;
if(difference < 0) {
console.log('time has expired');
}else{
setTimeout(someFunction, difference);
}
closeTime - currentTime gets the time between timestamps in ms, which will be negative if it's past closing time. We offset closing time by 30 minutes (by adding interval). Then we just have to check if difference < 0 to know if time has expired, and if not we can wait difference milliseconds to trigger someFunction