如何按周分组约会

本文关键字:约会 何按周 | 更新日期: 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;
}