I am trying to make a JavaScript countdown timer to UTC midnight that works in a browser (which typically converts to local time).
It seems to work most of the time, but for some reason it will go negative sometimes. I want it to always display time until midnight UTC.
-3 hours 23 minutes 34 seconds
I'm pretty sure I tested this on crossing midnight, but lately it's been going negative like it isn't pulling a new date.
Here is what I am using now on my website.
setInterval(() => {
let toDate = new Date()
let tomorrow = new Date()
tomorrow.setHours(24, 0, 0, 0)
let diffMS =
tomorrow.getTime() / 1000 -
toDate.getTime() / 1000 -
toDate.getTimezoneOffset() * 60
let diffHr = Math.floor(diffMS / 3600)
diffMS = diffMS - diffHr * 3600
let diffMi = Math.floor(diffMS / 60)
diffMS = diffMS - diffMi * 60
let diffS = Math.floor(diffMS)
let result = diffHr + ' hours '
result += diffMi + ' minutes '
result += diffS + ' seconds '
this.timeRemaining = result
}, 1000)
Not sure exactly where your issue is from, but using local time adjusted by offset is somewhat fraught. Also the offset changes over a DST boundary so that might be an issue too (or not) so the countdown will jump the equivalent of the change in offset (either + or -).
It's very much simpler to set the end using UTC methods and count down to that, e.g.
// Return time to next UTC midnight as x hours x minutes x seconds
function toMidnightUTC(date = new Date()) {
let d = new Date(+date);
d.setUTCHours(24,0,0,0);
let diff = d - date;
return `${diff/3.6e6 |0} hours ` +
`${diff%3.6e6 / 6e4 |0} minutes ` +
`${diff%6e4 / 1000 |0} seconds`;
}
// Run 20ms after next full second
let runIt = ()=> {
document.getElementById('s0').textContent = toMidnightUTC();
let lag = 1020 - new Date() % 6e4;
setTimeout(runIt, lag);
};
// Start the process…
runIt();
<span id="s0"></span>
Note that setInterval isn't a good way to do a countdown as the interval is not guaranteed to run every second and typically slowly drifts so it doesn't tick with the system clock and drifts, so skips a second from time to time.
Better to use setTimeout and get the time to the next full second each time so it runs very close to the system clock tick and rarely skips (though it still might if the system is working hard).
Related
I have a problem, where I try to find the number of hours and minutes after I had last collected the item. For example, if I had collected the item 1 minute ago, the code should be able to print out the remaining time left to collect the item again(23 h and 59 m) in this format. Till now, I have worked out this much:
UserJSON is a JSON file with the user's name and the time of their last claim
let deltaHour = Math.floor(new Date().getTime() - UserJSON[message.author.id].lastclaim) / (60 * 60 * 24)
let deltaRealHour = Math.floor(deltaHour)
let deltaMin = ((deltaHour % 1) / 100) * 60
Please help me out.
Here is a possible solution.
You can use the function I wrote before (timeUntilNextClaim()), supply it with the last time that your user claimed the item (in epoch seconds) and it will return to you the delta milliseconds until the item can be claimed.
You can then use the second funciton I wrote for you (toHrsAndMinutes) to turn milliseconds into hours and minutes (rounded down of course).
Please let me know if you have any questions or would like any more help with this. I'd be happy to help.
function timeUntilNextClaim(LastClaim) {
/* this function takes an epoch time of the
last time the user claimed the item as an argument and
returns (in ms) the time until they can pick up the
item again */
let now = new Date();
// add 24 hours to last claim time to calculate next claim time
let timeTilClaim = (LastClaim + 8.64e7) - now;
if (timeTilClaim <= 0) {
return 0;
} else {
return timeTilClaim;
}
}
function toHrsAndMinutes(duration) {
// this function will return an array of [hours, minutes] of a duration specified in ms
var hours, minutes;
hours = Math.floor(duration / 3.6e6);
minutes = Math.floor((duration % 3.6e6) / 60000);
return [hours, minutes];
}
function test() {
// tests / Example Usage
let usrLastClaimTime = new Date() - 20160000; // lets pretend they claimed it last 5.6 hours ago
let now = new Date();
let msTilNextClaim = timeUntilNextClaim(usrLastClaimTime);
let hoursTilClaim, minsTilClaim;
[hoursTilClaim, minsTilClaim] = toHrsAndMinutes(msTilNextClaim);
console.log(`The current time is ${now}`);
console.log(`The item was last claimed at ${Date(usrLastClaimTime)}`);
console.log(`You can claim again in ${hoursTilClaim} Hours and ${minsTilClaim} Minutes`);
}
test();
getTime() is milliseconds since Jan 1, 1970, 00:00:00.000 GMT
let lastclaim = 1632196398605;
let delta = (new Date() - new Date(lastclaim)) / (60 * 1000 * 60);
let deltaHrs = Math.floor(delta);
let deltaMins = Math.floor((delta % 1) * 60);
console.log(`deltaHrs:`, deltaHrs);
console.log(`deltaMins:`, deltaMins);
does any one know this could be achieved in react native, I am working on an location based app that requires showing ETA to the customer. However, I do not want to show just 25mins away, I would like to implement 'arriving 'Arriving Today, 9:45 AM. which implies the current time is 9:20 and the ETA is added to the current time.
something like the image below
You can achieve it using the below function:
getETA(minutesLeftInArrival) {
let arrivalTimeLeft = minutesLeftInArrival * 60 * 1000; // convert to unix timestamp
let currentTime = Date.now(); //TIME RIGHT NOW
let timeToArrive = currentTime + arrivalTimeLeft; // TIME OF ARRIVAL
let time = new Date(timeToArrive).toTimeString(); // TIME in 24 hour format
time = time.split(" ")[0]; // Remove GMT+...... from time
time = time.replace(/:\d\d([ ap]|$)/,'$1'); // remove seconds from time
let H = +time.substr(0, 2);
let hour = H % 12 || 12;
let ampm = (H < 12 || H === 24) ? "AM" : "PM"; // Return AM or PM depedning of 24 Hour time
time = hour + time.substr(2, 3) + " " + ampm;
return time; // RETURNS 12 HOUR TIME
}
https://snack.expo.io/#ammarahmed/getetatime
So the code below is the code that I was able to work with so far; however, doesn't do exactly what I need it to do.
I want to be able to call a function (aka: sundayDelta() ), however, I would love to be able to define the day of week and time of day I want the function to use in the countdown inside the calling of the function. I'm not sure if this is possible...but I was thinking something like this
sundayDelta(1,1000) which would turn into Day of Week Sunday and time of day: 1000 (10:00am). Not sure if something like this is even possible; however, I'm hoping it is. I plan on having multiple countdowns going on the same page just appearing at different times of day.
When the countdown finishes, I want it to refresh a div (doesn't matter what name the div has)
I would also love to be able to incorporate PHP server time into this that way everyone is seeing the correct countdown no matter where they are.
Any help would be great! Thanks for your input!
function plural(s, i) {
return i + ' ' + (i > 1 ? s + 's' : s);
}
function sundayDelta(offset) {
// offset is in hours, so convert to miliseconds
offset = offset ? offset * 20 * 20 * 1000 : 0;
var now = new Date(new Date().getTime() + offset);
var days = 7 - now.getDay() || 7;
var hours = 10 - now.getHours();
var minutes = now.getMinutes() - 00 ;
var seconds = now.getSeconds()- 00;
if (hours < 0){
days=days-1;
}
var positiveHours= -hours>0 ? 24-(-hours) : hours;
return [plural('day', days),
plural('hour', positiveHours),
plural('minute', minutes),
plural('second', seconds)].join(' ');
}
// Save reference to the DIV
$refresh = $('#refresh');
$refresh.text('This page will refresh in ' + sundayDelta());
// Update DIV contents every second
setInterval(function() {
$refresh.text('This page will refresh in ' + sundayDelta());
}, 1000);
When I make flexible text for intervals, I like to subtract out until nothing is left. That way you only show the non-0 values:
function sundayDelta(target_date) {
var now = new Date();
var offset = Math.floor((Date.parse(target_date) - now.valueOf()) / 1000);
var r = [];
if (offset >= 86400) {
var days = Math.floor(offset / 86400);
offset -= days * 86400;
r.push(plural('day', days));
}
if (offset >= 3600) {
var hours = Math.floor(offset / 3600);
offset -= hours * 3600;
r.push(plural('hour', hours));
}
if (offset >= 60) {
var minutes = Math.floor(offset / 60);
offset -= minutes * 60;
r.push(plural('minute', minutes));
}
if (offset != 0) {
r.push(plural('second', offset));
}
return r.join(' ');
}
In the PHP code, you can set variable this way. And we'll leave time zones out of it for now, but they could be added as well just by specifying them.
echo "target_date = '" . date('Y-m-d H:i:s', strotime('next Sunday 10am')) . "';\n";
I'm writing a Greasemonkey script where I want to know when it was last run. To do this, I wanted to store the current time with GM_setValue and compare that time to the time when the script is run again.
However, it seems that the Date().getTime() results won't pass to GM_setValue. For instance if you run:
var newtime = new Date().getTime();
GM_setValue('lastrun', newtime);
alert(GM_getValue('lastrun'));
There's obviously errors since the alert box won't pop up. However if you replace the first line with:
var newtime = 1;
you then get 1 returned in the alert box like you would expect.
That pretty much just isolates the date format causing the issue here. Any ideas on how to deal with this, or better ways to save dates between times a script has run?
I had a similar issue. The number is too large to be stored as a firefox cookie value. My solution was to limit the precision to the second (divide by 1000, drop remainder) and subtract 43 years (1356998400 seconds) to bring the date measured from to Midnight Jan 1st 2013 so my code looks like this:
var time = Math.floor((new Date().getTime() / 1000) - 1356998400);
GM_setValue("runTime", time);
and the retrieval looked like:
var time = GM_getValue("runTime", 0);
if (time != 0) {
var cur = Math.floor((new Date().getTime() / 1000) - 1356998400);
var cur = cur - time;
var sec = cur % 60;
var min = Math.floor(cur / 60);
console.log("Script took " + min + ":" + sec);
}
I trying to create a very simple time difference calculation. Just "endtime - starttime". I'm getting +1 hour though. I suspect it has with my timezone to do, since I'm GMT+1.
Regardless, that should not affect the difference, since both start and end times are in the same timezone.
Check my running example-code here:
http://jsfiddle.net/kaze72/Rm3f3/
$(document).ready(function() {
var tid1 = (new Date).getTime();
$("#tid").click(function() {
var nu = (new Date).getTime();
var diff = new Date(nu - tid1);
console.log(diff.getUTCHours() + ":" +
diff.getUTCMinutes() + ":" +
diff.getUTCSeconds());
console.log(diff.toLocaleTimeString());
});
})
You must understand what Date object represent and how it stores dates. Basically each Date is a thin wrapper around the number of milliseconds since 1970 (so called epoch time). By subtracting one date from another you don't get a date: you just get the number of milliseconds between the two.
That being said this line doesn't have much sense:
var diff = new Date(nu - tid1);
What you really need is:
var diffMillis = nu - tid1;
...and then simply extract seconds, minutes, etc.:
var seconds = Math.floor(diffMillis / 1000);
var secondsPart = seconds % 60;
var minutes = Math.floor(seconds / 60);
var minutesPart = minutes % 60;
var hoursPart = Math.floor(minutes / 60);
//...
console.log(hoursPart + ":" + minutesPart + ":" + secondsPart);
Working fiddle.