This example shows one solution for how to get d3 axis ticks to display relative time (durations) into the past by hour and minute [-01:30, -01:00, -00:30, 00:00, 00:30, 01:30].
forked from mbostock's block: Time of Day
xxxxxxxxxx
<meta charset="utf-8">
<style>
.dots path {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis--y line {
stroke-opacity: 0.2;
}
.axis--y path {
stroke: none;
}
.axis text {
font: 10px sans-serif;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
// This custom tick mark formatter displays signed hours and minutes
// relative to the given "zeroTime" date.
function relativeTimeFormatter(zeroTime){
return function (d){
var minutesNegative = d3.time.minutes(d, zeroTime).length;
var minutesPositive = d3.time.minutes(zeroTime, d).length;
var minutes = minutesNegative ? minutesNegative : minutesPositive;
var hours = Math.floor(minutes / 60);
var sign = minutesNegative ? "-" : "";
hours = addLeadingZero(hours);
minutes = (minutes % 60);
minutes = addLeadingZero(minutes);
return sign + hours + ":" + minutes;
}
}
function addLeadingZero(number){
return (number < 10 ? "0" : "") + number;
}
var parseTime = d3.time.format.utc("%H:%M").parse,
zeroTime = parseTime("10:00");
var margin = {top: 30, right: 30, bottom: 30, left: 30},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.time.scale.utc()
.domain([
// 3 hours before zero time.
d3.time.hour.utc.offset(zeroTime, -3),
// 2 hours after zero time.
d3.time.hour.utc.offset(zeroTime, 2)
])
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("tweets.csv", type, function(error, data) {
if (error) throw error;
y.domain([0, d3.max(data, function(d) { return d.rate; })]);
svg.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(relativeTimeFormatter(zeroTime)));
svg.append("g")
.attr("class", "dots")
.selectAll("path")
.data(data)
.enter().append("path")
.attr("transform", function(d) { return "translate(" + x(d.time) + "," + y(d.rate) + ")"; })
.attr("d", d3.svg.symbol()
.size(40));
var tick = svg.append("g")
.attr("class", "axis axis--y")
.call(d3.svg.axis()
.scale(y)
.tickSize(-width)
.orient("left"))
.select(".tick:last-of-type");
var title = tick.append("text")
.attr("dy", ".32em")
.text("tweets per hour");
tick.select("line")
.attr("x1", title.node().getBBox().width + 6);
});
function type(d) {
d.rate = +d.count / 327 * 60; // January 8 to November 30
d.time = parseTime(d.time);
d.time.setUTCHours((d.time.getUTCHours() + 24 - 7) % 24);
return d;
}
</script>
https://d3js.org/d3.v3.min.js