我来介绍一些 LINQ 中关于时间分组的高级用法和常见场景。
using System;
using System.Linq;
using System.Collections.Generic;
public class Order
{
public int Id { get; set; }
public DateTime OrderTime { get; set; }
public decimal Amount { get; set; }
}
public class Program
{
public static void Main()
{
// 示例数据
var orders = new List<Order>
{
new Order { Id = 1, OrderTime = new DateTime(2024, 1, 1, 10, 30, 0), Amount = 100 },
new Order { Id = 2, OrderTime = new DateTime(2024, 1, 1, 14, 20, 0), Amount = 200 },
new Order { Id = 3, OrderTime = new DateTime(2024, 1, 2, 9, 15, 0), Amount = 150 },
new Order { Id = 4, OrderTime = new DateTime(2024, 2, 1, 11, 45, 0), Amount = 300 }
};
// 1. 按天分组
var dailyGroups = orders.GroupBy(o => o.OrderTime.Date)
.Select(g => new
{
Date = g.Key,
Orders = g.ToList(),
TotalAmount = g.Sum(o => o.Amount)
});
// 2. 按月分组
var monthlyGroups = orders.GroupBy(o => new { o.OrderTime.Year, o.OrderTime.Month })
.Select(g => new
{
Year = g.Key.Year,
Month = g.Key.Month,
Orders = g.ToList(),
TotalAmount = g.Sum(o => o.Amount)
});
// 3. 按小时段分组(例如:按4小时一段)
var hourlyGroups = orders.GroupBy(o => new
{
Date = o.OrderTime.Date,
TimeSlot = o.OrderTime.Hour / 4
})
.Select(g => new
{
Date = g.Key.Date,
TimeSlot = $"{g.Key.TimeSlot * 4:D2}:00-{(g.Key.TimeSlot + 1) * 4:D2}:00",
Orders = g.ToList(),
TotalAmount = g.Sum(o => o.Amount)
});
// 4. 按周分组
var weeklyGroups = orders.GroupBy(o => new
{
Year = o.OrderTime.Year,
Week = GetIso8601WeekOfYear(o.OrderTime)
})
.Select(g => new
{
Year = g.Key.Year,
Week = g.Key.Week,
Orders = g.ToList(),
TotalAmount = g.Sum(o => o.Amount)
});
// 5. 按季度分组
var quarterlyGroups = orders.GroupBy(o => new
{
o.OrderTime.Year,
Quarter = (o.OrderTime.Month - 1) / 3 + 1
})
.Select(g => new
{
Year = g.Key.Year,
Quarter = g.Key.Quarter,
Orders = g.ToList(),
TotalAmount = g.Sum(o => o.Amount)
});
// 6. 动态时间窗口分组(例如:每30分钟一组)
var windowSize = TimeSpan.FromMinutes(30);
var timeWindowGroups = orders.GroupBy(o =>
new DateTime(o.OrderTime.Ticks - (o.OrderTime.Ticks % windowSize.Ticks)))
.Select(g => new
{
WindowStart = g.Key,
WindowEnd = g.Key.Add(windowSize),
Orders = g.ToList(),
TotalAmount = g.Sum(o => o.Amount)
});
}
// 计算ISO 8601周数的辅助方法
private static int GetIso8601WeekOfYear(DateTime date)
{
var thursday = date.AddDays(3 - ((int)date.DayOfWeek + 6) % 7);
return (thursday.DayOfYear - 1) / 7 + 1;
}
}
这个代码示例展示了多种 LINQ 时间分组的高级用法:
-
按天分组
- 使用
Date
属性去除时间部分 - 适合统计每日订单总量、销售额等
- 使用
-
按月分组
- 使用匿名类型组合年份和月份
- 避免不同年份同月混在一起
-
按时间段分组
- 将24小时划分为多个时间段
- 适合分析业务高峰期
-
按周分组
- 使用 ISO 8601 标准计算周数
- 适合周报表生成
-
按季度分组
- 通过月份计算季度
- 适合季度报表
-
动态时间窗口
- 自定义时间间隔
- 适合实时数据分析
你可以根据业务需求选择合适的分组方式。每种分组方法都包含了:
- 分组键的定义
- 结果的投影
- 聚合计算(如总金额)
需要注意的是:
- 时区处理:实际应用中需要考虑时区转换
- 性能优化:大数据量时考虑添加适当的索引
- 内存使用:注意
ToList()
的使用时机