Search code examples
javascriptdatedate-arithmetic

Adding dates by week produces wrong date. Why is this happening?


I made a small project with educational purposes (for myself). The project consists on calculating the number of weeks between dates or the start or final date depending on a given number of weeks. At any point, the user must input two of these values:

  • Start date
  • End date
  • Number of weeks

At the beggining it worked, however after some tests I noticed it was throwing incorrect values. For example, for a start date 3/22/2018 (march the 22nd, 2018) and 28 weeks it would throw and end date 11/03/2018 (november the 3rd, 2018) which is wrong, but, if I calculate the number of weeks for a start date 3/22/2018 (march the 22nd, 2018) and an end date 10/05/2018 (october the 5th, 2018) it throws 28 weeks which is correct. Further inspection shows it only happens for dates starting from 04/01/2018 (april the 1st, 2018).

After some research I found out doing date calculations in miliseconds is better as some browsers do not support some date functions.

Nevertheless I would like to know if there is an explanation on this behaviour.

Thanks in advance.

<script>
    function calcDates(){
        var startInput = document.getElementById("start");
        var endInput = document.getElementById("end");
        var weeksInput = document.getElementById("weeks");
        var startValidation = false;
        var endValidation = false;
        var weeksValidation = false;

        if (startInput.value != "") {
            startValidation = true;
        }
        else{
            startValidation = false;
        }


        if (endInput.value != "") {
            endValidation = true;
        }
        else{
            endValidation = false;
        }

        if (weeksInput.value != "") {
            weeksValidation = true;
        }
        else{
            weeksValidation = false;
        }

        //Calculate weeks number
        if ((startValidation == true && endValidation == true && weeksValidation == false)||document.getElementById("weekRadio").checked == true) {
            document.getElementById("weekRadio").checked = true;
            var start = new Date(startInput.value);
            var end = new Date(endInput.value);
            weeksInput.value = Math.round((end-start)/604800000);
        }

        //Calculate End date
        if ((startValidation == true && endValidation == false && weeksValidation == true)||document.getElementById("endRadio").checked == true) {
            document.getElementById("endRadio").checked = true;
            var start = new Date(startInput.value);
            var weeks = weeksInput.value;
            var end = new Date();
            end.setDate(start.getDate() + (weeks*7));
            console.log(start.getDate());
            console.log(weeks*7);
            console.log(start.getDate()+(weeks*7));
            console.log(end.toISOString().substring(0,10));
            endInput.value = end.toISOString().substring(0,10);
        }

        //Calculate Start date
        if ((startValidation == false && endValidation == true && weeksValidation == true)||document.getElementById("startRadio").checked == true) {
            document.getElementById("startRadio").checked = true;
            var end = new Date(endInput.value);
            var weeks = weeksInput.value;
            var start = new Date();
            start.setDate(end.getDate() - (weeks*7));
            console.log(-weeks*7);
            console.log(end.toISOString().substring(0,10));
            startInput.value = start.toISOString().substring(0,10);
        }
    }
</script>

Solution

  • Date.setDate "sets the day of the Date object relative to the beginning of the currently set month."

    In the following snippet of code, end's currently set month will start out being whatever month it happens to be at the time the code is executed. And setDate will set the new date value relative to the start of whatever month that is.

    var end = new Date();
    end.setDate(start.getDate() + (weeks*7));
    

    In order to get the expected results in this scenario, you should use this code snippet, which initializes end to the startInput.value time (which will include the correct month):

    var end = new Date(startInput.value);
    end.setDate(start.getDate() + (weeks*7));