When using offsets with irregular intervals, "calendar math" is generally desired, but JavaScript does few favors for us on this front:
> date = new Date('Tue Mar 31 2015 16:59:59 GMT-0600 (MDT)');
< Tue Mar 31 2015 16:59:59 GMT-0600 (MDT)
> date.getMonth()
< 2
> date.setMonth(date.getMonth() + 1)
< 1430521199000
> date.getMonth() // 2 + 1 == 4? Yikes.
< 4
> date
< Fri May 01 2015 16:59:59 GMT-0600 (MDT)
d3.time.month.offset() exposes this detail:
> date = new Date('Tue Mar 31 2015 16:59:59 GMT-0600 (MDT)');
< Tue Mar 31 2015 16:59:59 GMT-0600 (MDT)
> d3.time.month.offset(date, 1) // I really want 2015-04-30...
< Fri May 01 2015 16:59:59 GMT-0600 (MDT)
...which makes it less than suitable to do something like calculate the end of a three-month quarter given a start date.
My sense is that moment.js gets this right:
> moment.utc('2015-03-31T23:59:59').add(1, 'months').toDate()
< Date 2015-04-30T23:59:59.000Z
> moment.utc(moment.utc('2015-04-01T00:00:00').add(3, 'months').toDate() - 1).toDate()
< Date 2015-06-30T23:59:59.999Z
But I am unsure if that means d3 gets this wrong? Or are D3 intervals just unsuitable for strictly calendar-based boundary calculation?
Thoughts?
Sean