React仪表板时间范围数据过滤及日期无效问题排查
问题描述
我正在开发一个React仪表板,需要按**日(Dia)、周(Semana)、月(Mês)、学期(Semestre)、年(Ano)**等多种时间范围过滤数据,要求过滤后的数据从指定范围的起始点开始,到现有最旧数据点结束。但在渲染图表时,遇到了日期显示为Invalid Date的问题,当前实现代码如下:
原始数据示例
const data = [ { created_at: '2024-06-10T10:00:00Z', temperature: 25, humidity: 40 }, { created_at: '2024-06-09T10:00:00Z', temperature: 26, humidity: 42 }, // 更多数据点... ];
查找最旧数据函数
const findOldestData = (data) => { if (!data || data.length === 0) return null; return data.reduce((oldest, record) => { const recordDate = new Date(record.created_at); return recordDate < oldest ? recordDate : oldest; }, new Date()); };
过滤函数
const filterDataByRange = (data, range) => { if (!data || data.length === 0) return []; const now = new Date(); const oldestDate = findOldestData(data); if (!oldestDate) return []; let startDate; switch (range) { case 'Dia': startDate = new Date(now); startDate.setHours(0, 0, 0, 0); break; case 'Semana': startDate = new Date(now); startDate.setDate(now.getDate() - 7); startDate.setHours(0, 0, 0, 0); break; case 'Mês': startDate = new Date(now.getFullYear(), now.getMonth(), 1); startDate.setHours(0, 0, 0, 0); break; case 'Semestre': startDate = new Date(now.getFullYear(), Math.floor(now.getMonth() / 6) * 6, 1); startDate.setHours(0, 0, 0, 0); break; case 'Ano': startDate = new Date(now.getFullYear(), 0, 1); startDate.setHours(0, 0, 0, 0); break; default: startDate = oldestDate; } const endDate = new Date(); const filteredData = []; let currentDate = new Date(startDate); while (currentDate <= endDate) { const foundRecord = data.find(record => { const recordDate = new Date(record.created_at); return ( recordDate.getFullYear() === currentDate.getFullYear() && recordDate.getMonth() === currentDate.getMonth() && recordDate.getDate() === currentDate.getDate() ); }); if (foundRecord) { filteredData.push({ created_at: currentDate.toISOString(), data: foundRecord }); } else { filteredData.push({ created_at: currentDate.toISOString(), data: { temperature: null, humidity: null, // 根据需要添加其他传感器字段 } }); } currentDate.setDate(currentDate.getDate() + 1); } return filteredData; };
图表渲染代码
return ( <div> {/* ... 其他组件 */} {Object.keys(filteredData).map((key) => ( <ChartComponent key={key} data={filteredData.map(entry => ({ created_at: new Date(entry.created_at), value: entry.data[key] }))} label={key} /> ))} </div> ); };
问题根源与修复方案
1. 最旧数据查找函数逻辑错误
findOldestData初始值设为当前时间new Date(),若存在未来日期的数据会导致计算偏差;同时未校验created_at的有效性,无效日期字符串会生成Invalid Date破坏比较逻辑。
修复代码:
const findOldestData = (data) => { if (!data || data.length === 0) return null; return data.reduce((oldest, record) => { const recordDate = new Date(record.created_at); // 跳过无效日期 if (isNaN(recordDate.getTime())) return oldest; // 初始值设为第一个有效日期 if (oldest === null) return recordDate; // UTC比较避免时区偏差 return recordDate.getUTCFullYear() < oldest.getUTCFullYear() || (recordDate.getUTCFullYear() === oldest.getUTCFullYear() && recordDate.getUTCMonth() < oldest.getUTCMonth()) || (recordDate.getUTCFullYear() === oldest.getUTCFullYear() && recordDate.getUTCMonth() === oldest.getUTCMonth() && recordDate.getUTCDate() < oldest.getUTCDate()) ? recordDate : oldest; }, null); };
2. 过滤函数的日期范围与需求不符
原代码将endDate设为当前时间,与“到最旧数据点结束”的需求矛盾;同时未限制startDate不早于最旧数据,会生成大量无意义的空数据。
修复逻辑:
- 计算数据中的最新日期作为结束点
- 确保
startDate不早于最旧数据、不晚于最新数据 - 统一使用UTC时间处理避免时区偏差
3. 图表渲染的循环逻辑错误
Object.keys(filteredData)返回的是数组索引,而非temperature/humidity等字段,导致entry.data[key]无法获取正确值,同时循环逻辑完全错误。
修复代码:
// 定义要渲染的指标字段 const metrics = ['temperature', 'humidity']; return ( <div> {/* ... 其他组件 */} {metrics.map((metric) => ( <ChartComponent key={metric} data={filteredData.map(entry => { const date = new Date(entry.created_at); return { created_at: isNaN(date.getTime()) ? null : date, value: entry.data[metric] }; })} label={metric} /> ))} </div> );
4. 全链路日期有效性校验
在所有创建Date实例的环节添加校验,避免Invalid Date流入后续逻辑:
- 过滤数据时跳过无效日期的记录
- 渲染图表时对日期有效性做二次校验
完整修复后的过滤函数示例
const findOldestData = (data) => { if (!data || data.length === 0) return null; return data.reduce((oldest, record) => { const recordDate = new Date(record.created_at); if (isNaN(recordDate.getTime())) return oldest; if (oldest === null) return recordDate; return recordDate.getUTCFullYear() < oldest.getUTCFullYear() || (recordDate.getUTCFullYear() === oldest.getUTCFullYear() && recordDate.getUTCMonth() < oldest.getUTCMonth()) || (recordDate.getUTCFullYear() === oldest.getUTCFullYear() && recordDate.getUTCMonth() === oldest.getUTCMonth() && recordDate.getUTCDate() < oldest.getUTCDate()) ? recordDate : oldest; }, null); }; const filterDataByRange = (data, range) => { if (!data || data.length === 0) return []; const now = new Date(); const oldestDate = findOldestData(data); if (!oldestDate) return []; // 获取数据中的最新日期 const latestDate = data.reduce((latest, record) => { const recordDate = new Date(record.created_at); if (isNaN(recordDate.getTime())) return latest; if (latest === null) return recordDate; return recordDate > latest ? recordDate : latest; }, null); if (!latestDate) return []; let startDate; switch (range) { case 'Dia': startDate = new Date(now); startDate.setUTCHours(0, 0, 0, 0); break; case 'Semana': startDate = new Date(now); startDate.setUTCDate(now.getUTCDate() - 7); startDate.setUTCHours(0, 0, 0, 0); break; case 'Mês': startDate = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)); break; case 'Semestre': const semesterStartMonth = Math.floor(now.getUTCMonth() / 6) * 6; startDate = new Date(Date.UTC(now.getUTCFullYear(), semesterStartMonth, 1)); break; case 'Ano': startDate = new Date(Date.UTC(now.getUTCFullYear(), 0, 1)); break; default: startDate = oldestDate; } // 确保起始点在有效数据范围内 startDate = new Date(Math.max(startDate.getTime(), oldestDate.getTime())); const endDate = latestDate; const filteredData = []; let currentDate = new Date(startDate); while (currentDate <= endDate) { const foundRecord = data.find(record => { const recordDate = new Date(record.created_at); if (isNaN(recordDate.getTime())) return false; return ( recordDate.getUTCFullYear() === currentDate.getUTCFullYear() && recordDate.getUTCMonth() === currentDate.getUTCMonth() && recordDate.getUTCDate() === currentDate.getUTCDate() ); }); if (foundRecord) { filteredData.push({ created_at: currentDate.toISOString(), data: foundRecord }); } else { filteredData.push({ created_at: currentDate.toISOString(), data: { temperature: null, humidity: null, // 根据需要添加其他传感器字段 } }); } currentDate.setUTCDate(currentDate.getUTCDate() + 1); } return filteredData; };
内容的提问来源于stack exchange,提问作者bern_code
相关产品推荐
相关产品推荐

