如何按周分组约会
本文关键字:约会 何按周 | 更新日期: 2023-09-27 18:18:21
我正在为我正在创建的定制应用程序编写Excel导出程序,并且我有一个关于c#中的LINQ分组的问题。
基本上,这个新的Excel导出类有两个日期。该类然后检索此日期范围内的所有货物。作为该导出程序的一部分,我需要能够将日期分组为周,并获得该周的值。例如,如果我的日期是2011年12月7日和2011年12月22日(dd/MM/yyyy格式),我需要将它们之间的所有货物分组为周(每周从周日开始)。使用上述日期的理想结果是
Week 1: (consignments between 04/12/2011 and 10/12/2011)
Week 2: (consignments between 11/12/2011 and 17/12/2011)
Week 3: (consignments between 18/11/2011 and 24/12/2011)
任何想法?
这里的基本问题是如何将DateTime
实例投影到一年中的一周值中。这可以通过调用Calendar.GetWeekOfYear
来完成。所以定义投影:
Func<DateTime, int> weekProjector =
d => CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
d,
CalendarWeekRule.FirstFourDayWeek,
DayOfWeek.Sunday);
您可以通过调整方法调用中的参数来精确地配置"周数"是如何确定的。如果您喜欢,您也可以决定将投影定义为例如扩展方法;这不会改变代码的本质。在任何情况下,您都可以按周分组:
var consignmentsByWeek = from con in consignments
group con by weekProjector(con.Date);
如果您还想将输出限制为两个特定日期之间的发货,只需添加适当的where
子句;分组逻辑不变
虽然我不同意作为一个受人尊敬的回答,但我认为这里公认的答案是错误的,这根本不是一个预测一年中的一周价值的问题。
GetWeekOfYear(),以及一般的概念,是根据某种商定的标准将索引值分配给一年内的周。不适合按照提问者的要求,将日期分成相邻的七天。
不仅在多年结束时少于7天的组中使用GetWeekOfYear()作为建议的结果,而且更糟糕的是,因为GetWeekOfYear()支持的各种标准通常会将一年的第一天分配到前一年的最后一周,而GetWeekOfYear()结果只包含整数周索引,没有引用相关的年份。按new { Year = date.Year, weekProjector(date) }
或date.Year + "-" + weekProjector(date)
在提问者的年份分组,将看到2011年1月1日与圣诞节一起分组,直到同年12个月后的新年前夕。
所以我认为最初的问题基本上是将不是投射到一年中的一周值,而是投射到所有时间值的周,你可能会说"从y/m/d开始的一周",所以分组只需要在一周的第一天完成,即(假设你很乐意默认为周日)简单地:
group by date.AddDays(-(int)date.DayOfWeek)
除了 Jon's
答案,您还可以获得一周第一天的日期,然后按该日期分组。
获取一周第一天的日期。您可以使用以下代码:
public static class DateTimeExtensions
{
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
int diff = dt.DayOfWeek - startOfWeek;
if (diff < 0)
{
diff += 7;
}
return dt.AddDays(-1 * diff).Date;
}
}
那么你可以按一周的第一个日期分组,像这样:
var consignmentsByWeek = from con in consignments
group con by con.Datedate.StartOfWeek(DayOfWeek.Monday);
我试过这样做(它正在工作:))
@foreach (var years in _dateRange.GroupBy(y => y.Year))
{
<p>@years.Key</p>
foreach (var months in years.GroupBy(m => m.Month))
{
<p>@CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(months.Key)</p>
foreach (var weeks in months.GroupBy(w => w.AddDays(-(int)w.DayOfWeek)))
{
<p>@weeks.Key.ToString("dd-MMM-yy")</p>
}
}
}
我注意到OP在理想输出中有第1周,第2周等。这不是一年中的一周,而是"指数"。根据寄售日期显示的星期。基于已经提供的其他一些答案,以下是我的解决方案:
void DoExample()
{
//Load some sample data
var range = new List<DateTime>();
var curDate = DateTime.ParseExact("07/12/2011", "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture);
var maxDate = DateTime.ParseExact("22/12/2011", "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture);
while(curDate < maxDate)
{
range.Add(curDate);
curDate = curDate.AddDays(1);
}
//Run the method to get the consignments
var c = GetConsignments(range, DayOfWeek.Sunday);
//Output to match OP's "ideal" specs
foreach(var v in c)
{
Console.WriteLine($"Week {v.EntryIndex + 1} (number {v.WeekOfYear} in year): (consignments between {v.RangeStart:dd/MM/yyyy} and {v.RangeEnd:dd/MM/yyyy}). Actual date range is {v.RangeStart:dd/MM/yyyy}-{v.RangeEnd:dd/MM/yyyy} ({(v.FullWeek ? "Full" : "Partial")} week)");
}
//Most other answers place a lot of value on the week of the year, so this would include that.
// Also includes the actual date range contained in the set and whether all dates in that week are present
foreach (var v in c)
{
Console.WriteLine($"Week {v.EntryIndex + 1} (number {v.WeekOfYear} in year): (consignments between {v.RangeStart} and {v.RangeEnd})");
}
}
//Note that this lets us pass in what day of the week is the start.
// Not part of OP's requirements, but provides added flexibility
public List<ConsignmentRange> GetConsignments(IEnumerable<DateTime>consignments, DayOfWeek startOfWeek=DayOfWeek.Sunday)
{
return consignments
.OrderBy(v => v)
.GroupBy(v => v.AddDays(-(int)((7 - (int)startOfWeek) + (int)v.DayOfWeek) % 7))
.Select((v, idx) => new ConsignmentRange
{
//These are part of the OP's requirement
EntryIndex = idx,
RangeStart = v.Key, // part of requirement
RangeEnd = v.Key.AddDays(6), // part of requirement
//These are added as potentially useful
WeekOfYear = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
v.Key, CalendarWeekRule.FirstFourDayWeek, startOfWeek),
FirstDate = v.Min(),
LastDate = v.Max(),
FullWeek = (v.Distinct().Count() == 7)
}
)
.ToList();
}
我们还需要定义这个类(或它的一个子集,取决于你想包含什么数据):
public class ConsignmentRange
{
public int EntryIndex;
public int WeekOfYear;
public bool FullWeek;
public DateTime FirstDate;
public DateTime LastDate;
public DateTime RangeStart;
public DateTime RangeEnd;
}