使用Chart.js绘制无年份时间轴:解决季节累计数据排序问题
问题描述
我想用Chart.js绘制每日累计总和折线图,每个季节(10月至次年9月)对应一条折线。数据存在PostgreSQL表中,结构如下:
date season cum_sum 2021-12-23 2022 11 2022-01-01 2022 19 2022-01-04 2022 20 2022-01-05 2022 40 2022-03-01 2022 43 2022-12-01 2023 3 2022-12-02 2023 7 2022-12-10 2023 11 2022-12-23 2023 17 2023-01-01 2023 19 2023-01-05 2023 30
当前遇到两个问题:
- x轴未按季节时间顺序排列:2023季中2022季没有的日期会被挤到x轴末尾
- 10-12月作为季初,却被显示在x轴末尾,顺序完全颠倒
当前使用的JS代码如下:
$( document ).ready(function() { // retrieve data from PHP as JSON object using fetch API fetch('./inc/get_data.php') .then(response => response.json()) .then(baseData => { const data_2022 = baseData['2022'].map(arr => ({ x: arr[0], y: parseFloat(arr[1])})); const data_2023 = baseData['2023'].map(arr => ({ x: arr[0], y: parseFloat(arr[1])})); // create chart var ctx = document.getElementById('myChart').getContext('2d'); var chart = new Chart(ctx, { type: 'line', data: { datasets: [{ label: 'Season 2022', data: data_2022, borderColor: 'blue', fill: false }, { label: 'Season 2023', data: data_2023, borderColor: 'red', fill: false }] }, options: { scales: { xAxes: [{ type: 'time', time: { unit: 'day', unitStepSize: 1, displayFormats: { 'day': 'MMM DD' }, tooltipFormat: 'MM-DD', } }], yAxes: [{ ticks: { beginAtZero: true } }] } } }); }) .catch(error => console.error(error)); });
解决方案
核心解决思路是把每个季节的日期转换为「季节基准虚拟日期」,让Chart.js把10月-次年9月当成一个连续的时间区间,而非按公历年份排序。具体步骤如下:
1. 前端预处理:转换日期为季节对齐格式
给每条数据的日期做年份替换:
- 如果原日期是10-12月,年份直接用所属季节的年份
- 如果原日期是1-9月,年份用「季节年份-1」
举个例子:
- 2021-12-23(属于2022季节)→ 转成
2022-12-23 - 2022-01-01(属于2022季节)→ 转成
2021-01-01
这样处理后,每个季节的日期都会形成「YYYY-10-01」到「YYYY+1-09-30」的连续区间,10-12月自然会排在1-9月前面。
修改JS中的数据处理逻辑:
// 日期转换工具函数 function convertToSeasonDate(dateStr, season) { const date = new Date(dateStr); const month = date.getMonth() + 1; // JS月份从0开始,转成1-12格式 let year = season; // 1-9月属于季节年份的前一年 if (month <= 9) { year -= 1; } // 构造新日期字符串,保持月日不变,替换年份 return `${year}-${String(month).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; } // 处理数据时应用转换 const data_2022 = baseData['2022'].map(arr => ({ x: convertToSeasonDate(arr[0], 2022), y: parseFloat(arr[1]) })); const data_2023 = baseData['2023'].map(arr => ({ x: convertToSeasonDate(arr[0], 2023), y: parseFloat(arr[1]) }));
2. 调整Chart.js的x轴配置
确保x轴按时间升序排列,并且只显示月日(隐藏虚拟年份):
options: { scales: { // 注意:如果用Chart.js v2版本,保持xAxes数组写法;v3+用x对象 x: { type: 'time', time: { unit: 'day', displayFormats: { day: 'MMM DD' }, tooltipFormat: 'MM-DD' }, parser: 'yyyy-MM-dd', // 明确日期解析格式,避免识别错误 reverse: false, // 强制升序排列 title: { display: true, text: '日期(季节:10月-次年9月)' } }, y: { ticks: { beginAtZero: true }, title: { display: true, text: '累计总和' } } } }
3. 可选:后端提前处理日期(更高效)
如果能修改PHP接口get_data.php,建议直接在PostgreSQL查询时完成日期转换,减少前端工作量:
SELECT CASE WHEN EXTRACT(MONTH FROM date) >= 10 THEN TO_CHAR(date, '"' || season || '"-"MM"-"DD"')::DATE ELSE TO_CHAR(date, '"' || (season - 1) || '"-"MM"-"DD"')::DATE END AS season_date, cum_sum, season FROM your_table ORDER BY season, season_date;
前端直接用返回的season_date即可,无需再做转换。
最终效果
处理后x轴会严格按季节时间顺序排列:10月→11月→12月→1月→2月→…→9月,不同季节的相同月日会自动对齐,缺失日期也不会乱跑到轴的末尾。
内容的提问来源于stack exchange,提问作者erik-stengel
相关产品推荐
相关产品推荐

