I'm trying to query stats for the hour which as the time 5 hours ago.
When I run this code, I see in the logs that the local time is 6 so I expect pastHour to be 1, but it says 3.
Why?
var HOURS_BACK = 5;
function main() {
var past = new Date(new Date().getTime() - HOURS_BACK * 3600 * 1000);
var pastHour = past.getHours();
var pastDateStr = getDateStringInTimeZone(past, 'yyyy-MM-dd');
query = "SELECT customer.id, metrics.impressions, segments.hour FROM customer WHERE metrics.impressions = 0 AND segments.hour = " + pastHour + " AND segments.date = '" + pastDateStr + "'";
Logger.log("query " + query);
}
edit — I'll leave the content below, but I now think that the mystery of the OP is not explainable by what's posted in the question.
It's important to keep in mind how Date values work. Internally, a Date is always a UTC-relative timestamp. That is, if (hypothetically) you and somebody across the world from you simultaneously called new Date().getTime(), you'd both get the same timestamp. (Obviously not really possible, but imagine you live in a cartoon world.)
However, the Date APIs like .getHours() work in your local time. The difference between local time and universal time causes lots of people confusion all the time, and things only get worse when your date/time values are subsequently involved in database operations, where the same problems may crop up once again.
Related
I have this webpage that takes in an input in seconds, and produces what the time will be after the time has elapsed. So I went about it using the following code
let duration = prompt("Enter your trip duration in seconds: "); // get the time from the vaue of the input element
let newtime = new Date(); // get the current time
newtime.setSeconds(newtime.getSeconds() + duration)
I decided to test the code and it gave me weird results and for some reason when I divided the duration by ten it started to work. Even though it worked I wasn't satisfied. So I added some more code to try and find where the bug is.
let oldtime = new Date();
oldtime.setSeconds(oldtime.getSeconds() + 60);
I added some code so that it would show on the webpage to compare the two times and these are my results when I enter 60 as the duration...
Just a little summary. The code works when I hard code the values in but it behaves weirdly if I try get the input from a prompt
You are concatenating a string to a number instead of adding. You have to parse the result from prompt to a number (with the unary plus operator):
let duration = prompt("Enter your trip duration in seconds: "); // get the time from the vaue of the input element
let newtime = new Date(); // get the current time
newtime.setSeconds(newtime.getSeconds() + +duration)
console.log(newtime)
I believe the issue is because you need to parse the duration from a string to number. So, try this
newtime.setSeconds(newtime.getSeconds() + parseInt(duration, 10))
You can use a library like Luxon to deal with dates and times without having to worry about unexpected behaviour like the one you described.
import { DateTime } from "luxon";
const now = DateTime.now();
const timeAfterTrip.plus({ seconds: duration });
console.log(timeAfterTrip);
I don't use time features in javascript often but need a countdown of 24 hours. I thought I had it figured out when it worked in Android, but unfortunately in iOS it is returning the Nan. I have read all the stack questions I could but still cannot devise a way to make it work ... (maybe I need more coffee)
The server time is formatted like this
2020-07-11 01:00:13
It is a timestamp type with a current timestamp default in mySQL
I bring it into the app via json. Here is the code I have that works in android. (The purpose is to countdown 24 hours then delete
var timecount2 = new Date(obj.chat_time);
var subtracttime = new Date("01 00:00:00"); // bascially 24 hours
var remainingcount = Math.abs(subtracttime-timecount2); // difference in milliseconds
var finishedcount = new Date(remainingcount, "HH:mm:ss");
var date = new Date(remainingcount);
//I break out each part so I can format it in a unique way
var d = addZero(date.getDay());
var h = addZero(date.getHours());
var m = addZero(date.getMinutes());
var s = addZero(date.getSeconds());
//add a little function to add a trailing zero if needed
function addZero(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
That works great in android (although again I don't often work with date and time in javascript) but fails in iOS
Any suggesstions?
I have a strange timezone/date formatting issue that recently came up with some new code, and what makes it more strange is that it only affects two months - August and September.
The code takes a date string with UTC time formatted like this:
10-06-2017 09:29:15
And converts it to a new string with the same format but with local time. The zeroPad function ensures that the format remains the same.
We implemented it in March and everything worked fine. It's within Classic ASP on IIS9/Server 2012.
As soon as we got to August, it broke. 08-10-2017 09:33:06 becomes 12-09-2016 20:33:06.
Can anyone see what I've done wrong?
function jsConvert(dateString) {
var patterns = dateString.split(/[\-\s:]/g);
var date = new Date(parseInt(patterns[2]),
parseInt(patterns[0]) - 1,
parseInt(patterns[1]),
parseInt(patterns[3]),
parseInt(patterns[4]),
parseInt(patterns[5]));
date.setTime(date.getTime() - getTimezoneOffset() * 60 * 1000);
var result = zeroPad(date.getMonth() + 1);
result += '-' + zeroPad(date.getDate());
result += '-' + date.getFullYear();
result += ' ' + zeroPad(date.getHours());
result += ':' + zeroPad(date.getMinutes());
result += ':' + zeroPad(date.getSeconds());
return result;
}
function zeroPad(number) {
return (number < 10) ? '0' + number : number;
}
What are the units of time in your getTimezoneOffset() function?
Your code is written as though the getTimezoneOffset() function returns a number of minutes, since you are multiplying by 60 and then 1000, to get millseconds.
But if your getTimezoneOffset is returning seconds, you will be over-doing the multiplication and therefore jumping back too far in time.
I think it would have to be milliseconds, to jump back the distance you are getting. #CBroe above mentions that perhaps you mean the builtin getTimezoneOffset function, which is indeed in minutes. Perhaps you have a separate getTimezoneOffset function defined in your code elsewhere, that returns an answer in milliseconds? In which case CBroe's answer fixes it.
My next suggestion would be to add lines of debugging code
For example, could you add the following?
At the beginning, add console.log("A",dateString).
After var patterns = dateString.split(/[\-\s:]/g); add a line console.log("B",patterns);.
After var date = ...(patterns[5])); add a line console.log("C",date);.
After date.setTime...1000); add a line console.log("D",date); console.log("E",getTimezoneOffset());.
If you show us the output of these lines, we should be able to pinpoint the problem easily. I have included item E because I am just wondering if there is yet another getTimezoneOffset() function in your system, which we are not aware of, or something. Seeing its value will help reassure everyone.
Meanwhile can you confirm the time zone you are running the code in? I am guessing it is in the USA rather than Europe, from your preference for putting month before the day?
So as it turns out this is a known, albeit obscure issue. It has to do with the fact that parseInt assumes that numbers with leading zeros are NOT base 10, but instead radix. It's well documented here: Javascript parseInt() with leading zeros
Once I made the change to:
parseInt(patterns[2]**, 10**);
All was good.
Thanks for the input.
I'm trying to decrement a variable once a day. I have written the following code for that.
var counter = 10; //any value
setInterval(function() {
counter = counter - 1;
}, 86400000);
Is there a better or efficient way to achieve the same thing ?
P.S : - I do not wish to use any libraries.
The only thing I see you miss is to set the initial value of counter variable.
I would write:
var counter = 1000; // or any useful value
setInterval(function() {
--counter;
}, 24 * 60 * 60 * 1000); // this is more self-explanatory than 86400000, and, being evaluated just once, it will have a tiny effect on the performace of the script
I don't see any problem in the way you write it. You use interval, ok, but this is not the worst evil you may do to set up the variable value.
You may think of another solution with a function which returns you the current counter.
var initialValue = 20000;
function getCounter() {
return initialValue - Math.floor(Date.now() / 1000 / 60 / 60 / 24);
}
console.log(getCounter());
The difference is that it takes the current day number starting from the UNIX time beginning. Every day the day number will be increased, so the result of the function will be decreased by 1.
But still I don't see how this solution can be better than yours.
I'm not totally sure why, but using setInterval like this makes me uncomfortable.
If I were to require this, I would use something like this approach:
var counter = 10;
var timeout = new Date();
setInterval(function(){
if(new Date() >= timeout)
{
--counter; // the action to perform
timeout = new Date(timeout.getTime() + 86400000); // update the timeout to the next time you want the action performed
}
console.log(counter);
},1000); // every second is probably way more frequent than necessary for this scenario but I think is a decent default in general
One thing that this allows is to, for example, set the next timeout to midnight of tomorrow rather than being locked in to "X seconds since the previous execution". The key is the inversion of control - the action itself can now dictate when it should next run.
Though I would probably abstract away the details behind an interface accepting a start, interval, and action.
The biggest problem in my eyes is that you have to keep this one JS process running consistently for days at a time to have it do what you need. The world is not so perfect that things don't need an occasional reboot...including the average JS process.
Personally I would store a timestamp of my starting point, then (whenever I need to know how much time has elapsed) grab a new timestamp and use it to calculate how many days it has been. That way even if something interrupts my process I can still be right where I started.
Maybe use window.localStorage to save the last time, and if it is greater than 60*60*24 (seconds in a day) set the last time to this morning/now/1:00 and then decrease the value and save it.
Example:
var d = new Date();
var mins = -(1+d.getHours())*60+d.getMinutes();
var secs = mins*60+d.getSeconds(); // total seconds passed today from 1:00
var now = d.getCurrentTime():
var lastCheck = localStorage.getItem("lastCheck");
if (!lastCheck)
{
localStorage.saveItem("lastCheck",now-secs); // beginning of today
}
var dayPassed = now - lastCheck > 24*60*60; // change to see if a day has passed
if (dayPassed)
{
// save seconds
localStorage.setItem("counter",localStorage.getItem("counter")-1);
localStorage.saveItem("lastCheck",now-secs); // beginning of today
}
It makes more sense to me to check how many days have passed since a specific date and decrement that number of days from the counter. Mostly just because I wouldn't expect anybody to leave the same page open without the need or want to reload for days on end. I would do something like this:
counter = 365; // original counter
var start = new Date(2016, 03, 20); // original date
var now = new Date();
var days = Math.floor(Math.abs(start.getTime()-now.getTime())/(24*60*60*1000))
counter -= days;
That way every time you visited the page, it would be decremented correctly. Note that this ignores any issues with leap days or time zones. The example above would have a counter of 360 for me. And then if you did expect it to be open for days, reload it automatically with:
self.setTimeout(function(){document.location.reload()}, 86400000);
I was just wondering if it is possible to have a javascript for loop that only iterates through the loop once a day i.e. when the date changes?
for(i=0; i < myArray.length; i++){
alert(myArray[i]);
}
So in the above loop, let it run, and freeze it or something only till the data changes, and the do another iteration, and just keep on doing that.. You know what I mean.
Thanks in advance!
Using localStorage is the best way to go when you don't have a server (because a user can change the computer's time and break your logic, and using a server it's harder to hack this)
Method below is more bulletproof:
// checks if one day has passed.
function hasOneDayPassed()
// get today's date. eg: "7/37/2007"
var date = new Date().toLocaleDateString();
// if there's a date in localstorage and it's equal to the above:
// inferring a day has yet to pass since both dates are equal.
if( localStorage.yourapp_date == date )
return false;
// this portion of logic occurs when a day has passed
localStorage.yourapp_date = date;
return true;
}
// some function which should run once a day
function runOncePerDay(){
if( !hasOneDayPassed() ) return false;
// your code below
alert('Good morning!');
}
runOncePerDay(); // run the code
runOncePerDay(); // does not run the code
If you want something to happen at predefined intervals, you can set a timeout/interval:
http://www.w3schools.com/js/js_timing.asp
For example:
var dayInMilliseconds = 1000 * 60 * 60 * 24;
setInterval(function() { alert("foo"); },dayInMilliseconds );
edit: since you mentioned that the code will be running in a browser, this assumes the browser is running for at least 24 hrs and will not work otherwise.
the best way to achieve it is by creating a cookie that lasts for1 day..
Even if after the refresh of the web page or browser gets closed that countdown will still continue..
setcookie($cookie_name, $cookie_value, time() + 86400, "/");
This means 86400 = 1 day
Hope it helps