0

I'm getting a bizarre problem when running one of the ASP pages I've created. I've tested the web page on few PCs and only a few of them are able to display the page properly. For majority of PCs I've tested it on, the page fully loads and is fully functional. For some people the page freezes and Chrome shows that it's waiting for cache or waiting for the server - all input fields are not clickable. On the other hand, the page works for them perfectly fine on Edge. There's one PC that the page doesn't work neither on Chrome nor Edge. Every PC has the same version of Windows 10, same version of Chrome and Edge.

After commenting out some code I have found out that there's one particular for loop that causes the crashes on some PCs. Basically, what I'm trying to do is to display some data for certain range of dates, if there's no data for a date within that date range, it would normally be missing, therefore, I'm adding it in the below for loop and later on assigning 0 value to that date so that I have a table with nice layout with continuous dates.

// Checking if there are no missing dates in the date range. This could happen if the date doesn't exist in the SQL database.
for (var i = 0; i < dateArray.length - 1; i++) {
    // Checking if the next date is only incremented by 1
    var currentDate = GetDate(dateArray[i]);
    var nextDate = GetDate(dateArray[i + 1]);
    var shouldBeNextDate = GetNextDayDate(currentDate);

    // Adding the date that is missing. If the date won't be found later the quantities will be set to 0.
    if (convertDateToString(shouldBeNextDate) != convertDateToString(nextDate)) {
        dateArray.splice(i + 1, 0, convertDateToString(shouldBeNextDate));
    }
}

// Returns Date() from string
function GetDate(date){
    var numbers = date.match(/\d+/g);
    return new Date(numbers[2], numbers[1] - 1, numbers[0]);
}

// Returns the next day of the Date() passed as an argument
function GetNextDayDate(date){
    var numbers = convertDateToString(date).match(/\d+/g);
    return new Date(numbers[2], numbers[1] - 1, parseInt(numbers[0]) + 1);
}

// Returns Date() converted to string 
function convertDateToString(strDate){
    // Setting to MM/dd/YYYY format
    strDate = new Date(strDate).toLocaleDateString();
    var firstDash = strDate.indexOf("/");
    var secondDash = strDate.indexOf("/", firstDash + 1);
    var tempMM = strDate.substr(0, firstDash);
    var tempDD = strDate.substr(firstDash + 1, secondDash - firstDash - 1);
    var tempYYYY = strDate.substr(strDate.length - 4, 4);

    return tempDD + "/" + tempMM + "/" + tempYYYY;
}

When the above for loop was commented out the page loaded successfully for everyone. Any ideas what could be causing the issues? Is there something straightforward I'm missing?

Mosè Raguzzini
  • 15,399
  • 1
  • 31
  • 43
Dawid Kubiak
  • 57
  • 1
  • 7
  • Note that in your edit you added a pair of braces, which could modify how the code behaves. I think you pulled these over from another [rejected edit](https://stackoverflow.com/review/suggested-edits/24438430) by mistake, so you might want to check that. – CalvT Oct 29 '19 at 16:25
  • @CalvT - I did add braces in my edit, but in this specific example the braces do not change the behavior in the original code. They did cause confusion for me initially though, when combined with incorrect indentation because I thought the braces were matched wrong. – Andrew Oct 29 '19 at 16:47
  • @Andrew ok, I don't know enough to be able to pass judgement on that myself. However, for the future, general practice is to leave code edits to the OP, as unintentional edits can change how it works. A comment suggesting how the OP could improve is probably the best thing. I know your intentions were good though, so thank you for contributing :) – CalvT Oct 29 '19 at 16:56
  • The *convertDateToString* function uses a poor strategy for formatting a date. The result of *toLocaleDateString* is implementation dependent and may depend on system settings. Far better to use *getFullYear*, *getMonth*, etc. and format the string yourself. See [*How to format a JavaScript date*](https://stackoverflow.com/questions/3552461/how-to-format-a-javascript-date). – RobG Oct 29 '19 at 22:17

1 Answers1

2

The problem is with this code:

strDate = new Date(strDate).toLocaleDateString();

Although you state in comments that this will give a string representation in MM/dd/YYYY format, this is not guaranteed. As documented on mdn (italics mine):

The toLocaleDateString() method returns a string with a language sensitive representation of the date portion of this date.

For instance, when I type the following in my browser's console:

new Date().toLocaleDateString()

...then in FireFox I get:

"10/29/2019"

which is what you expect, but in Chrome I get:

"29-10-2019"

Apparently, there is some confusion of what my locale really is. The important thing however is that it is wrong to assume any format (unless you pass arguments to toLocaleDateString that make it independent of your current locale).

So imagine you pass the Date object 10/29/2019 to convertDateToString: it will in this latter case not even find any slashes in the string returned by .toLocaleDateString, and so the string it returns itself will be "//2019". When GetNextDayDate tries to make a Date object out of that, it returns an invalid Date.

Consequently, the if condition in your loop will always be true, and a new entry will be spliced into your array, making the length of your array greater, and so the loop never finishes.

The solution is to only perform date-to-string conversion when really necessary, and when you do, to use the Date methods to extract the date parts, not toLocaleDateString.

Here is the corrected code:

for (var i = 0; i < dateArray.length - 1; i++) {
    var currentDate = GetDate(dateArray[i]);
    var nextDate = GetDate(dateArray[i + 1]);
    var shouldBeNextDate = GetNextDayDate(currentDate);

    // No need to convert to String
    if (shouldBeNextDate < nextDate) {
        dateArray.splice(i + 1, 0, convertDateToString(shouldBeNextDate));
    }
}
console.log(dateArray);

function GetDate(date){
    var numbers = date.match(/\d+/g);
    return new Date(numbers[2], numbers[1] - 1, numbers[0]);
}

function GetNextDayDate(date) { // Don't convert to string here.
    date = new Date(date); // clone the date
    date.setDate(date.getDate() + 1); // add one day to it
    return date;
}

function convertDateToString(date) {
    // Get the date parts directly
    let tempMM = date.getMonth() + 1;
    let tempDD = date.getDate();
    let tempYYYY = date.getFullYear();

    // Pad with zeroes where needed
    return (tempDD + "/" + tempMM + "/" + tempYYYY).replace(/\b\d\b/g, "0$&");
}
trincot
  • 317,000
  • 35
  • 244
  • 286
  • 1
    To me, the use of "locale" in *toLocaleString* is a misnomer that sews confusion. It's based on a language code, it really has nothing to do with where a user is located or the date format they prefer or typically use. Hence I think any use of *toLocaleString* for dates is fraught as it assumes a language code is sufficient to determine the date format a user typically uses, and the results are not necessarily consistent. (I think that was a longwinded way of explaining my upvote). – RobG Oct 29 '19 at 22:46