UUake Up UUake Up - 2 months ago 8
Javascript Question

Oddities with JS Date() Object

Ok so I'm writing a simple script that you can feed any year into and it will spit out the number of days in each month of that year. My function looks something like this:

function( year ){
var months = [];
var date = new Date();
date.setFullYear( year );


for( i = 1; i < 13; i++ ){
date.setMonth( i );
date.setDate( 0 );
months[i] = date.getDate();
console.log([date, i, date.getMonth()]);
}

console.log(months);
return months;
}


Should work a treat as far as I'm aware but the strangest thing occurs around August. Here's the console log:

VM691:10 [Sun Jan 31 2016 12:31:41 GMT+0000 (GMT), 1, 0]
VM691:10 [Mon Feb 29 2016 12:31:41 GMT+0000 (GMT), 2, 1]
VM691:10 [Thu Mar 31 2016 12:31:41 GMT+0100 (BST), 3, 2]
VM691:10 [Sat Apr 30 2016 12:31:41 GMT+0100 (BST), 4, 3]
VM691:10 [Tue May 31 2016 12:31:41 GMT+0100 (BST), 5, 4]
VM691:10 [Thu Jun 30 2016 12:31:41 GMT+0100 (BST), 6, 5]
VM691:10 [Sun Jul 31 2016 12:31:41 GMT+0100 (BST), 7, 6]
VM691:10 [Fri Sep 30 2016 12:31:41 GMT+0100 (BST), 8, 8]
VM691:10 [Fri Sep 30 2016 12:31:41 GMT+0100 (BST), 9, 8]
VM691:10 [Mon Oct 31 2016 12:31:41 GMT+0000 (GMT), 10, 9]
VM691:10 [Wed Nov 30 2016 12:31:41 GMT+0000 (GMT), 11, 10]
VM691:10 [Sat Dec 31 2016 12:31:41 GMT+0000 (GMT), 12, 11]
VM691:13 [1: 31, 2: 29, 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 30, 9: 30, 10: 31, 11: 30, 12: 31]


As you can see September is read twice even though the i count clearly states August (note setMonth(i) takes the months in the classic order i.e. 1 = January, but getMonth() returns them in a programmatic order 0 = January). So I did a little digging and turns out that when the setMonth method is explicitly set to 8 (August) the console returns a list that fluctuates between August and September:

VM757:10 [Wed Aug 31 2016 12:36:12 GMT+0100 (BST), 1, 7]
VM757:10 [Fri Sep 30 2016 12:36:12 GMT+0100 (BST), 2, 8]
VM757:10 [Wed Aug 31 2016 12:36:12 GMT+0100 (BST), 3, 7]
VM757:10 [Fri Sep 30 2016 12:36:12 GMT+0100 (BST), 4, 8]
VM757:10 [Wed Aug 31 2016 12:36:12 GMT+0100 (BST), 5, 7]
VM757:10 [Fri Sep 30 2016 12:36:12 GMT+0100 (BST), 6, 8]
VM757:10 [Wed Aug 31 2016 12:36:12 GMT+0100 (BST), 7, 7]
VM757:10 [Fri Sep 30 2016 12:36:12 GMT+0100 (BST), 8, 8]
VM757:10 [Wed Aug 31 2016 12:36:12 GMT+0100 (BST), 9, 7]
VM757:10 [Fri Sep 30 2016 12:36:12 GMT+0100 (BST), 10, 8]
VM757:10 [Wed Aug 31 2016 12:36:12 GMT+0100 (BST), 11, 7]
VM757:10 [Fri Sep 30 2016 12:36:12 GMT+0100 (BST), 12, 8]
VM757:13 [1: 31, 2: 30, 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 30, 9: 31, 10: 30, 11: 31, 12: 30]


When set 9 it returns only September and at 7 only July, so what gives?

Any help would be greatly appreciated. Cheers

Answer

Step through your code and see what happens:

  • Initial setup, date = 13/Sep/2016
  • Enter loop
  • Set month to 1, date = 13/Feb/2016
  • Set day to 0, date = 0/Feb/2016 = 31/Jan/2016
  • Log details
     

  • Second iteration of loop:

  • Set month to 2, date = 31/Mar/2016
  • Set day to 0, date = 0/Mar/2016 = 29/Feb/2016
  • Log details

This works fine, up until August.

  • Set month to 8, date = 30/Aug/2016
  • Set day to 0, date = 0/Aug/2016 = 31/Jul/2016
  • Log details
     

  • Set month to 9, date = 31/Sep/2016 = 1/Oct/2016 !!!

  • Set date to 0, date = 0/Oct/2016 = 30/Sep/2016
  • Log details - "august" is logged as having 30 days

To solve this problem, call setDate(1) at the start of your loop, to ensure you won't have any overflow problems when setting the month.