Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to group dates by week?

Tags:

c#

linq

grouping

I am writing an Excel exporter for a bespoke application I am creating, and I have a question about LINQ grouping in C#.

Basically, this new Excel exporter class is given two dates. The class then retrieves all consignments between this date range.

As part of this exporter, I need to be able to group the dates into weeks, and get the values for that week. So for example, if I'm given 07/12/2011 and 22/12/2011 (dd/MM/yyyy format), I need to group all consignments between them ranges into weeks (each week beginning with Sunday). The ideal result using the above dates would be

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)

Any ideas?

like image 622
Chris Avatar asked Dec 19 '11 13:12

Chris


People also ask

How do I sort weekly data in Excel?

Select a cell in the column you want to sort. On the Data tab, in the Sort & Filter group, click Sort. In the Sort dialog box, under Column, in the Sort by or Then by box, select the column that you want to sort by a custom list. Under Order, select Custom List.


3 Answers

The fundamental question here is how to project a DateTime instance into a week of year value. This can be done using by calling Calendar.GetWeekOfYear. So define the projection:

Func<DateTime, int> weekProjector = 
    d => CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
         d,
         CalendarWeekRule.FirstFourDayWeek,
         DayOfWeek.Sunday);

You can configure exactly how the "week number" is determined by tweaking the parameters in the method call. You can also decide to define the projection as e.g. an extension method if you prefer; this does not change the essence of the code. In any case, you are then ready to group by week:

var consignmentsByWeek = from con in consignments
                         group con by weekProjector(con.Date);

If you also want to constrain the output to consigments between two specific dates, just add an appropriate where clause; the grouping logic does not change.

like image 57
Jon Avatar answered Sep 30 '22 11:09

Jon


Hesitant though I am to disagree with as esteemed an answerer I believe the accepted answer here is wrong, and this is not fundamentally a question of projecting to a week of year value.

GetWeekOfYear(), and the concept in general, is about assigning index values to weeks within a year according to some agreed standard. It is not suitable for placing dates into groups of seven adjacent days as I believe the questioner requires.

Not only will use of GetWeekOfYear() as proposed result in groups of fewer than seven days at the end of many years, but worse still, as the various standards supported by GetWeekOfYear() will often apportion the first days of a year to the last week of the previous year, and yet the GetWeekOfYear() result contains only the integer week index with no reference to associated year, grouping by new { Year = date.Year, weekProjector(date) } or date.Year + "-" + weekProjector(date) in the questioner's year would see January 1st 2011 grouped in with Christmas Day through to New Year's Eve twelve months later that same year.

So I would argue that the original question is fundamentally one of projecting not to a week of year value but to a week of all time value, "week beginning y/m/d" you might say, so grouping need only be done by the first day of the week, i.e. (assuming you're happy to default to Sunday) simply:

group by date.AddDays(-(int)date.DayOfWeek)
like image 36
stovroz Avatar answered Sep 30 '22 09:09

stovroz


In addition to Jon's answer you can get the date of the first day in the week then group by that date.

To get the date of the first day in the week. you can use this code:

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;
    }
}

then you can group by the first date of the week like this:

var consignmentsByWeek = from con in consignments
                         group con by con.Datedate.StartOfWeek(DayOfWeek.Monday);
like image 23
hoss77 Avatar answered Sep 30 '22 11:09

hoss77