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

ASP.NET MVC 按完整自然周分组当月数据时如何处理跨月周归属

跨月自然周分组统计代码改造方案

现有实现的核心局限

  • 原逻辑直接将查询范围硬编码为当月1日至当月最后1日,完全覆盖不了跨月自然周的边界数据
  • 原依赖的GetWeekOfMonth()扩展方法会将同一个跨月自然周硬拆分到相邻两个月的分组中,不符合「按完整自然周统计、跨月周按天数占比判定归属」的规则

核心改造逻辑

按照需求规则,分组流程调整为三步:

  1. 先对目标月首尾两个跨月周做归属判定:
    • 计算目标月1日所在自然周的周一作为周起点,统计该周内分属上月、当月的天数
    • 若当月在该周的天数 > 上月天数,将整个跨月周归入当月统计范围;否则将该周整体划给上月,当月统计起点顺延到下一个自然周的周一
    • 用同样规则计算目标月最后1日所在自然周的归属,动态调整当月的统计结束时间
  2. 按照动态计算的统计起止时间拉取数据库数据,不再硬锁自然月区间
  3. 分组时以完整自然周(周一首日为周标识)为单位分组,匹配预先判定好的归属月份,自动排序生成月内周序

改造后的可运行代码

public ActionResult DisplayChart()
{
    // 保留原有目标月取数逻辑:取当前时间往前推3天所在的自然月
    DateTime now = DateTime.Now.AddDays(-3);
    var targetYear = now.Year;
    var targetMonth = now.Month;
    var targetMonthFirstDay = new DateTime(targetYear, targetMonth, 1);
    var targetMonthLastDay = targetMonthFirstDay.AddMonths(1).AddDays(-1);

    // ---------------------- 首跨月周归属判定 ----------------------
    // 计算目标月1日所在自然周的周一首日
    int offsetToMonday = DayOfWeek.Monday - targetMonthFirstDay.DayOfWeek;
    if (offsetToMonday > 0) offsetToMonday -= 7; // 处理周日的偏移场景
    var firstCrossWeekStart = targetMonthFirstDay.AddDays(offsetToMonday);

    int daysInPreviousMonthOfCrossWeek = (targetMonthFirstDay - firstCrossWeekStart).Days;
    int daysInTargetMonthOfCrossWeek = 7 - daysInPreviousMonthOfCrossWeek;

    DateTime queryStart;
    bool isFirstCrossWeekBelongToTarget = daysInTargetMonthOfCrossWeek > daysInPreviousMonthOfCrossWeek;
    if (isFirstCrossWeekBelongToTarget)
    {
        // 跨月周归属当月,从该周周一开始统计
        queryStart = firstCrossWeekStart;
    }
    else
    {
        // 跨月周归属上月,从下一个完整自然周周一开始统计
        queryStart = firstCrossWeekStart.AddDays(7);
    }

    // ---------------------- 尾跨月周归属判定 ----------------------
    // 计算目标月最后1日所在自然周的周日为周末日
    int offsetToSunday = DayOfWeek.Sunday - targetMonthLastDay.DayOfWeek;
    var lastCrossWeekEnd = targetMonthLastDay.AddDays(offsetToSunday).Date;

    int daysInNextMonthOfCrossWeek = (lastCrossWeekEnd - targetMonthLastDay).Days;
    int daysInTargetMonthOfLastCrossWeek = 7 - daysInNextMonthOfCrossWeek;

    DateTime queryEnd;
    bool isLastCrossWeekBelongToTarget = daysInTargetMonthOfLastCrossWeek > daysInNextMonthOfCrossWeek;
    if (isLastCrossWeekBelongToTarget)
    {
        // 跨月周归属当月,统计到该周周日
        queryEnd = lastCrossWeekEnd;
    }
    else
    {
        // 跨月周归属下月,统计到上一个完整自然周周日
        queryEnd = lastCrossWeekEnd.AddDays(-7);
    }

    // ---------------------- 数据拉取与分组 ----------------------
    var result = db.Chats
        .Where(c => System.Data.Entity.DbFunctions.TruncateTime(c.MSTChatCreatedDateTime) >= queryStart
                 && System.Data.Entity.DbFunctions.TruncateTime(c.MSTChatCreatedDateTime) <= queryEnd)
        .Select(x => new { x.MSTChatCreatedDateTime })
        .ToList()
        .GroupBy(c =>
        {
            var cleanDate = c.MSTChatCreatedDateTime.ToCleanDateTime();
            // 计算当前日期所属自然周的周一首日
            int weekOffset = DayOfWeek.Monday - cleanDate.DayOfWeek;
            if (weekOffset > 0) weekOffset -= 7;
            var currentWeekStart = cleanDate.AddDays(weekOffset).Date;

            // 匹配周归属的统计月份
            int groupYear = targetYear;
            int groupMonth = targetMonth;
            // 首跨月周若判定归属于上月,同步修正分组的年、月
            if (!isFirstCrossWeekBelongToTarget && currentWeekStart == firstCrossWeekStart)
            {
                groupMonth = targetMonth == 1 ? 12 : targetMonth - 1;
                groupYear = targetMonth == 1 ? targetYear - 1 : targetYear;
            }
            return new
            {
                GroupYear = groupYear,
                GroupMonth = groupMonth,
                WeekStart = currentWeekStart
            };
        })
        // 按自然周起始时间排序,自动生成月内周序
        .OrderBy(g => g.Key.WeekStart)
        .Select((g, weekIndex) => new ReportVM
        {
            Title = string.Format("{0}/{1}/Week{2}", g.Key.GroupYear, g.Key.GroupMonth, weekIndex + 1),
            ChatCountCreatdDate = g.Count()
        })
        .ToList();

    return View(result);
}

方案匹配验证

  • 对应2021年4月场景:4月1日为周四,所在自然周起点为3月29日,周内3月占3天、4月占4天,判定归属4月,queryStart设为3月29日,整个周纳入4月首周统计,完全匹配示例要求
  • 对应5月的场景:若5月1日所在跨月周内4月占5天、5月占2天,判定该周归属4月,queryStart顺延到该跨月周之后的第一个周一,该周数据不纳入5月统计范围

内容的提问来源于stack exchange,提问作者Riyaz Shaikh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 22:36:04