For a client's website, I needed to enumerate the 12 months preceding a given date to create links to archived content. The site uses a javascript templating engine to create HTML, offloading the process from the server, so generating the list of months on the client side in javascript seemed like a reasonable choice. For the past week, everything looked great, but suddenly today I noticed that it was repeating the current month.
The code itself is pretty simple. It's written as a
dustjs helper function that uses the
Date.setMonth
function to handle wrapping from January of one year to December
of the previous year.
dust.helpers.month_list = function(chunk, ctx, bodies, params) {
var count = dust.helpers.tap(params.count, chunk, ctx) | 0;
var curDate = new Date();
for (var i = 0; i < count; ++i) {
var dateStr = (1900 + curDate.getYear()) + '-' + (curDate.getMonth()+1);
chunk = chunk.render(bodies.block, ctx.push({date : dateStr}));
curDate.setMonth(curDate.getMonth()-1);
}
return chunk;
}
Some quick printf debugging, adding console.log(curDate)
at the start of the
loop, shows this surprising result:
Tue Dec 31 2013 20:47:14 GMT-0800 (Pacific Standard Time)
Sun Dec 01 2013 20:47:14 GMT-0800 (Pacific Standard Time)
Fri Nov 01 2013 20:47:14 GMT-0700 (Pacific Daylight Time)
Tue Oct 01 2013 20:47:14 GMT-0700 (Pacific Daylight Time)
Apparently, on the 31st of the month, subtracting one month from the current
date does not have the desired effect, in Chrome (on Windows 8.1). I ran the
test again in IE 11 and observed the same behavior, as well as tried by
manually setting the date to the 31st of October and subtracting a month, again
seeing the same behavior. I'm not sure if that means this is somehow part of
the specification, or if it's a bug caused by a library used underneath the
hood in both browsers, but the end result is the same. My "clean" code to use
setMonth(getMonth()-1)
instead of writing out an if statement to detect the
start of the year and wrap correctly now contains a cryptic loop that detects
if the month didn't actually change and subtracts it again, all to deal with a
bug that only happens once a month.
Post-migration update: The original comments for this post discussed the
solution a bit and concluded that the best approach is to use
curDate.setDate(0)
to get the previous month, per the MDN
documentation.
The comments are lost to the internet now but I felt the correct solution was
important enough to include in an update here.