C# 从DateTime对象集合中解析每月项目

C# 从DateTime对象集合中解析每月项目,c#,collections,group-by,C#,Collections,Group By,我以前尝试过选择datetime对象,并按dayOfweek和time对它们进行分组 简单地说一下:我收集了一些DateTime List<DateTime> collectionOfDateTime = GetDateColletion(); 因此,在这一点上,我已经将这些按周分组(一致时间)工作得很好 我现在有一个新的要求,按月分组,而不是按周分组。当我说“月”时,它不是一个月的同一天,而是类似于“每个月的第一个星期二” 我试图找出在分组中使用什么“键”,以便将符合每月逻辑的所

我以前尝试过选择datetime对象,并按dayOfweek和time对它们进行分组

简单地说一下:我收集了一些DateTime

List<DateTime> collectionOfDateTime = GetDateColletion();
因此,在这一点上,我已经将这些按周分组(一致时间)工作得很好

我现在有一个新的要求,按月分组,而不是按周分组。当我说“月”时,它不是一个月的同一天,而是类似于“每个月的第一个星期二”

我试图找出在分组中使用什么“键”,以便将符合每月逻辑的所有项目分组(每月的第一个星期二,每月的第二个星期五,等等)

作为一个例子,让我们假设我有这些日期开始

var date1 = new DateTime(2013, 1, 4).AddHours(8);  // This is the first Friday in Jan
var date2 = new DateTime(2013, 2, 1).AddHours(8);  // This is the first Friday in Feb
var date3 = new DateTime(2013, 1, 5).AddHours(3);  // This is the first Sat in Jan
var date4 = new DateTime(2013, 2, 2).AddHours(3);  // This is the first Sat in Feb
var date5 = new DateTime(2013, 2, 2).AddHours(6);  // This is the first Sat in Feb - different time
如果这些是进入原始数组的日期,我需要一个groupby以3个组结束

  • 第一个组将包含date1和date2
  • 第二组将包含date3和date4
  • date5将是独立的,因为它与给定不同时间的任何其他组都不匹配

有谁能建议按照这个标准分组吗?

可能有一种更简单的方法,但我想到的是:

我从你的问题中得出结论,你需要将“2月的第一个星期二到3月的第一个星期一”等所有内容分组,这样你就可以得到这些“月”跨度,这些跨度的天数是可变的,取决于开始的月份。如果是这样的话,那么您确实需要使用一年中的某一天将其分解为多个范围,以便:

Group by the First Wednesday of the Month 2013
Group 0 (0-1) 
All DayOfYear between 0 and 1 2013
Group 1 (2-36) 
The first Wednesday of the month: January is DayOfYear 2. 
The first Wednesday of the month: February is DayOfYear 37. 
etc.
第一个范围是函数f,f(32)=1(DayOfYear是32),因为它在2到37的范围内。这个f是一个范围的索引集合,在集合中查找给定DayOfYear所属的项,并将该项的索引作为组号返回

您可以通过从
GetDateCollection
获取最小和最大日期来动态构建此表,以确定总体范围。由于围绕日期的逻辑本身是一个相当复杂的主题,我会选择一个库(特别是文档),从最小日期开始,逐日前进,直到找到第一个符合条件的日期(即“本月的第一个星期一”)并创建一个从0到那天的范围-1作为组0,并将其推送到一个索引集合(
ArrayList
可能)。然后从该日期开始循环,直到月份发生变化,构造一个新范围并将该范围推送到同一索引集合上

每当你跨入一个新的一年,你将不得不在你的DayOfYear中添加365(如果上一年是闰年,则为366),因为DayOfYear每年都会重置

现在,您已经有了一个范围集合,它充当了一个表,根据日期将日期分组为所需的单位

编写一个函数,将给定日期的DayOfYear(
+[365 | 366]*x
,其中x是从最小年份开始比较的日期的年数)与集合中的项目进行比较,直到找到该日期所在的范围,并将该项目的索引作为组号返回。(或者,每个范围可以是一个
Func
,如果提供的
DateTime
在该范围内,则返回true。)


范围集合的另一种数据结构是一个ushort数组,其长度等于日期范围内从最小日期到最大日期的所有日期,以及每天分配的组号的值(使用范围计算,如上所述)。这将在分组时执行得更快,但如果使用较小的数据集(只有几百个日期),性能可能不会明显提高。

要使用Linq进行分组,此代码可能会帮助您:

List<DateTime> collectionOfDateTime = GetDateColletion();
collectionOfDateTime.GroupBy(s => Convert.ToInt16(s.DayOfWeek) & s.Hour & s.Minute);
List collectionOfDateTime=getdatecollection();
collectionOfDateTime.GroupBy(s=>Convert.ToInt16(s.DayOfWeek)和s.Hour&s.Minute);

我认为这比看起来容易:

var byDayOfMonth = from d in dates
                   let h = (d.Day / 7) + 1
                   group d by new { d.DayOfWeek, h } into g
                   select g;
局部变量
h=(d.Day/7)+1
设置当月的
DayOfWeek

我运行它进行测试,收到了两组,与您的示例中完全相同。这些组的键是:

{ DayOfWeek = Friday, h = 1 }
{ DayOfWeek = Saturday, h = 1 }
也就是说,有“每月第一个星期五”和“每月第一个星期六”的小组

如果愿意,您可以通过
d.Hour
和/或
d.Minute
轻松扩展分组键:

var byDayOfMonth = from d in dates
                   let h = (d.Day / 7) + 1
                   group d by new { d.DayOfWeek, h, d.Hour, d.Minute } into g
                   select g;
结果(仅限钥匙):


这个答案是如何按月份分组的?我在问题中添加了一个示例,使其更加明确。前提仍然是一样的,但不是将按
DayOfYear
索引的数组与上面计算的“第一个星期六”或“第三个星期一”值相关联。(这比范围更容易计算。)然后为了满足不同的时间示例,您按小时对每个组进行分组。
var byDayOfMonth = from d in dates
                   let h = (d.Day / 7) + 1
                   group d by new { d.DayOfWeek, h, d.Hour, d.Minute } into g
                   select g;
{ DayOfWeek = Friday, h = 1, Hour = 8, Minute = 0 }
{ DayOfWeek = Saturday, h = 1, Hour = 3, Minute = 0 }
{ DayOfWeek = Saturday, h = 1, Hour = 6, Minute = 0 }