Search code examples
highcharts

highcharts datetime stepped area or column chart render on tick marks


I'm trying to create a highcharts column (or stepped area) graph that displays columns right next together and align with the tick marks.

With column chart, I try to adjust the pointWidth to fill in the # of pixels between ticks but that starts shifting the column to the left and no longer aligns with the tick.

With area chart, there are gaps as the start and end of each series only renders a half column and the columns don't align with the ticks.

Highcharts.chart('container', {
  chart: {
    type: 'column',
    // type: 'area',
  },
  xAxis: {
    type: 'datetime',
    tickInterval: 24 * 3600 * 1000,
    min: new Date('2023-06-14T00:00:00').getTime(),
    max: new Date('2023-06-27T00:00:00').getTime(),
    gridLineColor: '#cccccc',
    gridLineWidth: 1,
    labels: {
      format: '{value:%a %d}'
    }
  },
  plotOptions: {
    column: {
      borderRadius: 0,
      borderWidth: 0,
      pointPlacement: 'on',
      pointPadding: 0,
      grouping: false,
      pointWidth: 40,
    },
    area: {
      step: 'center',
    },
    series: {
      enableMouseTracking: false,
      dataLabels: {
        enabled: true,
        color: '#00000',
        style: {
          fontSize: '18px',
          fontWeight: 'normal',
          textOutline: false
        },
      }
    }
  },
  series: [{
    name: 'Data 1',
    color: 'cyan',
    data: [
      { x: new Date('2023-06-15T00:00:00'), y: 24 },
      { x: new Date('2023-06-16T00:00:00'), y: 5 },
      { x: new Date('2023-06-17T00:00:00'), y: 55 },
      { x: new Date('2023-06-18T00:00:00'), y: 15 },
      { x: new Date('2023-06-19T00:00:00'), y: 16 },
      { x: new Date('2023-06-20T00:00:00'), y: 2 },
    ]
  },{
    name: 'Data 2',
    color: 'red',
    data: [
      { x: new Date('2023-06-21T00:00:00'), y: 24 },
      { x: new Date('2023-06-22T00:00:00'), y: 5 },
      { x: new Date('2023-06-23T00:00:00'), y: 55 },
      { x: new Date('2023-06-24T00:00:00'), y: 15 },
      { x: new Date('2023-06-25T00:00:00'), y: 16 },
      { x: new Date('2023-06-26T00:00:00'), y: 2 },
    ]
  }]
});

JSFiddle:

Expected Graph


Solution

  • In the plotOptions.column, setting the pointWidth to a value (in pixels) is a very fragile computation. The best way to achieve what you wanted is to set pointPadding: 0 (which you did) and groupPadding: 0. See the docs on pointWidth, esp. the "When set to undefined" part.

    There is still another problem: that of the time zone. By default highcharts uses UTC for axis ticks and labels, which results in various shifts for different users, especially at time zones far from UTC. That is due to the fact that your dates are parsed in user time zones: new Date('2023-06-14T00:00:00').

    • One solution is to set the dates in UTC also, for instance by adding a Z at the end of the date string: new Date('2023-06-14T00:00:00Z'), see date string format. jsFiddle with this version

    • Another solution is to disable highchart's using of UTC, set useUTC: false, jsFiddle, that will result in both date parsing and axis ticks being set in user's time zone.


    Edit: As I realise that the time zone issue might not be sufficiently explained, let me give an example: consider the user (that is the person that opens in their browser the webpage containing the chart) is on time zone UTC+6 (the time zone known by the browser, typically retrieved from the operating system of the user's computer).

    • new Date('2023-06-14T00:00:00') evaluates to midnight for the user time zone;
    • but at midnight on user's UTC+6, (where the centre of the data point is) the time at UTC(+0) is 6 hours behind, so in UTC the date is new Date('2023-06-13T18:00:00Z').
    • conversely, at midnight UTC (where the axis tick is placed), on user's time zone the date will be new Date('2023-06-14T06:00:00').
    • thus, the x axis tick, if set on UTC, is 6 hours to the right to the x centre of the data rectangle, which is evaluated at UTC+6, so the rectangle seems shifted by 6h, or 25% of its width of 1 day to the left.