year week the number español c# .net date-arithmetic

c# - the - week number excel



Error en el cálculo de WeekNumber.NET? (6)

Tengo un problema bastante raro. Vivo en Dinamarca y aquí la primera semana (Semana 1) de 2013 comienza el 31 de diciembre de 2012 y dura 7 días, como suelen hacerlo las semanas :)

Según .NET, sin embargo, el 30 de diciembre es la semana 52, el 31 es la semana 53 y el 1 de enero es la semana 1.

La semana 53 dura solo un día, y la semana 1 dura 6 días. Claramente, esto debe ser incorrecto (una semana de menos de 7 días) y ciertamente es incorrecto en el contexto danés. Donde el 31 de diciembre es la semana 1, NO la semana 53.

El siguiente código ilustra el problema (CurrentCulture es "da-DK")

static void Main(string[] args) { //Here I get Monday DayOfWeek firstDayOfWeek = DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek; //Here I get FirstFourDayWeek CalendarWeekRule weekRule = DateTimeFormatInfo.CurrentInfo.CalendarWeekRule; DateTime date = new DateTime(2012,12,30); for (int i = 0; i <= 10; i++) { DateTime currentDate = date.AddDays(i); Console.WriteLine("Date: {0} WeekNumber: {1}", currentDate.ToShortDateString(), CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(currentDate, weekRule, firstDayOfWeek)); } Console.ReadLine(); }

¿He hecho algo mal o es un error en .NET? Si es lo último, ¿tiene sugerencias para calcular los números de semana correctamente?


1.1.2013 es martes, y comienza la semana # 1. 31.12.2012 es lunes, pertenece al año 2012 y, por lo tanto, es la semana # 53. Naturalmente, no puede tener la semana 1 en diciembre, ya que eso significaría que habría dos semanas 1 en un año, y por lo tanto causaría todo tipo de problemas desagradables con el código.

No hay ninguna regla que una semana debe ser exactamente de 7 días.


Calendar.GetWeekOfYear no es compatible con la especificación ISO8601, consulte también el formato de la Semana del Año ISO 8601 en Microsoft .Net .

Puede utilizar la clase Semana de la biblioteca de períodos de tiempo para .NET :

// ---------------------------------------------------------------------- public void CalendarWeekSample() { DateTime testDate = new DateTime( 2007, 12, 31 ); // .NET calendar week TimeCalendar calendar = new TimeCalendar(); Console.WriteLine( "Calendar Week of {0}: {1}", testDate.ToShortDateString(), new Week( testDate, calendar ).WeekOfYear ); // > Calendar Week of 31.12.2007: 53 // ISO 8601 calendar week TimeCalendar calendarIso8601 = new TimeCalendar( new TimeCalendarConfig { YearWeekType = YearWeekType.Iso8601 } ); Console.WriteLine( "ISO 8601 Week of {0}: {1}", testDate.ToShortDateString(), new Week( testDate, calendarIso8601 ).WeekOfYear ); // > ISO 8601 Week of 31.12.2007: 1 } // CalendarWeekSample


El problema es que el método GetWeekOfYear no respeta ISO 8601, que es lo que usted espera, pero no lo hace.

Tenga en cuenta que mientras usa FirstFourDayWeek , la msdn.microsoft.com/en-us/library/… dice:

La primera semana basada en el valor de FirstFourDayWeek puede tener de cuatro a siete días.

que es una violación de la norma ISO 8601 de que todas las semanas deben tener siete días.

blogs.msdn.com/b/shawnste/archive/2006/01/24/…

Puede utilizar el siguiente método para obtener el número de semana correcto de acuerdo con ISO 8601:

int weekNumber(DateTime fromDate) { // Get jan 1st of the year DateTime startOfYear = fromDate.AddDays(- fromDate.Day + 1).AddMonths(- fromDate.Month +1); // Get dec 31st of the year DateTime endOfYear = startOfYear.AddYears(1).AddDays(-1); // ISO 8601 weeks start with Monday // The first week of a year includes the first Thursday // DayOfWeek returns 0 for sunday up to 6 for saterday int[] iso8601Correction = {6,7,8,9,10,4,5}; int nds = fromDate.Subtract(startOfYear).Days + iso8601Correction[(int)startOfYear.DayOfWeek]; int wk = nds / 7; switch(wk) { case 0 : // Return weeknumber of dec 31st of the previous year return weekNumber(startOfYear.AddDays(-1)); case 53 : // If dec 31st falls before thursday it is week 01 of next year if (endOfYear.DayOfWeek < DayOfWeek.Thursday) return 1; else return wk; default : return wk; } }

Source (también hay muchas otras funciones por ahí ...)

Así, cambiando su bucle a

for (int i = 0; i <= 10; i++) { DateTime currentDate = date.AddDays(i); Console.WriteLine("Date: {0} WeekNumber: {1}: CorrectWeekNumber: {2}", currentDate.ToShortDateString(), CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(currentDate, weekRule, firstDayOfWeek), weekNumber(currentDate)); }

resultará en:

Fecha: 30.12.2012 WeekNumber: 52: CorrectWeekNumber: 52
Fecha: 31.12.2012 WeekNumber: 53: CorrectWeekNumber: 1
Fecha: 01.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
Fecha: 02.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
Fecha: 03.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
Fecha: 04.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
Fecha: 05.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
Fecha: 06.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
Fecha: 07.01.2013 WeekNumber: 2: CorrectWeekNumber: 2
Fecha: 08.01.2013 WeekNumber: 2: CorrectWeekNumber: 2
Fecha: 09.01.2013 WeekNumber: 2: CorrectWeekNumber: 2


Gracias por todas las respuestas. También busqué un poco más y finalmente creé dos métodos de C # para lograr lo que quería:

Primero, un resumen que se encuentra en uno de los comentarios en: blogs.msdn.com/b/shawnste/archive/2006/01/24/…

Que Jon Senchyna también señaló:

public static int WeekNumber(this DateTime date) { Calendar cal = CultureInfo.InvariantCulture.Calendar; DayOfWeek day = cal.GetDayOfWeek(date); date = date.AddDays(4 - ((int)day == 0 ? 7 : (int)day)); return cal.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); }

Y también uno en: http://www.tondering.dk/claus/cal/week.php#calcweekno

public static int WeekNumber2(this DateTime date) { int a; int b; int c; int s; int e; int f; if (date.Month <= 2) { a = date.Year - 1; b = a / 4 - a / 100 + a / 400; c = (a - 1) / 4 - (a - 1) / 100 + (a - 1) / 400; s = b - c; e = 0; f = date.Day - 1 + 31 * (date.Month - 1); } else { a = date.Year; b = a / 4 - a / 100 + a / 400; c = (a - 1) / 4 - (a - 1) / 100 + (a - 1) / 400; s = b - c; e = s + 1; f = date.Day + ((153 * (date.Month - 3) + 2) / 5) + 58 + s; } int g = (a + b) % 7; int d = (f + g - e) % 7; int n = f + 3 - d; if (n < 0) return 53 - ((g - s) / 5); if (n > (364 + s)) return 1; return n / 7 + 1; }

Ambos me dieron lo que yo quería.

También escribí una pequeña prueba de unidad que prueba que devuelven los mismos números de semana durante los primeros 3000 años del calendario.

[TestMethod] public void WeekNumbers_CorrectFor_3000Years() { var weekNumbersMethod1 = WeekNumbers3000Years(DateManipulation.WeekNumber).ToList(); var weekNumbersMethod2 = WeekNumbers3000Years(DateManipulation.WeekNumber2).ToList(); CollectionAssert.AreEqual(weekNumbersMethod1, weekNumbersMethod2); } private IEnumerable<int> WeekNumbers3000Years(Func<DateTime, int> weekNumberCalculator) { var startDate = new DateTime(1,1,1); var endDate = new DateTime(3000, 12, 31); for(DateTime date = startDate; date < endDate; date = date.AddDays(1)) yield return weekNumberCalculator(date); }


No sé nada sobre el calendario danés, pero las reglas para .NET son claras y su código se corresponde exactamente con eso. La documentación de MSDN para la enumeración CalendarWeekRule está msdn.microsoft.com/en-us/library/… , para referencia. En resumen:

.NET siempre considerará el 31 de diciembre como "la última semana del año".

Usted dice que está obteniendo un valor de la regla FirstFourDayWeek para la semana. Como el 1 de enero de 2013 es un martes, el resto de esa semana tiene seis días (la próxima semana comienza el lunes siguiente). Por lo tanto, 1-6 de enero de 2013 se considera "Semana 1". Si tuviera FirstFullWeek para esa regla, la semana 1 comenzaría el lunes 7 en su lugar.

En cuanto a "Claramente, esto debe estar equivocado:" claramente está bien, según su propia especificación. Nunca existe un requisito para que cualquier lenguaje de programación o API se ajuste a una expectativa particular; Cada proyecto define sus propios requisitos. .NET sigue las reglas que podemos considerar inesperadas, pero están documentadas y son coherentes con su documentación.

Si eso es útil o no es otra pregunta ...


Puede usar el siguiente código para calcular el número de semana para una fecha determinada:

public static int GetWeekNumber(DateTime date) { var firstDayOfYear = new DateTime(date.Year, 1, 1); var lastDayOfYear = new DateTime(date.Year, 12, 31); var lastDayOfPreviousYear = new DateTime(date.Year - 1, 12, 31); var weekDayOfFirstDayOfYear = (int)firstDayOfYear.DayOfWeek + 1; var weekDayOfLastDayOfYear = (int)lastDayOfYear.DayOfWeek + 1; var days = (date - firstDayOfYear).Days; if (days <= 7 - weekDayOfFirstDayOfYear) // My day fall in 1''st week of the year { if (weekDayOfFirstDayOfYear > 5) return GetWeekNumber(lastDayOfPreviousYear); return 1; } else // My day fall not on 1''st week of the year { // Number of weeks that pass from 1''st Sunday of 2''nd week of the year var weekNo = ((days - (8 - weekDayOfFirstDayOfYear)) / 7) + 1; if (weekDayOfFirstDayOfYear < 6) // if Year start at Sun...Thursday the first week is added. weekNo++; // Check if Last week of the year belong to next year if (weekDayOfLastDayOfYear < 5) // if the year end in Sunday to Wednesday then it might belong to the 1''st week of the next year { if ((lastDayOfYear - date).Days < weekDayOfLastDayOfYear) { return 1; } } return weekNo; } }