apexcharts / Blazor-ApexCharts

A blazor wrapper for ApexCharts.js
https://apexcharts.github.io/Blazor-ApexCharts
MIT License
788 stars 91 forks source link

Chart does not render if X-Axis date includes 1970-01-01 #375

Closed sam-wheat closed 8 months ago

sam-wheat commented 8 months ago

I discovered this when testing some historical economic data. It took me a couple hours of guessing until I concluded that the chart does not render based on a literal X-Axis date.
I cloned the repo and searched for 1970 but found nothing.

The chart will render correctly in all cases if XAxisType="XAxisType.Datetime" is removed. However in my case I need this option because I am charting time series of different lengths and I need them to align correctly on the X-Axis.

The attached project demonstrate this anomaly. My project is net 7 and works same.

@page "/"

<PageTitle>Home</PageTitle>

<ApexChart TItem="MyData"
           Title="Sample Data" XAxisType="XAxisType.Datetime" Options="@options" >

    @foreach(string category in Data.GroupBy(x => x.Category).Select(x => x.Key))
    {

        <ApexPointSeries TItem="MyData"
                         Items="@(Data.Where(x => x.Category == category))"
                         Name="@category"
                         SeriesType="SeriesType.Line"
                         XValue="@(e => e.Date.ToString("yyyy-MM-dd"))"
                         YAggregate="@(e => e.Sum(e => e.Revenue))"
                         OrderBy="e=> e.X"  />
    }
</ApexChart>

@code {
    private List<MyData> Data { get; set; } = new();
    private ApexChartOptions<MyData> options;

    protected override void OnInitialized()
    {
        options = new ApexChartOptions<MyData> { Xaxis = new XAxis { Labels = new XAxisLabels { Format = "yyyy-MM-dd" } }};

        DateTime startDate = new DateTime(1969, 12, 20);      // renders
        //DateTime startDate = new DateTime(1969, 12, 22);    // renders

        //DateTime startDate = new DateTime(1969, 12, 23);    // no render
        //DateTime startDate = new DateTime(1969, 12, 24);    // no render
        //DateTime startDate = new DateTime(1969, 12, 25);    // no render
        //DateTime startDate = new DateTime(1969, 12, 26);    // no render
        //DateTime startDate = new DateTime(1969, 12, 27);    // no render
        //DateTime startDate = new DateTime(1969, 12, 28);    // no render
        //DateTime startDate = new DateTime(1969, 12, 29);    // no render
        //DateTime startDate = new DateTime(1969, 12, 30);    // no render
        //DateTime startDate = new DateTime(1969, 12, 31);    // no render
        //DateTime startDate = new DateTime(1970, 1, 1);      // no render

        //DateTime startDate = new DateTime(1970, 1, 2);      // renders

        for (int i = 0; i < 10; i++)
            Data.Add(new MyData { Category = "TRUCK", Date = startDate.AddDays(i), Revenue = i });
    }

    public class MyData
    {
        public string Category { get; set; }
        public DateTime Date { get; set; }
        public int? Revenue { get; set; }
    }
}

ChartTest.zip

joadan commented 8 months ago

Hi,

Apexchart.js uses Unix epoch, where January 1st, 1970 at 00:00:00 UTC is the starting point.

sam-wheat commented 8 months ago

Thank you @joadan - as noted I am able to render series prior to and after 1/1/70 - as long as 1/1/70 itself is not included in the series.. Is this behavior expected? There is a world of data that exists prior to and includes 1/1/70... is ApexCharts not suitable for this data?

joadan commented 8 months ago

Hi sorry I was a bit quick.. you can provide negative epoch values..

I update the https://apexcharts.github.io/Blazor-ApexCharts/line-charts#datetime like this and it seems to be working. Please note the method ToUnixTimeMilliseconds() set on the xvalues.

` <ApexChart TItem="Order" Title="Orders Value" XAxisType="XAxisType.Datetime" Options="options" Debug>

    <ApexPointSeries TItem="Order"
                     Items="SampleData.GetOrders()"
                     Name="Net Value"
                     SeriesType="SeriesType.Line"
                     XValue="@(e => e.OrderDate.FirstDayOfMonth().AddYears(-150).ToUnixTimeMilliseconds())"
                     YAggregate="@(e => e.Sum(e => e.NetValue))"
                     OrderBy="e=>e.X" />

    <ApexPointSeries TItem="Order"
                     Items="SampleData.GetOrders()"
                     Name="Gross Value"
                     SeriesType="SeriesType.Line"
                     XValue="@(e => e.OrderDate.FirstDayOfMonth().AddYears(-150).ToUnixTimeMilliseconds())"
                     YAggregate="@(e => e.Sum(e => e.GrossValue))"
                     OrderBy="e=>e.X" />
</ApexChart>

`

sam-wheat commented 8 months ago

Thank you @joadan the syntax below works. May I please suggest a bold note in the documentation. This is a a very subtle requirement and it manifests in a very unusual way. It took me a lot of time to find this because only data series with the exact date 1/1/1970 will not render. I also suggest that the literal data used to render the chart should not break the chart but that is just my opinion. Thanks again for your help.

 <ApexPointSeries TItem="MyData" 
              Items="@(Data.Where(x => x.Category == category))"
              Name="@category"
              SeriesType="SeriesType.Line"
              XValue="@(e => e.Date.ToUnixTimeMilliseconds())"
              YAggregate="@(e => e.Sum(e => e.Revenue))"
              OrderBy="e=> e.X"  />