ASP.NET MVC 按完整自然周分组当月数据时如何处理跨月周归属
跨月自然周分组统计代码改造方案
现有实现的核心局限
- 原逻辑直接将查询范围硬编码为当月1日至当月最后1日,完全覆盖不了跨月自然周的边界数据
- 原依赖的
GetWeekOfMonth()扩展方法会将同一个跨月自然周硬拆分到相邻两个月的分组中,不符合「按完整自然周统计、跨月周按天数占比判定归属」的规则
核心改造逻辑
按照需求规则,分组流程调整为三步:
- 先对目标月首尾两个跨月周做归属判定:
- 计算目标月1日所在自然周的周一作为周起点,统计该周内分属上月、当月的天数
- 若当月在该周的天数 > 上月天数,将整个跨月周归入当月统计范围;否则将该周整体划给上月,当月统计起点顺延到下一个自然周的周一
- 用同样规则计算目标月最后1日所在自然周的归属,动态调整当月的统计结束时间
- 按照动态计算的统计起止时间拉取数据库数据,不再硬锁自然月区间
- 分组时以完整自然周(周一首日为周标识)为单位分组,匹配预先判定好的归属月份,自动排序生成月内周序
改造后的可运行代码
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
相关产品推荐
相关产品推荐

