Obtenga el número de semana correcto de una fecha determinada

Busqué en Google y encontré muchas soluciones, pero ninguna de ellas me da el número de semana correcto para el 2012-12-31. Incluso el ejemplo en MSDN ( enlace ) falla.

2012-12-31 es lunes, por lo tanto, debería ser la semana 1, pero cada método que probé me da 53. Estos son algunos de los métodos que he probado:

De la Biblioteca MDSN:

DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo; Calendar cal = dfi.Calendar; return cal.GetWeekOfYear(date, dfi.CalendarWeekRule, dfi.FirstDayOfWeek); 

Solución 2:

 return new GregorianCalendar(GregorianCalendarTypes.Localized).GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); 

Solución 3:

 CultureInfo ciCurr = CultureInfo.CurrentCulture; int weekNum = ciCurr.Calendar.GetWeekOfYear(dtPassed, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); return weekNum; 

Actualizar

El siguiente método realmente devuelve 1 cuando la fecha es 2012-12-31. En otras palabras, mi problema era que mis métodos no seguían el estándar ISO-8601.

 // This presumes that weeks start with Monday. // Week 1 is the 1st week of the year with a Thursday in it. public static int GetIso8601WeekOfYear(DateTime time) { // Seriously cheat. If its Monday, Tuesday or Wednesday, then it'll // be the same week# as whatever Thursday, Friday or Saturday are, // and we always get those right DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time); if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday) { time = time.AddDays(3); } // Return the week of our adjusted day return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); } 

Como se indica en esta página de MSDN, existe una ligera diferencia entre la semana ISO8601 y la numeración de la semana .Net.

Puede consultar este artículo en MSDN Blog para obtener una explicación mejor: ” Formato de Semana ISO del año 8601 en Microsoft .Net ”

En pocas palabras, .Net permite dividir las semanas en años, mientras que el estándar ISO no. En el artículo también hay una función simple para obtener el número correcto de la semana ISO 8601 para la última semana del año.

Actualización El siguiente método realmente devuelve 1 para 2012-12-31 que es correcto en ISO 8601 (por ejemplo, Alemania).

 // This presumes that weeks start with Monday. // Week 1 is the 1st week of the year with a Thursday in it. public static int GetIso8601WeekOfYear(DateTime time) { // Seriously cheat. If its Monday, Tuesday or Wednesday, then it'll // be the same week# as whatever Thursday, Friday or Saturday are, // and we always get those right DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time); if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday) { time = time.AddDays(3); } // Return the week of our adjusted day return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); } 

Puede haber más de 52 semanas en un año. Cada año tiene 52 semanas completas + 1 o +2 (año bisiesto) adicionales. Ellos compensan una 53ª semana.

  • 52 semanas * 7 días = 364 días.

Entonces, por cada año, tiene al menos un día adicional. Dos para años bisiestos. ¿Estos días adicionales se cuentan como semanas separadas?

Cuántas semanas realmente hay depende del día de inicio de su semana. Consideremos esto para el 2012.

  • EE. UU. (Domingo -> sábado): 52 semanas + una semana corta de 2 días para 2012-12-30 y 2012-12-31. Esto resulta en un total de 53 semanas. Los dos últimos días de este año (domingo + lunes) componen su propia corta semana.

Verifique la configuración de su Cultura actual para ver qué usa como el primer día de la semana.

Como ve, es normal obtener 53 como resultado.

  • Europa (lunes -> domingo): el 2 de enero (2012-1-2) es el primer lunes, así que este es el primer día de la primera semana. Pregunta el número de la semana del 1 de enero y recibirás 52 ya que se considera parte de 2011 la semana pasada.

Incluso es posible tener una 54ª semana. Ocurre cada 28 años cuando el 1 de enero y el 31 de diciembre son tratados como semanas separadas. Debe ser un año bisiesto también.

Por ejemplo, el año 2000 tuvo 54 semanas. El 1 de enero (sábado) fue el primer día de la semana, y el 31 de diciembre (el sol) fue el segundo día de la semana.

 var d = new DateTime(2012, 12, 31); CultureInfo cul = CultureInfo.CurrentCulture; var firstDayWeek = cul.Calendar.GetWeekOfYear( d, CalendarWeekRule.FirstDay, DayOfWeek.Monday); int weekNum = cul.Calendar.GetWeekOfYear( d, CalendarWeekRule.FirstDay, DayOfWeek.Monday); int year = weekNum == 52 && d.Month == 1 ? d.Year - 1 : d.Year; Console.WriteLine("Year: {0} Week: {1}", year, weekNum); 

Imprime: Año: 2012 Semana: 54

Cambie CalendarWeekRule en el ejemplo anterior a FirstFullWeek o FirstFourDayWeek y obtendrá 53. Mantengamos el día de inicio el lunes ya que estamos tratando con Alemania.

Así que la semana 53 comienza el lunes 2012-12-31, dura un día y luego se detiene.

53 es la respuesta correcta. Cambia Culture to Germany si quieres probarlo.

 CultureInfo cul = CultureInfo.GetCultureInfo("de-DE"); 

Esta es la forma:

 public int GetWeekNumber() { CultureInfo ciCurr = CultureInfo.CurrentCulture; int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); return weekNum; } 

Lo más importante para el es el parámetro CalendarWeekRule .

Consulte aquí: https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=IT-IT&k=k(System.Globalization.CalendarWeekRule);k(TargetFrameworkMoniker-.NETFramework

Voy a jugar Nigromante aquí 🙂

Dado que no parece existir un .Net-culture que arroje el número correcto de la semana ISO-8601, calificaría bypass la determinación de semana incorporada en conjunto, y haré el cálculo manualmente, en lugar de intentar corregir un resultado parcialmente correcto. .

Lo que terminé con es el siguiente método de extensión:

 ///  /// Converts a date to a week number. /// ISO 8601 week 1 is the week that contains the first Thursday that year. ///  public static int ToIso8601Weeknumber(this DateTime date) { var thursday = date.AddDays(3 - date.DayOfWeek.DayOffset()); return (thursday.DayOfYear - 1) / 7 + 1; } ///  /// Converts a week number to a date. /// Note: Week 1 of a year may start in the previous year. /// ISO 8601 week 1 is the week that contains the first Thursday that year, so /// if December 28 is a Monday, December 31 is a Thursday, /// and week 1 starts January 4. /// If December 28 is a later day in the week, week 1 starts earlier. /// If December 28 is a Sunday, it is in the same week as Thursday January 1. ///  public static DateTime FromIso8601Weeknumber(int weekNumber, int? year = null, DayOfWeek day = DayOfWeek.Monday) { var dec28 = new DateTime((year ?? DateTime.Today.Year) - 1, 12, 28); var monday = dec28.AddDays(7 * weekNumber - dec28.DayOfWeek.DayOffset()); return monday.AddDays(day.DayOffset()); } ///  /// Iso8601 weeks start on Monday. This returns 0 for Monday. ///  private static int DayOffset(this DayOfWeek weekDay) { return ((int)weekDay + 6) % 7; } 

En primer lugar, ((int) date.DayOfWeek + 6)% 7) determina el número del día de la semana, 0 = lunes, 6 = domingo.

date.AddDays (- ((int) date.DayOfWeek + 6)% 7) determina la fecha del lunes anterior al número de semana solicitado.

Tres días después es el objective el jueves, que determina en qué año está la semana.

Si divide el número de días (basado en cero) dentro del año por siete (redondee hacia abajo), obtendrá el número de semana (basado en cero) en el año.

En c #, los resultados del cálculo entero se redondean implícitamente.

C # al puerto Powershell del código anterior de il_guru :

 function GetWeekOfYear([datetime] $inputDate) { $day = [System.Globalization.CultureInfo]::InvariantCulture.Calendar.GetDayOfWeek($inputDate) if (($day -ge [System.DayOfWeek]::Monday) -and ($day -le [System.DayOfWeek]::Wednesday)) { $inputDate = $inputDate.AddDays(3) } # Return the week of our adjusted day $weekofYear = [System.Globalization.CultureInfo]::InvariantCulture.Calendar.GetWeekOfYear($inputDate, [System.Globalization.CalendarWeekRule]::FirstFourDayWeek, [System.DayOfWeek]::Monday) return $weekofYear } 

La forma más fácil de determinar el estilo de la semana número ISO 8601 usando c # y la clase DateTime.

Pregunte esto: el jueves de cómo es que muchos jueves del año es el jueves de esta semana. La respuesta es igual al número de semana deseado.

 var dayOfWeek = (int)moment.DayOfWeek; // Make monday the first day of the week if (--dayOfWeek < 0) dayOfWeek = 6; // The whole nr of weeks before this thursday plus one is the week number var weekNumber = (moment.AddDays(3 - dayOfWeek).DayOfYear - 1) / 7 + 1; 
 var cultureInfo = CultureInfo.CurrentCulture; var calendar = cultureInfo.Calendar; var calendarWeekRule = cultureInfo.DateTimeFormat.CalendarWeekRule; var firstDayOfWeek = cultureInfo.DateTimeFormat.FirstDayOfWeek; var lastDayOfWeek = cultureInfo.LCID == 1033 //En-us ? DayOfWeek.Saturday : DayOfWeek.Sunday; var lastDayOfYear = new DateTime(date.Year, 12, 31); var weekNumber = calendar.GetWeekOfYear(date, calendarWeekRule, firstDayOfWeek); //Check if this is the last week in the year and it doesn`t occupy the whole week return weekNumber == 53 && lastDayOfYear.DayOfWeek != lastDayOfWeek ? 1 : weekNumber; 

Funciona bien para las culturas de EE. UU. Y Rusia. ISO 8601 también será correcto, porque la semana rusa comienza el lunes.

¡Buenas noticias! Una solicitud de extracción que agrega System.Globalization.ISOWeek a .NET Core acaba de fusionarse y está actualmente progtwigda para la versión 3.0. Esperemos que se propague a las otras plataformas .NET en un futuro no muy lejano.

El tipo tiene la siguiente firma, que debería cubrir la mayoría de las necesidades de la semana ISO :

 namespace System.Globalization { public static class ISOWeek { public static int GetWeekOfYear(DateTime date); public static int GetWeeksInYear(int year); public static int GetYear(DateTime date); public static DateTime GetYearEnd(int year); public static DateTime GetYearStart(int year); public static DateTime ToDateTime(int year, int week, DayOfWeek dayOfWeek); } } 

Puedes encontrar el código fuente aquí .

La pregunta es: ¿cómo se define si una semana es en 2012 o en 2013? Supongo que su suposición es que, dado que 6 días de la semana son en 2013, esta semana debería marcarse como la primera semana de 2013.

No estoy seguro de si este es el camino correcto a seguir. Esa semana comenzó en 2012 (el lunes 31 de diciembre), por lo que debe marcarse como la última semana de 2012, por lo tanto, debería ser el día 53 de 2012. La primera semana de 2013 debería comenzar el lunes 7.

Ahora puede manejar el caso particular de semanas límite (primera y última semana del año) utilizando la información del día de la semana. Todo depende de tu lógica.

  DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo; DateTime date1 = new DateTime(2011, 1, 1); Calendar cal = dfi.Calendar; Console.WriteLine("{0:d}: Week {1} ({2})", date1, cal.GetWeekOfYear(date1, dfi.CalendarWeekRule, dfi.FirstDayOfWeek), cal.ToString().Substring(cal.ToString().LastIndexOf(".") + 1)); 

Un año tiene 52 semanas y 1 día o 2 en caso de un año de vuelta (52 x 7 = 364). 2012-12-31 sería la semana 53, una semana que solo tendría 2 días porque 2012 es un año de vuelta.