A pedant that hangs out in the dark corner-cases of the web.

Thursday, March 30, 2006

Safari Date bug

The parameter for Date.setDate() is converted to a signed byte integer in Safari. This means that it can only be called with numbers from -128 to 127; all other values "roll over" into that range.

So, in Safari:

dt.setDate(128) = dt.setDate(-128)
dt.setDate(129) = dt.setDate(-127)
dt.setDate(130) = dt.setDate(-126)
...
dt.setDate(254) = dt.setDate(-2)
dt.setDate(255) = dt.setDate(-1)
dt.setDate(256) = dt.setDate(0)
dt.setDate(257) = dt.setDate(1)
dt.setDate(258) = dt.setDate(2)
dt.setDate(259) = dt.setDate(3)
...

Months and years/leap years are not exact, unless you use approximate mean months (30.436875) or mean years (365.2425), which can lead to rounding errors. So, if you need to add an arbitrary, exact time period to a date, the easiest way is dt.setDate(dt.getDate()+n), which even works for large values of n, since month and year rollovers are handled automatically.

Unfortunately, the Date.setDate() bug in Safari severely restricts the range of time that can be added to a date; only 96 (31 + 96 = 127) or fewer days can be reliably added. To work around this issue, something like this must be used:

function addDays(d,D)
{
var x= new Date(2000,1,1), y= new Date(2000,1,1);
if(x.setDate(128) > y.valueOf())
{ D.setDate(D.getDate()+d); return D; }
// Safari setDate(uint8) workaround
if(d < 0)
for(var i= -97; d < i; d-= i)
D.setDate(D.getDate()+i);
else
for(var i= 96; d > i; d-= i)
D.setDate(D.getDate()+i);
D.setDate(D.getDate()+d);
return D;
}

1 comment:

Viachaslau Churukanau said...

Thanks a lot!