uvishere
uvishere

Reputation: 430

How do I add and subtract months from a date?

I am trying to add/subtract an integer to the element of array. my function is

var addToMonth = function (date, monthsToAdd) {
    var tempDate = date.split("-");
    tempDate[1] = parseInt(tempDate[1]) + monthsToAdd;
    tempDate[1] = 0 + tempDate[1];
    console.log("TempDate "+tempDate[1]);
    tempDate = tempDate.join("-");
    console.log("Added TempDate: "+tempDate);
    return tempDate;
}

The acceptable date string is: date = "2016-04-05". If call the function as addToMonth(date,1). The output is correct that is 2016-05-05. But when I call it as addToMonth(date, -1). It doesn't work. What is the correct way to do it?

Upvotes: 0

Views: 5014

Answers (2)

Yogi
Yogi

Reputation: 7184

Original 2016

The selected answer is actually incorrect as the question asks how to add months, not days. The correct solution is:

   function addToMonth( date, months ) {
        var d = new Date( date || new Date() );
        d.setMonth( d.getMonth() + (months || 0), d.getDate());
        return d;      
    }

Show and then run the code snippet to view demo.

I've also up voted the question ... because it does show effort and dates are tricky to work with even for experienced coders (especially when time zones are involved). That's why there are libraries like monment.js and others. For more information on the Date object see: MDN Date

function addToMonth( date, months ) {
    var d = new Date( date || new Date() );
    d.setMonth( d.getMonth() + (months || 0), d.getDate());
    return d;      
}


// Test Data
var html = '', iso, step = 1;


[
  '4/5/2016',
  '4/5/2016 01:00 GMT', 
  '2016-04-05', 
  '2016-04-05T00:00:00', 
  '2016-04-05T23:59:59',
  '2016-04-05T23:59:59Z',
  '2016', 
  '4/5/2016',
  undefined, 
  null, 
  0, 
  '',
  (new Date())

].forEach(function(v,i) {
  iso = addToMonth( v, -1).toISOString().split('T').shift();
  html += '<tr><td>' + v + '</td><td>-1</td><td>' + iso + '</td></tr>';
  iso = addToMonth( v, 1).toISOString().split('T').shift();
  html += '<tr><td>' + v + '</td><td>+1</td><td>' + iso + '</td></tr>';


});

stdout.innerHTML = '<table>' + html + '</table>';
table {
  border-collapse: collapse;
  background-color: white;
}
td {
  max-width: 10em;
  min-width: 4em;
  border: 1px lightgray solid;
  padding: 2px;
  overflow: hidden;
  white-space: nowrap;
  font-family: monospace;
}
tr:nth-child(odd) {
  background-color: aliceblue;
}
<div id="stdout"></div>

Update 2023

A commenter noted the edge case where the day of the month is greater than the target maximum. In this case the function uses the default Date.setMonth behavior, which can spill over into the following month, e.g., 31 Jan + 1 month = 3 Mar rather than 28 Feb. Since the question doesn't specify how to handle these cases it seems reasonable to use the expected behavior and not try to guess what OP wants.

The commenter also noted that getDate is redundant in

d.setMonth( d.getMonth() + (months || 0), d.getDate())

True, and it can be shortened to

d.setMonth( d.getMonth() + (months || 0))

Interestingly, this change didn't appear to make any difference in performance (Chrome 100k loops).

Alternative code that incorporates commenter suggestions:

function addToMonth( date, months ) {
  let d = new Date( date || new Date() ), n = d.getDate(); 
  d.setMonth( d.getMonth() + (months || 0));
  if (d.getDate() < n) d.setMonth( d.getMonth(), 0);
  return d;      
}

Upvotes: 4

void
void

Reputation: 36703

Yours is not a right approach:

  1. parseInt should always use base 10 if working with decimals.
  2. addToMonth("2016-31-03", 1) will give 2016-32-03. WRONG X.
  3. addToMonth("2016-01-03", -1) will give 2016-00-03. WRONG X.
  4. The way you are padding the 0 is not right. (Find out why.)

Use setDate and getDate functions on date instead.

var addToMonth = function (date, monthsToAdd) {
    var d = new Date(date);
    d.setDate(d.getDate()+monthsToAdd);
    return d;
}

Upvotes: 1

Related Questions