IE Failing to evaluate second if condition (&&) - javascript

So I have this script:
function makeActive() {
var element, name, arr;
element = document.getElementById("liveChat");
name = "active";
arr = element.className.split(" ");
if (arr.indexOf(name) == -1) {
element.className += " " + name;
}
}
var currentTime = new Date();
var currentTimeFormatted = currentTime.toLocaleTimeString();
if(currentTimeFormatted >= '08:00:00' && currentTimeFormatted <= '16:30:00'){
makeActive();
}
Which works perfectly in Chrome, however in IE the class doesn't get added.
If I remove the
&& currentTimeFormatted <= '16:30:00'
IE also adds the class. Why would adding a second condition, break this script within IE?

To make this a tad easier than having to use && and || mix, or if your values are stored somewhere in a static file etc. You could create a kind of pseudo time, by multiply each section.
eg.
const cTime = new Date();
const ptime =
cTime.getHours() * 10000 +
cTime.getMinutes() * 100 +
cTime.getSeconds();
if (ptime >= 80000 && ptime <= 163000) {
console.log("Active");
} else {
console.log("InActive");
}

You are doing string comparisons, which means that the browser and locale dependent output of toLocaleTimeString() screws your code in IE, and possibly also in other browsers or regions, because this function is solely intended for producing a human-readable time representation.
So you should either:
(1) Use a string representation that is standardized, e.g. invoking toISOString(). This will also get rid of time zone problems, because the result will always be in UTC time:
var currentTimeFormatted = new Date().toISOString(); // 2018-11-07T12:28:12.448Z'
currentTimeFormatted = currentTimeFormatted.substr(currentTimeFormatted.indexOf('T') + 1, 8); // 12:27:12
Now the rest of your code will work (assuming you 08:00:00 and 16:30:00 are UTC times).
(2) Extract the hour and minute parts of the new Date() and compare those to integers:
var currentTime = new Date();
if(currentTime.getHours() >= 8
&& // similarly a comparison to < 16:30
) {
makeActive();
}
(3) Use the great solution by Keith (see below), which I think is the best way to go

IE's implementation of date.toLocaleTimeString() adds non-printable characters into the string. The easiest way to deal with them is to trim them from the string;
currentTimeFormatted = currentTime.toLocaleTimeString().replace(/[^ -~]/g,'')
When dealing with localized timezones and timezone comparison, it might be worth trying a library like moment.js which can also deal with comparing values using the isBetween funciton
Edit
As the other solutions have suggested - using toLocaleTimeString() is not a safe method of performing date comparison and should be avoided.

Related

Google Apps script - IN operator for minutes

I try to run a sequential code. First update should happen every 15 minutes, the second one always 3 minutes after the first one.
The code below runs every minute.
However, it never works in Google Apps Script. Any idea how to fix it?
I only came up with writing out the in clause to == and or. It does not look straight.
function update_per_1_min() {
var d = new Date();
var m = d.getMinutes();
var m = 16
if(m in [1.0,16.0,31.0,46.0]){
update_0()
} else if (m in [4,19,34,49]) {
update_1()
}
}
in checks whether the expression is a property of the object. Arrays have properties like 0 for the first index, 1 for the second index, etc:
const arr = ['foo', 'bar'];
console.log('foo' in arr);
console.log('0' in arr);
Properties are not the same thing as values, of course. It looks like you're trying to check whether the value is contained in the array, in which case you could use .includes (if you were able to use ES6), or indexOf for GAS:
function update_per_1_min() {
var d = new Date();
var m = d.getMinutes();
if([1,16,31,46].indexOf(m) !== -1){
update_0()
} else if ([4,19,34,49].indexOf(m) !== -1) {
update_1()
}
}
(note that trailing zeros after the . in numbers is meaningless - feel free to leave those out entirely)

Javascript use split on date range

I get a date range from some API (not in my control) which has the following possibilities of date format
dd-Mmm-yy
dd-Mmm-yyyy
dd/mm/yy
dd/mm/yyyy
mm/dd/yy
mm/dd/yyyy
yyyy-mm-dd
So I get the date range as
01-Dec-16-06-Dec-16 (as per dd-Mmm-yy) OR
01/12/16-06/12/16 (as per dd/mm/yy)
So hyphen (-) is the from/to date separator which the API uses (not in my control) & I get this single combined value
Now, in my code, I want to get the from & to dates separately.
So I use
range.split("-")
However, this does not work properly for 01-Dec-16-06-Dec-16
Not sure what is the best way to account for all the possibilities (considering that the from/to date separator would always be -)
Since this is a case of an ugly API, the only way to do this is by using "hacky" solutions.
Use #Rory McCrossan's suggestion: count the number of - in the string. If 1, split. If 5, split by the third.
Since the API uses the same format of date for both the left side and the right side, the total length of the string will always be ("left side" + "-" + "right side"). You can split the text on the middle character.
e.g.
let date = "01-Dec-16-06-Dec-16";
let idx = date.length / 2;
let start = date.substr(0, idx);
let end = date.substr(idx + 1);
Use regex.
From the formats provided, it looks like the from and to dates will always be the same length split by -. In that case, just do this:
var len = (yourDateRange.length - 1) / 2
var dateFrom = yourDateRange.substring(0, len)
var dateTo = yourDateRange.substring(len + 1)
If you have any format where the length is variable (Such as full name for month), this obviously won't work
It's a bit hacky, but gets the job done.
I used a chained indexOf call with the previous call as the fromIndex parameter (which is the 2nd parameter of indexOf). And seeing as there is either / in the string (then split by -) or not (then split by 3rd -), there was no need for any special checks.
function splitDates(date) {
// ~ is just a fancy way to turn indexOf into a
// boolean-equivalent check (-1 is 0, 0 is 1, etc)
if (~date.indexOf('/')) {
return date.split('-');
} else {
var idx = date.indexOf('-', 1 + date.indexOf('-', 1 + date.indexOf('-')));
return [date.slice(0, idx), date.slice(idx + 1)];
}
}
var dates = ['01-Dec-16-06-Dec-16', '01/12/16-06/12/16'];
dates.forEach((date) => {
alert(splitDates(date))
});

Javascript timer bug in Opera

this is a simple puzzle game using html 5 drag and drop to move spans to their correct spot. A timer starts on the first drag (function drag(e)), and stops when there are no more spans left in the reserve (not shown).
(side question : is there a standard way of beautifying the (m)m : ss timer output I want, or do I have to go on as I am?)
Why does the timer() function work perfectly in chrome and firefox, and yet the seconds reset in Opera at 8 seconds? If I don't try to beautify the seconds and use the commented out line instead, it works perfectly.
Best regards!
var timerOn = false;
function drag(e) {
if (timerOn == false) {
timerOn = window.setInterval(function(){ timer() }, 1000);
}
...
}
function timer() {
var content = document.getElementById("timer").textContent.split(":");
if (parseInt(content[1]) == 59) {
content[0] = (parseInt(content[0]) + 1).toString();
content[1] = "00";
}
else {
var s = parseInt(content[1]) + 1;
content[1] = (s < 10 ? "0" : "") + s.toString();
//~ content[1] = s.toString();
}
document.getElementById("timer").textContent = content[0] + ":" + content[1];
}
....
<span id="timer">0:00</span>
Because some browsers extend JavaScript's parseInt to treat the prefix 0 to mean "octal", and 08 is an invalid octal number.
In the various places you use parseInt, give it its second argument (the radix — e.g., number base — to use), e.g. parseInt(str, 10). (This is a good idea generally, for this very reason.)
I'm surprised that you're still finding this behavior in an up-to-date browser, though, as the ECMAScript5 specification released three and a half years ago explicitly forbids extending parseInt in that way, as noted in Annex E - Additions and Changes in the 5th Edition that Introduce Incompatibilities with the 3rd Edition:
15.1.2.2: The specification of the function parseInt no longer allows implementations to treat Strings beginning with a 0 character as octal values.
...and I don't think §B.1.1 - Additional Syntax - Numeric Literals applies to parseInt.

Javascript:Dates comparison

<script>
dfrom = datefrom.split("/");
dto = dateto.split("/");
//Checking Year Part;
if(parseInt(dfrom[2]) > parseInt(dto[2])){
alert("DateFrom Cannot greater than DateTo");
return false;
}
if((parseInt(dfrom[1]) > parseInt(dto[1])) && parseInt(dfrom[2]) == parseInt(dto[2])){
alert("DateFrom Cannot greater than DateTo");
return false;
}
if(parseInt(dfrom[0]) > parseInt(dto[0]) && (parseInt(dfrom[1]) == parseInt(dto[1])) && parseInt(dfrom[2]) == parseInt(dto[2])){
alert("DateFrom Cannot greater than DateTo");
return false;
}
</script>
This is my script code to compare dates and is working fine but when I check 07/04/2013 and 08/04/2013, it shows "DateFrom Cannot greater than DateTo" and only these dates are showing wrong result. Is any error in my script or something else?
Any help would be highly appreciable.
try this
dfrom = datefrom.split("/");
dto = dateto.split("/");
var x=new Date();
x.setFullYear(dfrom [2],dfrom [1]-1,dfrom [0]);
var y=new Date();
y.setFullYear(dto [2],dto [1]-1,dto [0]);
if (x>y)
{
alert("X is big ");
}
else
{
alert("Y is big");
}
see here
When interpreting the parseInt function's arguments, older browsers will use the octal radix (base-8 numbering system) as default when the string begins with "0" (e.g., '07', '08'). As of ECMAScript 5, the default is the decimal radix (10) (i.e., this is tricky, but at least now it is depreciated).
In other words, there is a chance that if you pass strings ("01") or numbers (01) that begin with 0 to parseInt without specifying the second parameter (radix, which means what numbering system), they will be interpreted as having radix 8. This means 07 === 7 and 08 probably has undefined behavior (0, "", undefined, who knows?).
To be safe, always set your radix in the second parameter to parseInt when dealing with dates (I know I do), for example parseInt(x, 10) for regular base 10.
By the way, leading numbers with 0 indicates the octal radix other languages, so it is a good to get rid of them when converting strings to numbers.
Good luck!
The easiest way to compare date strings is to turn them into date objects and compare those, so if your date strings are in the format d/m/y. you can do:
// s in format d/m/y
// e.g. 15/3/2013 or 15/03/2013
function toDate(s) {
var s = s.split('/');
return new Date(s[2], --s[1], s[0]);
}
var d0 = '3/3/2013';
var d1 = '15/3/2013';
// Compare dates
alert( toDate(d0) < toDate(d1) ); // true
alert( toDate(d1) < toDate(d0) ); // false
When used in a comparison or arithmetic operation, Date objects are coerced to a number that is their time value.

Converting strings to ints and ignoring colons

I want to convert two strings to ints to be able to compare them.
The strings are timers so I basically want to convert as below:
timer1 = 00:00:14 // 14
timer2 = 00:00:25 // 25
Step one is to remove the colons:
var str1 = "00:00:14";
str1 = str1.replace (/:/g, "");
Step two is to take the remaining number and turn it into a number. parseInt will return 0 for a string like "000014", so you can use parseFloat:
var result = parseFloat(str1);
Good luck!
UPDATE: I didn't consider the base 10 problem... this would work if you simply wanted to compare which time was greater, but to do a more "proper" comparison of real times you might want to convert both to formal Date objects first.
The below snippet removes all semicolons by means of a regular expression matching all occurences of : and converts it to a number using parseInt. If the string timer1 was invalid, NaN will be the result.
numeric_timer1 = parseInt(timer1.replace(/:/g, ""), 10)
This fulfills your literal request for "converting strings to ints and ignoring colons".
If you're interested in converting hour:minute:seconds to seconds, split the string up with the timer1.split(":") method and apply maths.
You can do something like this:
var s = '00:01:05';
var seg = s.split(':');
var hours = parseInt(seg[0]);
var minutes = parseInt(seg[1]);
var seconds = parseInt(seg[2]);
var total = (hours * 3600) + (minutes * 60) + seconds;
window.alert(total);
Try using regexps and implicit string->number conversion (e.g. "7"*1 = 7)
function timeToSeconds(s)
{
var m = s.match(/(\d{2}):(\d{2}):(\d{2})/);
/* capture time fields in HH:MM:SS format */
if (m == null)
return null;
else
return m[1]*3600 + m[2]*60 + m[3]*1;
}
You can compare those strings as strings and things should work just fine, if those segments really are always 2-digit segments.

Categories