c# - vivo - ¿Está.NET dándome el número de semana equivocado para el 29 de diciembre de 2008?
net tv programacion (7)
Según el calendario oficial (gregoriano) , el número de semana del 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.
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(date, rule, firstDayOfWeek)
.
La rule
parámetro es una enumeración con 3 valores posibles: FirstDay, FirstFourWeekDay, FirstFullWeek
. Por lo que he entendido, debería FirstFourWeekDay
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 preparé 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, 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 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é. Así que no hay soluciones especiales para 2008.
@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.
Hay más información disponible en los siguientes sitios:
- http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html
- http://www.w3.org/TR/NOTE-datetime
- http://www.cl.cam.ac.uk/~mgk25/iso-time.html
- http://www.cs.tut.fi/~jkorpela/iso8601.html
- http://hydracen.com/dx/iso8601.htm
- http://www.probabilityof.com/ISO8601.shtml
- http://www.mcs.vuw.ac.nz/technical/software/SGML/doc/iso8601/ISO8601.html
Simplemente pon:
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, de manera equivalente, el que incluye el 4 de enero.
Aquí hay una 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
Como 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.
Como solución alternativa, ¿por qué no utilizar FirstFourDayWeek
pero agregar:
if ( weekNumber > 52 )
weekNumber = 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 con la contabilización del final del año calendario con fines informativos. y el IRS (o 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.
Este artículo profundiza en el problema y las soluciones posibles. El eje del asunto es que la implementación del calendario .NET no parece implementar fielmente el estándar ISO
Los números de semana difieren de un país a otro y deben depender de su configuración regional / regional si no estoy del todo equivocado.
Editar: Wikipedia admite mi vaga recolección de que estos números difieren según el país: http://en.wikipedia.org/wiki/Week_number#Week_number
Esperaría un marco respetable para obedecer al PAÍS seleccionado en tu tiempo de ejecución local.
Sé que esta es una publicación anterior, pero de cualquier forma noda el tiempo parece obtener el resultado correcto ...