2011-12-19 27 views
15

Estoy escribiendo un exportador de Excel para una aplicación a medida que estoy creando, y tengo una pregunta sobre la agrupación LINQ en C#.¿Cómo se agrupan las fechas por semana?

Básicamente, esta nueva clase de exportador de Excel tiene dos fechas. La clase luego recupera todos los envíos entre este rango de fechas.

Como parte de este exportador, debo ser capaz de agrupar las fechas en semanas y obtener los valores para esa semana. Entonces, por ejemplo, si me dan el 07/12/2011 y el 22/12/2011 (formato dd/MM/aaaa), debo agrupar todos los envíos entre ellos en semanas (cada semana a partir del domingo). El resultado ideal usando las fechas anteriores sería

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) 

¿Alguna idea?

Respuesta

28

La pregunta fundamental aquí es cómo proyectar una instancia de DateTime en un valor de la semana del año. Esto se puede hacer usando llamando al Calendar.GetWeekOfYear. Por lo tanto definir la proyección:

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

se puede configurar exactamente cómo el "número de la semana" se determina mediante la deformación de los parámetros en la llamada al método. También puede decidir definir la proyección, como p. un método de extensión si lo prefiere; esto no cambia la esencia del código. En cualquier caso, ya está listo para agrupar por semana:

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

Si también desea limitar la salida de consignaciones entre dos fechas específicas, sólo tiene que añadir una cláusula apropiada where; la lógica de agrupamiento no cambia.

21

Vacilante, aunque estoy en desacuerdo con un estimado respondedor, creo que la respuesta aceptada aquí es incorrecta, y esto no es fundamentalmente una cuestión de proyectar a una semana del valor del año.

GetWeekOfYear(), y el concepto en general, se trata de asignar valores de índice a semanas dentro de un año de acuerdo con algún estándar acordado. No es adecuado para colocar las fechas en grupos de siete días adyacentes, ya que creo que el interrogador lo requiere.

No solo se utilizará GetWeekOfYear() como resultado propuesto en grupos de menos de siete días al final de muchos años, sino que aún peor, ya que los diversos estándares admitidos por GetWeekOfYear() a menudo distribuirán los primeros días de un año a la última semana del año anterior, y sin embargo el resultado GetWeekOfYear() contiene solo el índice de semana entero sin referencia al año asociado, agrupando por new { Year = date.Year, weekProjector(date) } o date.Year + "-" + weekProjector(date) en el año del interrogador vería el 1 de enero de 2011 agrupado con el día de Navidad a la víspera de Año Nuevo doce meses después ese mismo año.

Así que yo diría que la pregunta original es fundamentalmente uno de proyectar no a una semana del valor del año, pero a un semana de todo valor del tiempo, "semana del A/M/D" se podría decir, por lo que la agrupación sólo tiene que ser hecho por el primer día de la semana, es decir, (suponiendo que estés feliz por defecto a domingo) simplemente:

group by date.AddDays(-(int)date.DayOfWeek) 
+0

llegué a esta pregunta, porque ya tenía el código que se parecía mucho a la respuesta aceptada. No me gustó por las mismas razones que usted dice. Este código es más simple y hace un mejor trabajo al segmentar los días. ¡Gracias! Además, puede segmentar usando un día diferente de la semana (como los lunes) simplemente agregando ese día a este resultado. – user1304444

+0

Modifiqué la sugerencia en la respuesta (gran respuesta por cierto) para "ajustar" la fecha y hora al período más cercano. 'group by date.AddDays (((int) date.DayOfWeek) <4? (- (int) date.DayOfWeek): (- (int) date.DayOfWeek + 7))' – g2server

1

Además de Jon's respuesta se puede obtener la fecha de la primera día en la semana, luego agrupe para esa fecha.

Para obtener la fecha del primer día de la semana. puede utilizar este código:

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

continuación, se pueden agrupar por la primera fecha de la semana así:

var consignmentsByWeek = from con in consignments 
         group con by con.Datedate.StartOfWeek(DayOfWeek.Monday); 
Cuestiones relacionadas