Search code examples
javajfreechartjava-time

JFreeChart: create a chart with java.time.LocalDate or java.time.LocalDateTime


java.util.Date is very error prone. It is dead. Long live java.time.*.

Given a Map<LocalDate, Integer> dateToCountMap, how do I create a JFreeChart chart that shows the count per date?


Solution

  • In the case of LocalDate, you can create a time series chart by constructing the corresponding Day, as shown below.

    LocalDate ld = entry.getKey();
    Day d = new Day(ld.getDayOfMonth(), ld.getMonthValue(), ld.getYear());
    series.add(d, entry.getValue());
    

    If you have relevant time zone data, you can use it when constructing the Day. A similar approach can be used for LocalDateTime and any desired concrete RegularTimePeriod. See also the approach shown here and here, given an Instant. Moreover, a custom implementation of XYDataset, seen here, can simply convert the result of toEpochMilli() and return it from getX().

    image

    
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.text.DateFormat;
    import java.time.LocalDate;
    import java.util.HashMap;
    import java.util.Map;
    import javax.swing.JFrame;
    import org.jfree.chart.ChartFactory;
    import org.jfree.chart.ChartPanel;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.axis.DateAxis;
    import org.jfree.chart.plot.XYPlot;
    import org.jfree.data.time.Day;
    import org.jfree.data.time.TimeSeries;
    import org.jfree.data.time.TimeSeriesCollection;
    import org.jfree.data.xy.XYDataset;
    
    /**
     * @see https://stackoverflow.com/a/66713994/230513
     * @see https://stackoverflow.com/a/12481509/230513
     */
    public class XYTest {
    
        private static final int N = 16;
    
        private XYDataset createDataset() {
            long t = LocalDate.now().toEpochDay();
            Map<LocalDate, Integer> dateToCountMap = new HashMap<>();
            for (int i = 0; i < N; i++) {
                dateToCountMap.put(LocalDate.ofEpochDay(t + i), (int) Math.pow(i, 1.61));
            }
            TimeSeries series = new TimeSeries("Data)");
            for (Map.Entry<LocalDate, Integer> entry : dateToCountMap.entrySet()) {
                LocalDate ld = entry.getKey();
                Day d = new Day(ld.getDayOfMonth(), ld.getMonthValue(), ld.getYear());
                series.add(d, entry.getValue());
            }
            return new TimeSeriesCollection(series);
        }
    
        private JFreeChart createChart(final XYDataset dataset) {
            JFreeChart chart = ChartFactory.createTimeSeriesChart(
                "Test", "Day", "Value", dataset, false, false, false);
            XYPlot plot = (XYPlot) chart.getPlot();
            DateAxis domain = (DateAxis) plot.getDomainAxis();
            domain.setDateFormatOverride(DateFormat.getDateInstance());
            return chart;
        }
    
        static void create() {
            JFrame frame = new JFrame("Bar Chart");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            XYTest xyTest = new XYTest();
            XYDataset dataset = xyTest.createDataset();
            JFreeChart chart = xyTest.createChart(dataset);
            ChartPanel chartPanel = new ChartPanel(chart) {
    
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(800, 300);
                }
            };
            frame.add(chartPanel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(XYTest::create);
        }
    }