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.
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);
Thanks for opening my question. What I'm doing, to me, should be very simple. I am a beginner for programming so I am not aware of what I need to get this done. I need help.
The problem:
I have to have 4 columns for times. (Travelto, Arrive, Depart, Travelfrom) I don't always use all of them so my script has to recognize that I want certain values based on which cells in a row are blank or which have content. I have tried using isblank() on the spreadsheet to determine a binary number which I then convert to a decimal. I'd like my script to do that so I don't have to add another column to my google sheets. I think I would use an array and then check if each element is blank in the array then multiply each element in that array by 1 so it's now a number instead of a boolean. Then I want to take the elements of the array and convert them into a single binary number and convert that to a decimal number to feed to my switch case, which will contain the correct way to calculate the hours and return the hours in decimal so it should be formated such as 1.75 for 1 hr 45 mins. The value it returns must be able to be summed so the function can't return a string. also I prefer 2 decimal places for the output.
I have attempted to figure out how to calculate the time in google's apps Script. I have had limited success. The output of my script is unintelligible as to how it got the answer it did. This is probably because I can't figure out how to tell what the script sees the times as. does it see 13:00:00, 0.5416667, or something completely different? I can't seem to figure it out.
I want to pass two values from a google sheets spreadsheet, which are visually formatted as time, then take those two times subtract one from the other and get the amount of time between them, the duration so that I know how many hours have been worked.
function worked(time1,time2) //pass 2 time values to function
{ //Start of the function
var time1; //declare time1 variable
var time2; //Declare time 2 variable
var outnumber = time1-time2; //Declare outnumber and subtract time1 from time2
return outnumber //return the difference of time1 and time2
}
here's the link to my sheet and code included in the editor. anyone with the link can comment
https://docs.google.com/spreadsheet/ccc?key=0Ar4A89ZoxmJCdHBFR0VCblVtWUVvR3hFbTdlcjdKNUE&usp=sharing
Please tell me what I'm doing wrong or not doing at all to make this work.
Thanks
Goldenvocals369
The number you are seeing outputted is the difference in ms. You need to convert ms to the format you want.
I found a neat way to do that here: https://coderwall.com/p/wkdefg
Your code would look like this.
function worked(time1,time2)
{
var time1;
var time2;
var outnumber = time1-time2;
return msToTime(outnumber)
}
function msToTime(duration) {
var milliseconds = parseInt((duration%1000)/100)
, seconds = parseInt((duration/1000)%60)
, minutes = parseInt((duration/(1000*60))%60)
, hours = parseInt((duration/(1000*60*60))%24);
hours = (hours < 10) ? "0" + hours : hours;
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;
return hours + ":" + minutes + ":" + seconds;
}
You can use this function. The function's documentation is:
Returns the displayed value of the top-left cell in the range. The
value will be of type String. The displayed value takes into account
date, time and currency formatting formatting, including formats
applied automatically by the spreadsheet's locale setting. Empty cells
will return an empty string.
I've encountered a strange result when comparing two MomentJS objects using max and min methods. They appear to return the wrong value.
For example, this code returns today rather than tomorrow:
moment().max(moment().add(1, 'd'))
http://jsfiddle.net/cGtbY/
Can anyone explain this behavior?
You misinterpret the meaning of min and max.
From the test suite (https://github.com/moment/moment/blob/develop/test/moment/min_max.js#L51):
equalMoment(test, now.max(future), now, "Now with the maximum of the future should be now");
The way to understand the meaning is: a.max(b) <= b (at latest, the result can be the second date).
The documentation has a clear quote:
Sometimes, server clocks are not quite in sync with client clocks. This ends up displaying humanized strings such as "in a few seconds" rather than "a few seconds ago". You can prevent that with moment#max()
The .max function is therefore the numerical minimum (selecting the earlier moment)
After looking in the source code of MomentJS 2.2.1, here's the source code of max():
max: function ( other ) {
other = moment.apply( null, arguments );
return other > this ? this : other;
},
Seems like they are returning this when other is later.. Weird..
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
I am in charge of a website, and I have set up a "Quote of the Day" which currently is quite simplistic. See Here (on the right of the page)
What it currently does is it gets the Day of the month and the month, and normalises to one, then multiplies by the number of quotes (stored in an xml file) and rounds down. While this method will give me the same quote whichever machine I am on (something a random number generator could never do) it has been pointed out to me that this method is flawed. If you consider January the first couple quotes are going to be the same, 1*1/360, 2*1/360, 3*1/360, thus the quote isn't unique.
Can anyone think of a better way to select a quote of the day?
Fun question. Instead of relying on days of the month, why not count days since a given date? JS provides a pretty good property for that: getTime(), which gives you the number of milliseconds since 12am UTC on Jan. 1 1970, which you can convert to days with some simple division.
The only thing that complicates it is that if you expect your quotes to shift at midnight (and who doesn't?), you have to take into account the timezone. Again, JS provides that with getTimezoneOffset(), which gives the number of minutes ahead or behind the user's locale is compared to UTC. If you want ALL users to flip at the same time, regardless of where they live, just set this to a static value.
Your code could look something like this:
var intQuoteCount = 51; // The number of quotes in your library
var dtNow = new Date();
var intTZOffset = dtNow.getTimezoneOffset() * 60000; // automatically adjust for user timezone
var intNow = dtNow.getTime() - intTZOffset;
var intDay = Math.floor(intNow / 86400000); // The number of 'local' days since Jan 1, 1970
var intQuoteToDisplay = intDay % intQuoteCount;
True, determinism is something "a random number generator could never do". Fortunately (for this case, at least), programming languages provide pseudo-random number generators, not the real thing. The pseudo-random numbers are generated by doing a bunch of calculations on a "seed" value.
To get a repeatable "random" selection, then, all you need to do is set the seed in a way which is consistent for each day - I would suggest using the date, in "yyyymmdd" format, as the seed, but any other number which will be unchanged over the course of a day will work just as well.
Once you have your seed, tell the PRNG to use it with the command srand(mySeed); and you'll get the same sequence of "random" numbers from rand() every time (until mySeed changes).
If you want to show the quotes in order, you could get the current Julian Day number, which will increase by one each day, and take the reminder after dividing it by the number of quotes as the number of today's quote. If you want to show all quotes but the order of them to change each cycle, you can xor the quote number and rearrange the bits using some logic that you get from the quotient of the division.
You could try rounding up on an even day and rounding down on an odd day. But I'm am sure there is better ways, this is just quick suggestion.
Also you could try using the current day of the year in the calculation as this is unique for each new day in the year as opposed to repeating each month.
Do you have to limit yourself to having a cycle of 360 days? If you have for example 500 quotes. some might never be used.
How about- Every day pick a random number between 1 and #OfQoutes, use it as the quote of day index, and mark it as "used in current cycle".
Next time when you pick a number, if you pick a quote that is marked as "used in current cycle" re-pick until you get a number of quote which isn't marked so. When all quotes are marked, un-mark all of them.
This will ensure you're going through all quotes in each cycle, together with randomness, and it will obviously work for any number of quotes.
<body onLoad="thoughts_authors()">
<script>
function thoughts_authors()
{
var authors=new Array()
authors[0] = "Charles Schulz";
authors[1] = "Jack Wagner";
authors[2] = "Mark Twain";
authors[3] = "Oscar Wilde";
authors[4] = "David Letterman";
authors[5] = "Lily Tomlin";
var thoughts=new Array()
thoughts[0] = "Good Day Is Today";
thoughts[1] = "Style Is What You Choose";
thoughts[2] = "Be The Best Version Of You.";
thoughts[3] = "Truth Along Triumphs.";
thoughts[4] = "How can Life Be Devastating When YOU Are Present in It.";
thoughts[5] = "Believe In What You Say";
index = Math.floor(Math.random() * thoughts.length);
alert(thoughts[index]+ "-" + authors[index]);
}
</script>
THIS WILL GENERATE RANDOM QUOTES ALONG WITH RANDOM AUTHORS