This example shows how to plot daily time intervals in the style of Eric Boam’s Seven Months of Sleep. (The data here is fake.)
This graph uses a time scale to plot time-of-day along the y-axis. Time scales are normally used to plot absolute time: a specific moment on a specific day in a specific year. Here, though, we’re interested in studying the daily pattern, so the start and end times for each sleep interval are converted to offsets (elapsed times) relative to the midnight immediately preceeding the start time. Given an interval d:
var midnight = d3.utcDay.floor(d[0]),
startOffset = d[0] - midnight,
endOffset = d[1] - midnight;
The offsets are measured in milliseconds since that’s the JavaScript convention. To avoid inconsistencies across browser local time zones, the dates are represented as local times in the data but parsed as UTC. The resulting Date objects are inconsistent with the original dates, but the difference is irrelevant because we just want to plot the local time-of-day.
Also, this representation makes it easy to convert an offset back to an absolute time on an arbitrary day for using with a d3.scaleUtc and d3.utcFormat. The arbitrary day is the UNIX epoch:
function date(offset) {
return new Date(offset);
}
A similar technique is used in my visualization of Eric Fischer’s Twitter feed.
Unfortunately the example doesn't work well (or indeed at all) outside the California timezone. With my OS set in Europe TZ, the blue bars are displayed much higher (so high in fact that they don't appear on-screen if I don't tweak the y range). If I switch to New York TZ, then some bars appear a little, and others not.
Here is a block that apparently fixes the graph: https://bl.ocks.org/Fil/f2348baf08f341f8ee4ee24c1c2e0651
However the fix is not pretty. The problem is that the code uses some functions which are meant for the viewer's local time (eg. d3.timeDay.floor) in order to compute the sleeper's local day (say: the midnight that precedes sleeping time).
BTW this also means that the current code fails (even in California TZ) when the person works on D3 past midnight and goes to sleep at e.g. 1am. But that second problem is easily fixed by having
d.day = d3.timeDay.floor(d3.timeHour.offset(d[0],-12))
instead of
d.day = d3.timeDay.floor(d[0])
(with that, going to bed before 12am is associated to the previous day's night)
For the TZ problem, I don't think my solution is right. I'm fiddling with a
var fixtz
that add the timezones difference to all dates. There must be something better — but at that point I think we need to expand the API so that.floor()
and the like can be set to work relative to a specific TZ, and I'm giving up :-)