2009-01-09 8 views
14

De acuerdo con official (gregorian) calendar, el número de semana para 29/12/2008 es 1, porque después del último día de la semana 52 (es decir, 28/12) quedan tres o menos días en el año. Un poco raro, pero está bien, las reglas son reglas.¿Está .NET dándome el número de semana equivocado para el 29 de diciembre de 2008?

Así que de acuerdo a este calendario, tenemos estos valores límite para 2008/2009

  • 28/12 es la Semana 52
  • 29/12 es la semana 1
  • 1/1 es la semana 1
  • 8/1 es la Semana 2

C# ofrece una clase GregorianCalendar, que tiene una función GetWeekOfYear(date, rule, firstDayOfWeek).

El parámetro rule es una enumeración con 3 valores posibles: FirstDay, FirstFourWeekDay, FirstFullWeek. Por lo que he entendido, debería ir a la regla FirstFourWeekDay, pero probé todas ellas por si acaso.

El último parámetro informa qué día de la semana se debe considerar como el primer día de la semana, de acuerdo con ese calendario, es lunes, así que el lunes es.

Así que disparó una aplicación de consola rápida y sucia para probar esto:

using System; 
using System.Globalization; 

namespace CalendarTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var cal = new GregorianCalendar(); 
      var firstWeekDay = DayOfWeek.Monday; 
      var twentyEighth = new DateTime(2008, 12, 28); 
      var twentyNinth = new DateTime(2008, 12, 29); 
      var firstJan = new DateTime(2009, 1, 1); 
      var eightJan = new DateTime(2009, 1, 8); 
      PrintWeekDays(cal, twentyEighth, firstWeekDay); 
      PrintWeekDays(cal, twentyNinth, firstWeekDay); 
      PrintWeekDays(cal, firstJan, firstWeekDay); 
      PrintWeekDays(cal, eightJan, firstWeekDay); 
      Console.ReadKey(); 
     } 

     private static void PrintWeekDays(Calendar cal, DateTime dt, DayOfWeek firstWeekDay) 
     { 
      Console.WriteLine("Testing for " + dt.ToShortDateString()); 
      Console.WriteLine("--------------------------------------------"); 
      Console.Write(CalendarWeekRule.FirstDay.ToString() + "\t\t"); 
      Console.WriteLine(cal.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, firstWeekDay)); 
      Console.Write(CalendarWeekRule.FirstFourDayWeek.ToString() + "\t"); 
      Console.WriteLine(cal.GetWeekOfYear(dt, CalendarWeekRule.FirstFourDayWeek, firstWeekDay)); 
      Console.Write(CalendarWeekRule.FirstFullWeek.ToString() + "\t\t"); 
      Console.WriteLine(cal.GetWeekOfYear(dt, CalendarWeekRule.FirstFullWeek, firstWeekDay)); 
      Console.WriteLine("--------------------------------------------"); 
     } 
    } 
} 

... y esto lo que me pasa

Testing for 28.12.2008 
-------------------------------------------- 
FirstDay    52 
FirstFourDayWeek  52 
FirstFullWeek   51 
-------------------------------------------- 
Testing for 29.12.2008 
-------------------------------------------- 
FirstDay    53 
FirstFourDayWeek  53 
FirstFullWeek   52 
-------------------------------------------- 
Testing for 01.01.2009 
-------------------------------------------- 
FirstDay    1 
FirstFourDayWeek  1 
FirstFullWeek   52 
-------------------------------------------- 
Testing for 08.01.2009 
-------------------------------------------- 
FirstDay    2 
FirstFourDayWeek  2 
FirstFullWeek   1 
-------------------------------------------- 

Así que como vemos, ninguna de las combinaciones anteriores coincide con el calendario oficial (si tiene prisa, simplemente vea que 29/12 nunca recibe la semana n. ° 1).

¿Qué me estoy equivocando aquí? Tal vez hay algo evidente que me estoy perdiendo? (es viernes y tarde horas de trabajo aquí en Bélgica, tengan paciencia;))

Editar: Tal vez debería explicar: lo que necesito es una función que funciona para cualquier año, devolviendo los mismos resultados que el calendario gregoriano que vincula . Así que no hay soluciones especiales para 2008.

Respuesta

18

Este article se ve más en el tema y las posibles soluciones. El eje del asunto es que la implementación del calendario .NET no parece implementar fielmente el estándar ISO

+6

hmm ... MS desobedeciendo los estándares? Estoy sorprendido. – rmeador

+0

Pareciendo prometedor :) lamentablemente no puedo probar esto ahora, pero lo haré mañana. – rodbv

+0

rmeador: por lo que he visto Java tiene el mismo problema ... – rodbv

1

En mi experiencia, el comportamiento demostrado es el comportamiento típico, refiriéndose a la semana final parcial como la semana 53. Esto puede deberse a que toda la exposición significativa que he tenido a los números semanales se ha relacionado a la contabilidad al final del año calendario para fines de informes, y el IRS (o la agencia tributaria de su elección) considera que el año calendario finalizará el 31 de diciembre, no la última semana completa del año.

2

Los números de semana difieren de un país a otro y deben depender de la configuración regional/regional si no estoy del todo equivocado.

Editar: Wikipedia apoya mi vago recuerdo que estas cifras difieren según el país: http://en.wikipedia.org/wiki/Week_number#Week_number

Yo esperaría un marco respetable a obedecer el país seleccionado en el tiempo de ejecución local.

+0

Hm, podría ser. Veré si hago las cosas bien si lo arreglo en en-US cultureinfo. – rodbv

+0

Hice algunas pruebas y algunas lecturas de MSDN: la cultura se usa para informar los parámetros 2º y 3º de GetWeekOfYear, rule y firstdayofweek. Pero dado que estoy proporcionando esos, la cultura se vuelve irrelevante. – rodbv

-1

Como solución alternativa, podría decir que el número de semana es el mod 52 de WeekNumber. Creo que esto funcionaría para los casos que describe.

+0

Podría resolverlo en este caso, pero necesito un algoritmo general – rodbv

+1

y, además, hay años con 53 semanas, p. Ej. 2009 – rodbv

-1

Como solución, por qué no usar FirstFourDayWeek además de añadir:

if (weekNumber > 52) 
    weekNumber = 1; 
+1

Necesito un algoritmo general ... hay casos en que el año tiene oficialmente 53 semanas (2009 es así, IIRC) – rodbv

3

@Conrad es correcto. La implementación .NET de DateTime y GregorianCalendar no implementan/siguen la especificación ISO 8601 completa. Dicho esto, las especificaciones son extremadamente detalladas y no triviales para implementarse completamente, al menos para el análisis sintáctico.

Algunos más información está disponible en los siguientes sitios:

En pocas palabras:

Una semana se identifica por su número en un año determinado y se inicia con un lunes. La primera semana de un año es la que incluye el primer jueves, o equivalentemente la que incluye el 4 de enero

Aquí es parte del código que utilizo para manejar adecuadamente ISO 8601: fechas

#region FirstWeekOfYear 
    /// <summary> 
    /// Gets the first week of the year. 
    /// </summary> 
    /// <param name="year">The year to retrieve the first week of.</param> 
    /// <returns>A <see cref="DateTime"/>representing the start of the first 
    /// week of the year.</returns> 
    /// <remarks> 
    /// Week 01 of a year is per definition the first week that has the Thursday 
    /// in this year, which is equivalent to the week that contains the fourth 
    /// day of January. In other words, the first week of a new year is the week 
    /// that has the majority of its days in the new year. Week 01 might also 
    /// contain days from the previous year and the week before week 01 of a year 
    /// is the last week (52 or 53) of the previous year even if it contains days 
    /// from the new year. 
    /// A week starts with Monday (day 1) and ends with Sunday (day 7). 
    /// </remarks> 
    private static DateTime FirstWeekOfYear(int year) 
    { 
     int dayNumber; 

     // Get the date that represents the fourth day of January for the given year. 
     DateTime date = new DateTime(year, 1, 4, 0, 0, 0, DateTimeKind.Utc); 

     // A week starts with Monday (day 1) and ends with Sunday (day 7). 
     // Since DayOfWeek.Sunday = 0, translate it to 7. All of the other values 
     // are correct since DayOfWeek.Monday = 1. 
     if (date.DayOfWeek == DayOfWeek.Sunday) 
     { 
      dayNumber = 7; 
     } 
     else 
     { 
      dayNumber = (int)date.DayOfWeek; 
     } 

     // Since the week starts with Monday, figure out what day that 
     // Monday falls on. 
     return date.AddDays(1 - dayNumber); 
    } 

    #endregion 

    #region GetIsoDate 
    /// <summary> 
    /// Gets the ISO date for the specified <see cref="DateTime"/>. 
    /// </summary> 
    /// <param name="date">The <see cref="DateTime"/> for which the ISO date 
    /// should be calculated.</param> 
    /// <returns>An <see cref="Int32"/> representing the ISO date.</returns> 
    private static int GetIsoDate(DateTime date) 
    { 
     DateTime firstWeek; 
     int year = date.Year; 

     // If we are near the end of the year, then we need to calculate 
     // what next year's first week should be. 
     if (date >= new DateTime(year, 12, 29)) 
     { 
      if (date == DateTime.MaxValue) 
      { 
       firstWeek = FirstWeekOfYear(year); 
      } 
      else 
      { 
       firstWeek = FirstWeekOfYear(year + 1); 
      } 

      // If the current date is less than next years first week, then 
      // we are still in the last month of the current year; otherwise 
      // change to next year. 
      if (date < firstWeek) 
      { 
       firstWeek = FirstWeekOfYear(year); 
      } 
      else 
      { 
       year++; 
      } 
     } 
     else 
     { 
      // We aren't near the end of the year, so make sure 
      // we're not near the beginning. 
      firstWeek = FirstWeekOfYear(year); 

      // If the current date is less than the current years 
      // first week, then we are in the last month of the 
      // previous year. 
      if (date < firstWeek) 
      { 
       if (date == DateTime.MinValue) 
       { 
        firstWeek = FirstWeekOfYear(year); 
       } 
       else 
       { 
        firstWeek = FirstWeekOfYear(--year); 
       } 
      } 
     } 

     // return the ISO date as a numeric value, so it makes it 
     // easier to get the year and the week. 
     return (year * 100) + ((date - firstWeek).Days/7 + 1); 
    } 

    #endregion 

    #region Week 
    /// <summary> 
    /// Gets the week component of the date represented by this instance. 
    /// </summary> 
    /// <value>The week, between 1 and 53.</value> 
    public int Week 
    { 
     get 
     { 
      return this.isoDate % 100; 
     } 
    } 
    #endregion 

    #region Year 
    /// <summary> 
    /// Gets the year component of the date represented by this instance. 
    /// </summary> 
    /// <value>The year, between 1 and 9999.</value> 
    public int Year 
    { 
     get 
     { 
      return this.isoDate/100; 
     } 
    } 
    #endregion 
0

Sé que esta es una publicación anterior, pero de todas maneras el tiempo parece que noda obtiene el resultado correcto.

Cuestiones relacionadas