Tag Archives: time

When is seven days not a week?

I’ve been using Date.js on a project, and it’s an awesome library. I especially love when someone else handles dates for me (am-i-right fellow developers? ;) ), and everything was going great until we discovered a rare time bug. I had forgotten something very important regarding time: it’s relative.

We’re not writing guidance software for rockets, so we don’t have to deal with relativity in the Einstein sense, but relative in a more day-to-day and somewhat artificial sense. Although I’m not exactly sure how Benjamin Franklin really felt about daylight savings time, it certainly makes programming a little trickier for us Philadelphians.

Here is an example for those following along in firebug:

var start = new Date('2010/01/01');
var end = new Date('2011/01/01');
var diff = (new TimeSpan(end - start)).days;

for(var i=0;i<diff;i++) {
    var today = new Date(start).add(i).days();
    var plusSeven = today.clone().add(7).days();
    var oneWeek = (new TimeSpan(plusSeven - today));

    if ((oneWeek.days != 7) || (oneWeek.hours != 0)) {
        console.debug("Time Warp: ", today);
    }
}

Say you made some bit of code that generated a date range by adding or subtracting some number of days off a date, you’d have a bug from March 8th through March 14th, and then it’d be gone until November 1st through the 7th! Not only is this sneaky, it’s hard to predict! Although this isn’t really a bug in Date.js, it requires us to change how we think about time.

The recommended Googling didn’t provide a straightforward, and rock-solid technique for identifying and adjusting for this effect in any given time zone.

So I made one:

First, a quick example:
(my apologies if this is obvious or full of mistakes)

//Lets say you wanted to setup some DateTime controls
//with a rolling date range, lets say using an offset in days
//with Date.js you'd probably do something like this:

var offsetDays = 7;
var start = new Date('2010/03/09');
var end = start.clone().add(offsetDays).days();

//so far so good, we got the intended date:
//Tue, March 16th, 2010
//but we want to update and store the rolling date range,
//so lets get that range back out:

// ... snip! ...

var diff = (new TimeSpan(end - start));
if (diff.days != offsetDays) {
    //daylight savings time caused time to 'spring ahead',
    //and our 7 days is instead 6 days 23 hours
    console.debug('user changed date');
}

We’re going to need a simple function that gets a TimeSpan that is adjusted for the daylight savings time adjustment:

// gets the difference between two dates,
// adjusting for daylight savings time.
var getDSTAdjustedDiff = function(startDate, endDate) {
    var startOffset = startDate.getUTCOffset();
    var endingOffset = endDate.getUTCOffset();

    if (startOffset > endingOffset) {
        startDate = startDate.clone().add({hours: -1 });
    }
    if (endingOffset > startOffset) {
        endDate = endDate.clone().add({hours: -1 });
    }
    return (new TimeSpan(endDate - startDate));
};

If we revisit our example from before:

var start = new Date('2010/01/01');
var end = new Date('2011/01/01');
var diff = (new TimeSpan(end - start)).days;

for(var i=0;i<diff;i++) {
    var today = new Date(start).add(i).days();
    var plusSeven = today.clone().add(7).days();
    var oneWeek = (new TimeSpan(plusSeven - today));
    var adjustedDiff = getDSTAdjustedDiff(today, plusSeven);

    if ((oneWeek.days != 7) || (oneWeek.hours != 0)) {
        console.debug("Time Warp: ", today);
    }
    if ((adjustedDiff.days != 7) || (adjustedDiff.hours != 0)) {
        console.debug("Adjustment fail!: ", today, adjustedDiff );
    }
}

If everything went well, this code block should run and run without the second block ever firing. I hope this helps someone else out there avoid this pitfall, now if I can just figure out time zones…