Pregunta

De acuerdo con calendario oficial (gregoriano) , el número de semana para el 29/12/2008 es 1, porque después del último día de la semana 52 (es decir, 28/12) quedan tres días o menos en el año . Un poco extraño, pero bueno, las reglas son reglas.

Entonces, de acuerdo con 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 (fecha, regla, firstDayOfWeek) .

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

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

Así que encendí 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 es lo que obtengo

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

Entonces, como vemos, ninguna de las combinaciones anteriores coincide con el calendario oficial (si tiene prisa, solo vea que 29/12 nunca obtiene la semana # 1).

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

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

¿Fue útil?

Solución

Esto artículo profundiza en el problema y las posibles soluciones. El núcleo del asunto es que la implementación del calendario .NET no parece implementar fielmente el estándar ISO

Otros consejos

@Conrad es correcto. La implementación .NET de DateTime y GregorianCalendar no implementan / siguen la especificación ISO 8601 completa. Dicho esto, la especificación es extremadamente detallada y no trivial de implementar completamente, al menos para el lado del análisis.

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

En pocas palabras:

Una semana se identifica por su número en un año determinado y comienza 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í está parte del código que uso para manejar adecuadamente las fechas ISO 8601:

    #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

Los números de semana difieren de un país a otro y deberían depender de su configuración regional / regional si no estoy completamente equivocado.

Editar: Wikipedia admite mi vago recuerdo de que estos números difieren según el país: http: // en.wikipedia.org/wiki/Week_number#Week_number

Esperaría que un marco respetable obedeciera al PAÍS seleccionado en su tiempo de ejecución local.

En mi experiencia, el comportamiento demostrado es el comportamiento típico, que se refiere 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 de la semana se ha relacionado con la contabilidad del final del año calendario con fines informativos, y el IRS (o la agencia tributaria que elija) considera que el año calendario finalizará el 31 de diciembre, no la última semana completa del año.

Sé que esta es una publicación antigua, pero de cualquier manera parece que el tiempo noda obtiene el resultado correcto ...

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

Como solución alternativa, ¿por qué no usar FirstFourDayWeek sino agregar:

  if ( weekNumber > 52 )
    weekNumber = 1;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top