Custom Multiscale D3 Time Series

| 3 min. (428 words)

Recently we added the ability to switch between displaying 12 hour times or 24 hour times in your dashboard. Part of this included the time display on the graphs that we display in the dashboards. In order to produce these graphs we are using NVD3.js which is a library that sits on top of D3.js and provides a selection of functions that enable easier graph production with D3.

In order to display the graphs we use a D3 TimeSeries along the x axis. The default format for this has dynamic labelling so that for instance when we cross a year boundary the year is displayed, when we cross a month boundary the month name is displayed and so on.

As we now needed to alter this time series to display both AM/PM and 24 hour time we had to create a custom multi scale D3 time series to replace the built in one that can be overridden with the appropriate time formats.

This was actually a lot easier than initially thought. In order to provide the different time formats we use a multi dimensional array. Each element holds a different time format along with a function that defines if we should use this time format or not, you can see this below:

var timeFormats = [[d3.time.format.utc("%Y"), function () {
  return true;
}],
[d3.time.format.utc("%B"), function (d) {
  return d.getUTCMonth();
}],
[d3.time.format.utc("%b %d"), function (d) {
  return d.getUTCDate() !== 1;
}],
[d3.time.format.utc("%a %d"), function (d) {
  return d.getUTCDay() && d.getUTCDate() !== 1;
}],
[d3.time.format.utc("%H:00"), function (d) {
  return d.getUTCHours();
}],
[d3.time.format.utc("%I %p"), function (d) {
  return d.getUTCHours() && configuration.display24hourtimes === false;
}]]

We then need to feed this into a function that can iterate over the array and test each format in turn to see if we should use it. This way we cascade over the formats until we find the correct one and then we use it to display the x axis label. You can see this function below:

var timeFormatPicker = function (formats) {
  return function (date) {
    var i = formats.length - 1, f = formats[i];
    while (!f[1](date)) {
      f = formats[--i];
    }
    return f[0](date);
  };
};

After that it is a simple matter of giving the function to the charts x axis tick format when we are defining it and hey presto we have the same functionality of clever date formatting but with the ability to provide our own custom formats and tests.

var d3TimeFormats = timeFormatPicker(timeFormats);
chart.xAxis.tickFormat(d3TimeFormats);

To see this in action for yourself, login to your Raygun account now, or sign up here

Martin