Home > database >  d3.timeDay function starts in year 2023 and runs backwards to infinity
d3.timeDay function starts in year 2023 and runs backwards to infinity

Time:11-09

In my d3.js chart, I have data from 2002 to 2023.

I verified this using x.domain(), which returns:

0: Wed Jul 31 2002 00:00:00 GMT 0200 (Central European Summer Time) {}
1: Mon Jul 31 2023 00:00:00 GMT 0200 (Central European Summer Time) {}

Then I try to create x-axis ticks:

let x = d3.scaleTime()
    .rangeRound([0, 600]);
let fiscal_year_ends_month = 10;
d3.axisBottom(x)
.tickFormat(d3.timeFormat("%m/%y")).ticks(data.length < 60 ? d3.timeMonth : d3.timeYear)
.ticks(d3.timeDay.filter(function(d) {
    return d.getMonth() === fiscal_year_ends_month;
}))

This is supposed to iterate through all the dates in the range, find the ones that meet the condition that the month is what I input, and create the ticks.

However, it runs for infinity.

Specifically, if I do this:

.ticks(d3.timeDay.filter(function(d) {
    console.log(d.getFullYear(), d.getMonth(), d.getDate());
    return d.getMonth() === fiscal_year_ends_month;
}))

It appears to start at the end of my x.domain (2023) and run backwards to infinity. I let it run for a while now and the last output of console.log was June 10, 322 B.C.:

-322 5 10

Needless to say, my chart of stock market data should not start in the 4th century B.C.

Why isn't the process running only through the dates in my x.domain()?

CodePudding user response:

The issue can be re-created like this:

.ticks(d3.timeDay.filter(function(d) {
  console.log(d.getFullYear(), d.getMonth(), d.getDate());
  return false; // <--- generates infinite loop backward in time !!
}))

This suggests an issue in your input data with regards to the month index you are trying to filter on. It also suggests there's a bug in the library that let's this happen...

With fiscal_year_ends_month = 10 I assume you are looking to only have November ticks for each year of data ? There is a d3.timeMonth interval that you can use for this purpose e.g.:

.ticks(d3.timeMonth.filter(function(d) {
  return d.getMonth() === fiscal_year_ends_month;
}));

Working example:

// your constant
const fiscal_year_ends_month = 10;

// sample data
let data = [];
for (let i= 0; i<2370; i  ) {
  let d = new Date()
  data.push({
    dt: d.setDate(d.getDate()   i),
    val: i
  });
}

// time scale
let x = d3.scaleTime()
  .domain(d3.extent(data, d => d.dt))
  .range([0, 540]);
  
// render
let svg = d3.select("body")
  .append("svg")
  .attr("width", 600)
  .attr("height", 150)
  .append("g")
  .attr("transform", "translate(30,30)");
  
let xAxis = d3.axisBottom(x)
  .tickFormat(d3.timeFormat("%m/%y"))
  .ticks(d3.timeMonth.filter(function(d) {
    return d.getMonth() === fiscal_year_ends_month;
  }));
  
svg.append("g")
  .attr("transform", "translate(0,90)")
  .call(xAxis);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related