It would be nice if unit tests could save one from every silly mistake it is possible to make when writing software. These were my thoughts when I found the following bit of javascript code on a small website I wrote years ago. This rather obvious time-related bug only surfaced now because of the current date under which the code was running.
Give the following javascript method a yyyymmdd date string and you will only sometimes get the correct output:
function makeDate(rawDate) {
var dayNum=rawDate.substr(6,2);
var monNum=new Number(rawDate.substr(4,2));
var yrNum=new Number(rawDate.substr(0,4));
var monName=months[monNum-1];
var dateObj=new Date();
dateObj.setDate(new Number(dayNum));
dateObj.setMonth(monNum-1);
dateObj.setYear(yrNum);
var dayOfWeekNum=dateObj.getDay();
var dayOfWeekName=days[dayOfWeekNum];
return dayOfWeekName+" "+dayNum+" "+monName+" "+yrNum;
}
For instance, on Sep 24, 2011, feed the string "20110731" to the function above and you will get a result that claims 7/31/2011 is a Friday. That day is actually a Sunday.
The problem is that because the month stored in the date reference corresponds to a 30 day month when you set the day to 31, the month value increments as you set the day (while the day of month changes from 31 to 1). Stepwise explanation below:
function makeDate(rawDate) {
var dayNum=rawDate.substr(6,2);
var monNum=new Number(rawDate.substr(4,2));
var yrNum=new Number(rawDate.substr(0,4));
var monName=months[monNum-1];
var dateObj=new Date();
// dateObj at this point is today's date, Sat 24 Sep 2011
// since rawDate is "20110731" the dayNum we use next is "31"
dateObj.setDate(new Number(dayNum)); // boom!
// dateObj is now Fri 1 Oct 2011, how?
// because there is no such thing as September 31st,
// the date object rolls over to the next month.
dateObj.setMonth(monNum-1);
// we still set this correctly to July
// we still set this correctly to July
dateObj.setYear(yrNum); // and this is fine to 2011
var dayOfWeekNum=dateObj.getDay();
// but here the problem comes to light
// but here the problem comes to light
// dayOfWeekNum reflects a day of Friday, since
// July 1, 2011 is a Friday
// July 1, 2011 is a Friday
var dayOfWeekName=days[dayOfWeekNum];
return dayOfWeekName+" "+dayNum+" "+monName+" "+yrNum;
}
The way I chose to solve this is to set the dateObj from most significant denomination to least significant to avoid rollover problems. This order ensures that before we attempt to set any date like the 29th, 30th or 31st that the correct month has already been set, avoiding rollovers:
var dateObj=new Date();
dateObj.setYear(yrNum);
dateObj.setMonth(monNum-1);
dateObj.setDate(new Number(dayNum));
// as long as the month is already right, we're ok here
var dayOfWeekNum=dateObj.getDay();
After fixing this, I did find a good reference here for someone experiencing a similar problem:
No comments:
Post a Comment