Skip to content Skip to sidebar Skip to footer

What Is The Source Of This Off-by-one Error In A JavaScript Date?

I am trying to write a function that will take a string like 07/2020 and then return whether it is more than three months away. I have written a function isMoreThan3MonthsHence tha

Solution 1:

It looks like you're mixing the usage of Date.UTC and not when instantiating dates. For example, if you use the following for your validate function:

const validate = (str, 
                  [month, year] = str.split('/'), 
                  date = new Date(Date.UTC(+year, (+month)-1))) => 
    isMoreThan3MonthsHence({ utcYear: date.getUTCFullYear(), utcMonth: date.getUTCMonth() })

// Note: input is one-based months
console.log(validate('07/2020')) // Now true

It works as expected: JSFiddle

Removing the usage of Date.UTC altogether would perform the calculation in the user's local timezone, with any applicable daylight saving adjustment included. This could be seen as a valid approach, however would result in the behaviour you have described.


Note I've renamed the local prefixed variables based on feedback from Bergi. Using Date.UTC implies you're passing in UTC arguments.


Solution 2:

Other than mixing UTC and local dates, the way you're adding 3 months will cause an incorrect response for dates like 31 March, where adding 3 months simply by incrementing the month number results in a date for 1 July. See Adding months to a Date in JavaScript.

So validate('07,2020') will return false if run on 31 March.

To fix that, when adding months, check that the updated date is still on the same day in the month, otherwise it's rolled over so set it to the last day of the previous month.

function validate(s) {
  let testDate = addMonths(new Date(), 3);
  let [m, y] = s.split(/\D/);
  return testDate < new Date(y, m-1);
};

function addMonths(date, months) {
  let d = date.getDate();
  date.setMonth(date.getMonth() + +months);
  // If rolled over to next month, set to last day of previous month
  if (date.getDate() != d) {
    date.setDate(0);
  }
  return date;
}

// Sample
console.log('On ' + new Date().toDateString() + ':');
['07/2020', '04/2020'].forEach(
  s => console.log(s + ' - ' + validate(s))
);

Post a Comment for "What Is The Source Of This Off-by-one Error In A JavaScript Date?"