Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript date parsing bug - fails for dates in June (??)

I have some javascript which parses an ISO-8601 date. For some reason, it is failing for dates in June. But dates in July and May work fine, which doesn't make sense to me. I'm hoping a fresh set of eyes will help, because I can't see what I'm doing wrong here.

Function definition (with bug)

function parseISO8601(timestamp)
{
  var regex = new RegExp("^([\\d]{4})-([\\d]{2})-([\\d]{2})T([\\d]{2}):([\\d]{2}):([\\d]{2})([\\+\\-])([\\d]{2}):([\\d]{2})$");
  var matches = regex.exec(timestamp);
  if(matches != null)
  {
    var offset = parseInt(matches[8], 10) * 60 + parseInt(matches[9], 10);
    if(matches[7] == "-")
      offset = -offset;

    var date = new Date();
    date.setUTCFullYear(parseInt(matches[1], 10));
    date.setUTCMonth(parseInt(matches[2], 10) - 1); //UPDATE - this is wrong
    date.setUTCDate(parseInt(matches[3], 10));
    date.setUTCHours(parseInt(matches[4], 10));
    date.setUTCMinutes(parseInt(matches[5], 10) - offset);
    date.setUTCSeconds(parseInt(matches[6], 10));
    date.setUTCMilliseconds(0);

    return date;
  }
  return null;
}

Test code

alert(parseISO8601('2009-05-09T12:30:00-00:00').toUTCString());
alert(parseISO8601('2009-06-09T12:30:00-00:00').toUTCString());
alert(parseISO8601('2009-07-09T12:30:00-00:00').toUTCString());

Output

  • Sat, 09 May 2009 12:30:00 GMT
  • Thu, 09 Jul 2009 12:30:00 GMT
  • Thu, 09 Jul 2009 12:30:00 GMT

Update

Thanks for the quick answers, the problem was that the Date object was initially today, which happened to be July 31. When the month was set to June, before I changed the day, it was temporarily June 31, which got rolled forward to July 1.

I've since found the following to be a cleaner implementation, as it sets all the date attributes at once:

function parseISO8601(timestamp)
{
  var regex = new RegExp("^([\\d]{4})-([\\d]{2})-([\\d]{2})T([\\d]{2}):([\\d]{2}):([\\d]{2})([\\+\\-])([\\d]{2}):([\\d]{2})$");
  var matches = regex.exec(timestamp);
  if(matches != null)
  {
    var offset = parseInt(matches[8], 10) * 60 + parseInt(matches[9], 10);
    if(matches[7] == "-")
      offset = -offset;

    return new Date(
      Date.UTC(
        parseInt(matches[1], 10),
        parseInt(matches[2], 10) - 1,
        parseInt(matches[3], 10),
        parseInt(matches[4], 10),
        parseInt(matches[5], 10),
        parseInt(matches[6], 10)
      ) - offset*60*1000
    );
  }
  return null;
}
like image 655
Kip Avatar asked Jul 31 '09 18:07

Kip


2 Answers

The problem is that today is July 31.

When you set:

var date = new Date();

Then date.getUTCDate() is 31. When you set date.setUTCMonth(5) (for June), you are setting date to June 31. Because there is no June 31, the JavaScript Date object turns it into July 1. So immediately after setting calling date.setUTCMonth(5) if you alert(date.getUTCMonth()); it will be 6.

This isn't unique to June. Using your function on the 31st of any month for any other month that does not have 31 days will exhibit the same problem. Using your function on the 29th (non-leap years), 30th or 31st of any month for February would also return the wrong result.

Calling setUTC*() in such a way that any rollovers are overwritten by the correct value should fix this:

var date = new Date();
date.setUTCMilliseconds(0);
date.setUTCSeconds(parseInt(matches[6], 10));
date.setUTCMinutes(parseInt(matches[5], 10) - offset);
date.setUTCHours(parseInt(matches[4], 10));
date.setUTCDate(parseInt(matches[3], 10));
date.setUTCMonth(parseInt(matches[2], 10) - 1);
date.setUTCFullYear(parseInt(matches[1], 10));
like image 125
Grant Wagner Avatar answered Oct 20 '22 00:10

Grant Wagner


The date object starts off with the current date. It's the 31st today so setting 2009-06-09 gives:

var date = new Date();     // Date is 2009-07-31
date.setUTCFullYear(2009); // Date is 2009-07-31
date.setUTCMonth(6 - 1);   // Date is 2009-06-31 = 2009-07-01
date.setUTCDate(9);        // Date is 2009-07-09

If you set the date to the 1st before you begin, then you should be safe.

like image 34
Greg Avatar answered Oct 20 '22 00:10

Greg