Hi I'm using React Native to create an Auctions App I have a Flat List for render each product in auction. On the Flat List component I've a interval of 1000 milliseconds for update the state on all the items the time left.
And for update the current time I use the following method:
updateTime(){
let currentTime = moment().unix();
let diffTime = this.state.product.endTime - currentTime;
let duration = moment.duration(diffTime * 1000, 'milliseconds');
return duration.hours() + ":" + duration.minutes() + ":" + duration.seconds();
}
I retrive the end time for each product from Firebase Firestore.
But for some reason that I do not understand, the time remaining in different devices varies between 2, 3 or even 4 seconds.
If you can tell me what I am doing wrong or what is the best way to do this, I would appreciate it.
moment() gets the time set on the device, so it's expected that your different devices are not set to the exact same time.
You can use a time elapsed interval instead to count down after fetching the product end time:
setInterval(() => this.setState(prevState => ({ timeElapsed: prevState.timeElapsed + 1 }), 1000);
...
const timeRemaining = this.state.product.endTime - this.state.timeElapsed
Related
I want to send some user specific data to an API on every quarter after a user logs in. like every 9:15 then 9:30 then 9:45 and so on..
I can use setInterval for every 15 min but I want it to be exactly at the quarter.
For Example - A user logs in at 9:25, so instead of hitting the API after 15 min(9:40) from then I want it to hit at 9:30 and then 9:45 and so on..
How can I achieve this?
you can simply check every minute if minutes % 15 == 0 and then hit your API
Use Date to calculate time to next 15, 30, 45, 60 minute clock,
Use setTimeout to set a timer to the calculated time,
Start a new interval using setInterval.
function onEveryQuarter(callbackFn) {
// function that starts an interval that calls `callbackFn()` every 15 minutes
const startQuarterInterval = () => setInterval(callbackFn, 60*1000*15); // in milliseconds
// get minutes to next :15, :30, :45, :60 time
const now = new Date();
const nextQuarterTime = Math.abs(now.getMinutes() % 15 - 15) * 1000; // *1000 to convert to milliseconds
// start timer
const quarterTimer = setTimeout(startQuarterInterval, nextQuarterTime);
}
And then,
// desired function
const myFunc = () => console.log("hello world");
// to execute `onEveryQuarter` as soon as user visits your site.
window.onload = (e) => onEveryQuarter(myFunc);
at top of your main.js file.
Im try to set an issued time and an expiry time for my jwt . I cant seem to get the dates right . Either they are too far off or not integers at all . I want to achieve this without using moment.js . This is what i got so far (which is also not correct).
const payload = {
// issued at time
iat: currentDate.getTime() / 1000,
// JWT expiration time (10 minute maximum)
exp: new Date(currentDate.getTime() + 10 * 60000).getTime() / 1000,
// GitHub App's identifier
};
JWTs want UNIX timestamps ... seconds since 1970-01-01T00:00Z.
You get those from Javascript timestamps by doing stuff like this:
const nowTime = Math.floor(Date.now() * 0.001)
Javascript timestamps are UNIX timestamps in milliseconds, not seconds.
And of course 10min is 600s. So you should be able to do this to get your start and expiry times.
const nowTime = Math.floor(currentDate.getTime() / 1000)
const payload = {
iat: nowTime,
exp: 600 + nowTime
}
No moment.js required.
I need to make request three times a day at a certain time (8 AM, 12 AM, 4 PM).
What is the best way to implement this?
This feels a bit like a strange requirement for a JS task and I think you should rather look into making a Cron task for that. But for the sake of example, here's how you can do it:
import { of, Observable, timer, EMPTY } from "rxjs";
import { map, filter, timestamp, switchMap } from "rxjs/operators";
type Hour = number;
type Minutes = number;
const atTimes = (times: [Hour, Minutes][]): Observable<[Hour, Minutes]> =>
timer(0, 1000 * 60).pipe(
switchMap(() => {
const date: Date = new Date();
const [currentHour, currentMinutes] = [
date.getHours(),
date.getMinutes()
];
const time = times.find(
([hour, minutes]) => hour === currentHour && minutes === currentMinutes
);
if (!time) {
return EMPTY;
}
return of(time);
})
);
atTimes([[11,48]]).subscribe(x => console.log(x));
Live demo: https://stackblitz.com/edit/rxjs-sxxe41
PS: I've assumed that the update doesn't need to be triggered as soon as the new minute starts. If that's the case, then timer should tick every second and you should use distinctUntilChanged on the current hour/minutes to wait until they're differents.
You should look into asyncScheduler from RxJS which uses setInterval for time based operations.
Refer to this link: https://rxjs-dev.firebaseapp.com/guide/scheduler
Ideally cron jobs are best on the server side to deal with functions need executing at different times, might be worth looking in that as well.
Is it possible to start setInterval counter clock dependent instead of load dependent?
Assuming that every computer has the minutes of the clock always at the same time.
If I open the page at 12:01 and I want a set interval of 2 minutes that always start from 12:00 (or any other hour but at :00 minutes). I want the event to fire at 12:02, 12:04 etc. no matter when I load the page or if it was open or not at 12:00.
The answer can be in jquery too.
You could take the delta to the next wanted slot and adjust with it the timeout.
function out(k, v) {
console.log(k.toString().padEnd(8), v.toString().padStart(15));
}
var time0 = new Date().getTime(),
slot = 2 * 60 * 1000,
time1 = Math.floor(time0 / slot + 1) * slot,
delta = time1 - time0;
out('slot', slot);
out('delta', delta);
out('time0', time0);
out('time1', time1);
setTimeout(function () {
var time2 = new Date;
out('time2', time2.getTime());
out('real', time2.getTime() - time0);
console.log(time2.toISOString());
}, delta);
Firstly I am very new to javascript. I have a Shopify store and I am planning to have a countdown timer just like Amazon to each of my product pages. I know there are a lot of plugins I can use for Shopify but none of them matches with the theme and style of my Shopify store so I planned to make one myself.
What I want is if User-1 opens my website and navigates to a product page, he should see a timer counting down to a specific time say 12:00:00 (hh:mm:ss). Suppose User-1 sees 'Deal ends in 11:20:10' Now if User-2 opens the same product page at the same time then he should also see 'Deal ends in 11:20:10' The whole point is the timer should not refresh back to 12:00:00 every time the browser loads/reloads the page and every user on the website should see the same time remaining on the countdown timer.
I starting with a bit of research and managed to run a timer on my store. It's not exactly what I want but here's the code:
var interval;
var minutes = 1;
var seconds = 5;
window.onload = function() {
countdown('countdown');
}
function countdown(element) {
interval = setInterval(function() {
var el = document.getElementById(element);
if(seconds == 0) {
if(minutes == 0) {
el.innerHTML = "countdown's over!";
clearInterval(interval);
return;
} else {
minutes--;
seconds = 60;
}
}
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
}, 1000);
}
</script>
And here's what it looks like:
It does work but has the following problems:
It refreshes with the browser refresh.
It doesn't have hours.
It does not auto-repeat when the timer reaches zero.
For every user time remaining varies.
As I mentioned I am almost a noob in JavaScript. Can anyone help me build a countdown timer that overcomes the above issues?
Explanation
Problem 1: It refreshes with the browser refresh.
Cause: Because you hardcoded the seconds and minutes (var seconds = 5, var minutes = 1), every user that visits the page will see the timer counting down from the exact same value being "1 minute and 5 seconds remaining" again and again.
Solution: Instead of hardcoding the start value of the countdown, hardcode the deadline. So instead of saying to each visitor of your page "Start counting down from 1 min and 5 sec to 0!", you have to say something like "Count down from whatever time it is now to midnight!". This way, the countdown will always be synced across different users of your website (assuming that their computer clock is set correctly).
Problem 2: It doesn't have hours.
Cause: Your code has only variables to keep track of seconds and minutes, there is no code written to keep track of the hours.
Solution: Like proposed in the solution for problem 1: don't keep track of the hours/minutes/seconds remaining, but only keep track of the deadline and then calculate hours/minutes/seconds remaining based on the current client time.
Problem 3: It does not auto-repeat when the timer reaches zero.
Cause: The nested ifs (that check seconds == 0 and m == 0) in your code explicitly state to display the text "countdown's over!" when the countdown is over.
Solution: Keep a conditional that checks when the countdown is over but instead of displaying "countdown's over!", reset the deadline to a next deadline.
Problem 4: For every user time remaining varies.
Cause: See Problem 1
Solution: See Problem 1
Sample code
here is a piece of code that integrates the solutions mentioned above:
const span = document.getElementById('countdown')
const deadline = new Date
deadline.setHours(0)
deadline.setMinutes(0)
deadline.setSeconds(0)
function displayRemainingTime() {
if (deadline < new Date) deadline.setDate(deadline.getDate() + 1)
const remainingTime = deadline - new Date
const extract = (maximum, factor) => Math.floor((remainingTime % maximum) / factor)
const seconds = extract( 60000, 1000 )
const minutes = extract( 3600000, 60000 )
const hours = extract(86400000, 3600000)
const string = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
span.innerText = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
}
window.setInterval(displayRemainingTime, 1000)
displayRemainingTime()
<h3>
<span id="countdown"></span>
</h3>
Edit: make sure the client time is correct
If you don't trust your clients for having their time set up correctly. You can also send a correct timestamp from the server that serves your page. Since I don't know what kind of server you are using I can't give an example for your server code. But basically you need to replace the code (from the following example) after const trustedTimestamp = (being (new Date).getTime()) with a correct timestamp that you generate on the server. For the correct formatting of this timestamp refer to Date.prototype.getTime()
const span = document.getElementById('countdown')
const trustedTimestamp = (new Date).getTime()
let timeDrift = trustedTimestamp - (new Date)
const now = () => new Date(timeDrift + (new Date).getTime())
const deadline = now()
deadline.setHours(0)
deadline.setMinutes(0)
deadline.setSeconds(0)
function displayRemainingTime() {
if (deadline < now()) deadline.setDate(deadline.getDate() + 1)
const remainingTime = deadline - now()
const extract = (maximum, factor) => Math.floor((remainingTime % maximum) / factor)
const seconds = extract( 60000, 1000 )
const minutes = extract( 3600000, 60000 )
const hours = extract(86400000, 3600000)
span.innerText = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
}
window.setInterval(displayRemainingTime, 1000)
displayRemainingTime()
<h3>
<span id="countdown"></span>
</h3>
Time from google server
To create a working example I added this experiment where I get the correct time from a Google page. Do not use this code on your website because it is not guaranteed that google will keep hosting this web-page forever.
const span = document.getElementById('countdown')
const trustedTimestamp = (new Date).getTime()
let timeDrift = 0
const now = () => new Date(timeDrift + (new Date).getTime())
const deadline = now()
deadline.setHours(0)
deadline.setMinutes(0)
deadline.setSeconds(0)
window.setInterval(displayRemainingTime, 1000)
window.setInterval(syncClock, 3000)
displayRemainingTime()
syncClock()
function displayRemainingTime() {
if (deadline < now()) deadline.setDate(deadline.getDate() + 1)
const remainingTime = deadline - now()
const extract = (maximum, factor) => Math.floor((remainingTime % maximum) / factor)
const seconds = extract( 60000, 1000 )
const minutes = extract( 3600000, 60000 )
const hours = extract(86400000, 3600000)
span.innerText = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
}
function syncClock() {
const xmlhttp = new XMLHttpRequest();
xmlhttp.open("HEAD", "http://www.googleapis.com",true);
xmlhttp.onreadystatechange = function() {
const referenceTime = new Date
if (xmlhttp.readyState==4) {
const correctTime = new Date(xmlhttp.getResponseHeader("Date"))
timeDrift = correctTime - referenceTime
}
}
xmlhttp.send(null);
}
<h3>
<span id="countdown"></span>
</h3>
I am not sure how Shopify handles plugins, but there must be some way to edit CSS for them, so you could have them match the style of your theme.
What you've done here, in the JS you've provided, will always have that behavior because it is client-side code. The script will run after each refresh, and will always refresh the values minutes and seconds to the associated initial values namely 1 and 5. I believe this answers your first question.
As per your second question, you clearly have not coded hours in that JS script. So hours should not appear, it would be a mystery if they did!
With respect to your third question, you only call countdown once during the onload function.
And by the first answer, it is natural that the clock should not be synchronized among users since they would logically be refreshing your page at different times.
So I think your best bet is to use one of the plugins and just modify the CSS.