title: d3.js Date vs Half hour heat map not working
body: <p>I am trying to create a Date vs time heat map based on this <a href="http://bl.ocks.org/mbostock/3202354" rel="nofollow">http://bl.ocks.org/mbostock/3202354</a>
  The changes i am trying to do:
  ->x axis still has dates
  ->y axis will have 24 hours of the day in half hour intervals (00:00:00, 00:30:00, 01:00:00, 01:30:00 so on). Therefore 48 ticks</p>
  
  <p>I have managed to modify the code so far which is below. The two problems are:
  1. y axis does not have labels
  2. Data goes one tick below x axis</p>
  
  <p>I am a D3 noob (this is my first hands on experiment ) and struggling with it.</p>
  
  <p>d3+html+css code:</p>
  
  <pre><code>        &lt;!DOCTYPE html&gt;
          &lt;meta charset="utf-8"&gt;
          &lt;style&gt;
  
  body {
    font: 10px sans-serif;
  }
  
  .label {
    font-weight: bold;
  }
  
  .tile {
    shape-rendering: crispEdges;
  }
  
  .axis path,
  .axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
  }
  
  &lt;/style&gt;
  &lt;body&gt;
  &lt;script src="http://d3js.org/d3.v2.js?2.9.6"&gt;&lt;/script&gt;
  &lt;script&gt;
  
  var margin = {top: 20, right: 90, bottom: 30, left: 50},
      width = 960 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;
  
  var parseDate = d3.time.format("%m/%d/%Y").parse,
      formatDate = d3.time.format("%b %d");
  
  var parseTimeBucket = d3.time.format("%H:%M:%S").parse,
      formatTimeBucket =d3.time.format("%H:%M");  
  
  var StartTime = parseTimeBucket("00:00:00");
  var EndTime = parseTimeBucket("23:59:59");  
  
  var x = d3.time.scale().range([0, width]),
      y = d3.scale.linear().range([height, 0]),
      z = d3.scale.linear().range(["white", "black"]);
  
  // The size of the buckets in the CSV data file.
  // This could be inferred from the data if it weren't sparse.
  var xStep = 864e5,
      yStep = 18e5;
  
  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("TimeDateHeatMap.csv", function(buckets) {
  
    // Coerce the CSV data to the appropriate types.
    buckets.forEach(function(d) {
      d.date = parseDate(d.date);
      d.bucket = parseTimeBucket(d.bucket);
      d.count = +d.count;
    });
  
    // Compute the scale domains.
    x.domain(d3.extent(buckets, function(d) { return d.date; }));
    y.domain([StartTime, EndTime]);
   // y.domain(d3.extent(buckets, function(d) { return d.bucket; }));
    //console.log(d3.extent(buckets, function(d) { return d.bucket; }));
    z.domain([0, d3.max(buckets, function(d) { return d.count; })]);
  
    // Extend the x- and y-domain to fit the last bucket.
    // For example, the y-bucket 3200 corresponds to values [3200, 3300].
    x.domain([x.domain()[0], +x.domain()[1] + xStep]);
    y.domain([y.domain()[0], +y.domain()[1] + yStep]);
  
    // Display the tiles for each non-zero bucket.
    // See http://bl.ocks.org/3074470 for an alternative implementation.
    svg.selectAll(".tile")
        .data(buckets)
      .enter().append("rect")
        .attr("class", "tile")
        .attr("x", function(d) { return x(d.date); })
        .attr("y", function(d) { return y(d.bucket); })
        .attr("width", x(xStep) - x(0))
        .attr("height", y(0) - y(yStep))
        .style("fill", function(d) { return z(d.count); });
  
    // Add a legend for the color values.
    var legend = svg.selectAll(".legend")
        .data(z.ticks(6).slice(1).reverse())
      .enter().append("g")
        .attr("class", "legend")
        .attr("transform", function(d, i) { return "translate(" + (width + 20) + "," + (20 + i * 20) + ")"; });
  
    legend.append("rect")
        .attr("width", 20)
        .attr("height", 20)
        .style("fill", z);
  
    legend.append("text")
        .attr("x", 26)
        .attr("y", 10)
        .attr("dy", ".35em")
        .text(String);
  
    svg.append("text")
        .attr("class", "label")
        .attr("x", width + 20)
        .attr("y", 10)
        .attr("dy", ".35em")
        .text("Count");
  
    // Add an x-axis with label.
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.svg.axis().scale(x).ticks(d3.time.days).tickFormat(formatDate).orient("bottom"))
      .append("text")
        .attr("class", "label")
        .attr("x", width)
        .attr("y", -6)
        .attr("text-anchor", "end")
        .text("Date");
  
    // Add a y-axis with label.
    svg.append("g")
        .attr("class", "y axis")
        //.call(d3.svg.axis().scale(y).orient("left"))
        .call(d3.svg.axis().scale(y).ticks(d3.time.minutes).tickFormat(formatTimeBucket).orient("left"))
      .append("text")
        .attr("class", "label")
        .attr("y", 6)
        .attr("dy", ".71em")
        .attr("text-anchor", "end")
        .attr("transform", "rotate(-90)")
        .text("Value");
  });
  
  &lt;/script&gt;
  </code></pre>
  
  <p>TimeDateHeatMap.csv:</p>
  
  <pre><code>date,bucket,count
  7/20/2012,00:00:00,119
  7/20/2012,00:30:00,123
  7/20/2012,01:00:00,173
  7/20/2012,01:30:00,226
  7/20/2012,02:00:00,284
  7/20/2012,02:30:00,257
  7/20/2012,03:00:00,268
  7/20/2012,03:30:00,244
  7/20/2012,04:00:00,191
  7/20/2012,04:30:00,204
  7/20/2012,05:00:00,187
  7/20/2012,05:30:00,177
  7/20/2012,06:00:00,164
  7/20/2012,06:30:00,125
  7/20/2012,07:00:00,140
  7/20/2012,07:30:00,109
  7/20/2012,08:00:00,103
  7/21/2012,08:30:00,123
  7/21/2012,09:00:00,165
  7/21/2012,09:30:00,237
  7/21/2012,10:00:00,278
  7/21/2012,10:30:00,338
  7/21/2012,11:00:00,306
  7/21/2012,11:30:00,316
  7/21/2012,12:00:00,269
  7/21/2012,12:30:00,271
  7/21/2012,13:00:00,241
  7/21/2012,13:30:00,188
  7/21/2012,14:00:00,174
  7/21/2012,14:30:00,158
  7/21/2012,15:00:00,153
  7/21/2012,15:30:00,132
  7/22/2012,16:00:00,154
  7/22/2012,16:30:00,241
  7/22/2012,17:00:00,246
  7/22/2012,17:30:00,300
  7/22/2012,18:00:00,305
  7/22/2012,18:30:00,301
  7/22/2012,19:00:00,292
  7/22/2012,19:30:00,253
  7/22/2012,20:00:00,251
  7/22/2012,20:30:00,214
  7/22/2012,21:00:00,189
  7/22/2012,21:30:00,179
  7/22/2012,22:00:00,159
  7/22/2012,22:30:00,161
  7/22/2012,23:00:00,144
  7/22/2012,23:30:00,139
  7/22/2012,00:00:00,132
  7/22/2012,00:30:00,136
  7/22/2012,01:00:00,105
  7/23/2012,01:30:00,120
  7/23/2012,02:00:00,156
  7/23/2012,02:30:00,209
  7/23/2012,03:00:00,267
  7/23/2012,03:30:00,299
  7/23/2012,04:00:00,316
  7/23/2012,04:30:00,318
  7/23/2012,05:00:00,307
  7/23/2012,05:30:00,295
  7/23/2012,06:00:00,273
  7/23/2012,06:30:00,283
  7/23/2012,07:00:00,229
  7/23/2012,07:30:00,192
  7/23/2012,08:00:00,193
  7/23/2012,08:30:00,170
  7/23/2012,09:00:00,164
  7/23/2012,09:30:00,154
  7/23/2012,10:00:00,138
  7/23/2012,10:30:00,101
  7/23/2012,11:00:00,115
  7/23/2012,11:30:00,103
  7/24/2012,12:00:00,105
  7/24/2012,12:30:00,156
  7/24/2012,13:00:00,220
  7/24/2012,13:30:00,255
  7/24/2012,14:00:00,308
  7/24/2012,14:30:00,338
  7/24/2012,15:00:00,318
  7/24/2012,15:30:00,255
  7/24/2012,16:00:00,278
  7/24/2012,16:30:00,260
  7/24/2012,17:00:00,235
  7/24/2012,17:30:00,230
  7/24/2012,18:00:00,185
  7/24/2012,18:30:00,145
  7/24/2012,19:00:00,147
  7/24/2012,19:30:00,157
  7/24/2012,20:00:00,109
  7/25/2012,20:30:00,104
  7/25/2012,21:00:00,191
  7/25/2012,21:30:00,201
  7/25/2012,22:00:00,238
  7/25/2012,22:30:00,223
  7/25/2012,23:00:00,229
  7/25/2012,23:30:00,286
  7/25/2012,00:00:00,256
  7/25/2012,00:30:00,240
  7/25/2012,01:00:00,233
  7/25/2012,01:30:00,202
  7/25/2012,02:00:00,180
  7/25/2012,02:30:00,184
  7/25/2012,03:00:00,161
  7/25/2012,03:30:00,125
  7/25/2012,04:00:00,110
  7/25/2012,04:30:00,101
  7/26/2012,05:00:00,132
  7/26/2012,05:30:00,117
  7/26/2012,06:00:00,124
  7/26/2012,06:30:00,154
  7/26/2012,07:00:00,167
  7/26/2012,07:30:00,137
  7/26/2012,08:00:00,169
  7/26/2012,08:30:00,175
  7/26/2012,09:00:00,168
  7/26/2012,09:30:00,188
  7/26/2012,10:00:00,137
  7/26/2012,10:30:00,173
  7/26/2012,11:00:00,164
  7/26/2012,11:30:00,167
  7/26/2012,12:00:00,115
  7/26/2012,12:30:00,116
  7/26/2012,13:00:00,118
  7/26/2012,13:30:00,125
  7/26/2012,14:00:00,104
  </code></pre>
  
  <p>Changes to original mbostock code:They mostly concentrate on y axis, while x xis remains same as original. Because the original mbostock code has numbers on y axis while i need 48 half hour interval buckets on y axis</p>
  
  <p>I have added the following so that later on i can create a y axis of times:</p>
  
  <pre><code>var parseTimeBucket = d3.time.format("%H:%M:%S").parse,
      formatTimeBucket =d3.time.format("%H:%M"); 
  </code></pre>
  
  <p>Then changed domain this way:</p>
  
  <pre><code>var StartTime = parseTimeBucket("00:00:00");
  var EndTime = parseTimeBucket("23:59:59");   
  y.domain([StartTime, EndTime]);
  </code></pre>
  
  <p>also added ystep in addition to xstep. ystep corresponds to 30 minute intervals</p>
  
  <pre><code>var xStep = 864e5,
      yStep = 18e5;
  </code></pre>
  
  <p>and then finally to build the axis i changed the third line (.call...)</p>
  
  <pre><code>svg.append("g")
        .attr("class", "y axis")
         .call(d3.svg.axis().scale(y).ticks(d3.time.minutes, 30).tickFormat(formatTimeBucket).orient("left"))
      .append("text")
        .attr("class", "label")
        .attr("y", 6)
        .attr("dy", ".71em")
        .attr("text-anchor", "end")
        .attr("transform", "rotate(-90)")
        .text("Value");
  </code></pre>