-2

I want to generate range of dates with monthly interval but got some issue with setMonth(). I can't best tell whats wrong but the dates cannot go beyond the 13th month. After the 13th month, it now iterates in yearly interval. It works well from 1 to 13 months. Any help. Thanks

<script type="text/javascript">
 var start = new Date('2016-12-10');
 var year  = start.getFullYear();
 var month  = start.getMonth();
 var day    = start.getDate();
 var p = 16;
 for (var i = 0; i < p; i++) {
   var d = new Date(start.setMonth(i));
   var year = d.getFullYear();
   var month = d.getMonth()+1;
   var day = d.getDate();
   if (month <= 9) {
     var dates = year+'-'+'0'+month+'-'+day;
   }else {
     var dates = year+'-'+month+'-'+day;
   }
   document.write(i+' '+dates+'<br>');
 }

</script>
ASM
  • 169
  • 3
  • 12
  • Why do you redeclare `year`, `month`, `day`, and `dates`? Do you know that JavaScript has function scope? – cdhowie Jan 09 '17 at 17:51
  • 1
    In programming index usually start with 0. But anyway even if it started with 1 what do you expect is the 13th month then?????? I mean we're talking Gregorian calendar right??? Simple Google search: http://www.w3schools.com/jsref/jsref_setmonth.asp – haffla Jan 09 '17 at 17:51
  • Shouldn't it go to the next year? – epascarello Jan 09 '17 at 17:55
  • @haffla For example, look at [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMonth): *"If a parameter you specify is outside of the expected range, `setMonth()` attempts to update the date information in the `Date` object accordingly. For example, if you use 15 for `monthValue`, the year will be incremented by 1, and 3 will be used for month."* – cdhowie Jan 09 '17 at 18:01
  • @cdhowie Thanks but that's just stupid. Haha. There is % (modulo) to keep a number in a range. – haffla Jan 09 '17 at 18:43
  • @haffla This is actually a very common concept for date/time APIs, and JavaScript is not unique in having this kind of behavior. Whether it's "stupid" or not depends on your goals. This API allows one to advance a date to the next month, irrespective of whether the current month is December, with a single line of code. There are no edge cases that need handling because the Date API handles them for you. – cdhowie Jan 09 '17 at 19:01
  • @cdhowie I don't like this form implicit behavior. I like to take care of the validity of my input myself. Only thing you have to do is `month = month % 11` and I guess this is exactly what the JS API is doing internally. Anyway, cheers, good to know. – haffla Jan 09 '17 at 19:08
  • @haffla Congrats, you have an off-by-one bug in your implementation. It should be `month % 12`. And your version doesn't advance the year like the JavaScript implementation is documented to do. – cdhowie Jan 09 '17 at 19:28
  • @cdhowie implementation? hahaha. hardly an implementation my friend, not even a snippet, just an idea. That "bug" wouldn't have made it into an "implementation". – haffla Jan 09 '17 at 20:06
  • Note that for users west of Greenwich, `new Date('2016-12-10')` creates a local date for 2016-12-09, which might be unexpected. Don't parse strings with the Date constructor (or Date.parse), just set the values directly: `new Date(2016, 11, 10)`. Also, if your start date is 2016-01-31, then adding one month via *setMonth* will create a date for 2 March. See [*Adding months to a Date in JavaScript*](http://stackoverflow.com/questions/12793045/adding-months-to-a-date-in-javascript/12793246#12793246). – RobG Jan 09 '17 at 22:31

1 Answers1

1

Your issue is that start.setMonth() mutates the date object stored in start; it does not create a new date object. Setting a month over 11 causes the year to advance accordingly. Because you are changing the date object stored in start, each time you set to a value over 11 you are advancing another year.

Setting it to 12 causes the date value to be set to January the following year. Then you set it to 13, which causes it to be set to February the year after that, and so on. This is why it advances by a year every loop iteration when i is above 11.

What you should be doing is starting with a Date object and setting the date's month to its current month plus one, and not using a second date object at all (unless you need to retain a copy somewhere). This way, if the month is December, you will set the month to 12, but on the next iteration the month is back to January (0), and so you would set it to 1 (February).

var i, d = new Date('2016-12-10');
for (i = 0; i < 16; i += 1) {
    d.setMonth(d.getMonth() + 1); // Advance date by one month.
    console.log(d);
}

See an example

cdhowie
  • 158,093
  • 24
  • 286
  • 300