You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 14:37:04