Obter o número de semana correto de uma determinada data

Eu pesquisei muito no Google e encontrei muitas soluções, mas nenhuma delas me forneceu o número da semana correto para 2012-12-31. Mesmo o exemplo no MSDN ( link ) falha.

2012-12-31 é segunda-feira, portanto, deve ser a semana 1, mas cada método que eu tentei me dá 53. Aqui estão alguns dos methods que eu tentei:

Da biblioteca MDSN:

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

Solução 2:

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

Solução 3:

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

Atualizar

O método a seguir retorna 1 quando a data é 2012-12-31. Em outras palavras, meu problema era que meus methods não estavam seguindo o padrão 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); } 

Conforme observado nesta página do MSDN, há uma pequena diferença entre a semana ISO8601 e a numeração da semana .Net.

Você pode consultar este artigo no Blog da MSDN para obter uma explicação melhor: ” Formato ISO 8601 da semana do ano no Microsoft .Net ”

Simplificando, o .Net permite que semanas sejam divididas ao longo dos anos, enquanto o padrão ISO não. No artigo, há também uma function simples para obter o número da semana ISO 8601 correto para a última semana do ano.

Atualização O método a seguir retorna 1 para 2012-12-31 que está correto na ISO 8601 (por exemplo, Alemanha).

 // 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); } 

Pode haver mais de 52 semanas em um ano. Cada ano tem 52 semanas completas + 1 ou +2 (ano bissexto) extras. Eles compensam a 53ª semana.

  • 52 semanas * 7 dias = 364 dias.

Então, para cada ano você tem pelo menos um dia extra. Dois anos bissextos. Esses dias extras são contados como semanas separadas?

Quantas semanas há realmente depende do dia de início da semana. Vamos considerar isso para 2012.

  • EUA (domingo -> sábado): 52 semanas + uma semana curta de 2 dias para 2012-12-30 e 2012-12-31. Isso resulta em um total de 53 semanas. Últimos dois dias deste ano (domingo + segunda-feira) compõem sua própria semana curta.

Verifique as configurações atuais do seu Culture para ver o que ele usa como o primeiro dia da semana.

Como você vê, é normal obter 53 como resultado.

  • Europa (segunda-feira -> domingo): 2 de janeiro de 2012 (2012-1-2) é a primeira segunda-feira, então este é o primeiro dia da primeira semana. Peça o número da semana para o dia 1 de janeiro e você receberá 52 como é considerado parte da semana passada de 2011.

É até possível ter uma 54ª semana. Acontece a cada 28 anos quando o dia 1 de janeiro e o dia 31 de dezembro são tratados como semanas separadas. Deve ser um ano bissexto também.

Por exemplo, o ano de 2000 teve 54 semanas. 1º de janeiro (sábado) foi o primeiro dia de uma semana e 31 de dezembro (domingo) foi o segundo dia de uma 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); 

Cópias para fora: Ano: 2012 Semana: 54

Altere CalendarWeekRule no exemplo acima para FirstFullWeek ou FirstFourDayWeek e você receberá 53. Vamos manter o dia de início na segunda-feira, já que estamos lidando com a Alemanha.

Portanto, a semana 53 começa na segunda-feira, dia 12 de dezembro de 2012, dura um dia e depois pára.

53 é a resposta correta. Mude a cultura para a Alemanha, se quiser experimentá-lo.

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

Este é o caminho:

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

O mais importante é o parâmetro CalendarWeekRule .

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

Eu vou jogar o Necromancer aqui 🙂

Como não parece haver uma cultura .Net que produza o número correto da semana ISO-8601, avaliá-la ignoraria completamente a determinação construída em semanas e faria o cálculo manualmente, em vez de tentar corrigir um resultado parcialmente correto. .

O que acabei com o seguinte método de extensão:

 ///  /// 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; } 

Primeiro de tudo, ((int) date.DayOfWeek + 6)% 7) determina o número do dia da semana, 0 = segunda-feira, 6 = domingo.

date.AddDays (- ((int) date.DayOfWeek + 6)% 7) determina a data da segunda-feira a pré-solicitar o número da semana solicitada.

Três dias depois é a quinta-feira de destino, que determina em que ano a semana está.

Se você dividir o número do dia (baseado em zero) no ano por sete (arredondado para baixo), você obtém o número da semana (baseado em zero) no ano.

Em c #, os resultados do cálculo de números inteiros são redefinidos implicitamente.

C # para a porta Powershell do código acima do 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 } 

A maneira mais fácil de determinar o número de semana do estilo ISO 8601 usando c # e a class DateTime.

Pergunte isso: a quinta-feira do quinto dia do ano é a quinta-feira desta semana. A resposta é igual ao número da semana desejada.

 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 bem tanto para culturas norte-americanas quanto russas. A ISO 8601 também estará correta, porque a semana russa começa na segunda-feira.

Boas notícias! Uma solicitação pull, que adiciona o System.Globalization.ISOWeek ao .NET Core, foi mesclada e atualmente está prevista para a versão 3.0. Espero que ele se propague para as outras plataformas .NET em um futuro não muito distante.

O tipo tem a seguinte assinatura, que deve cobrir a maioria das necessidades da 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); } } 

Você pode encontrar o código fonte aqui .

A pergunta é: como você define se uma semana é em 2012 ou em 2013? Sua suposição, eu acho, é que desde 6 dias da semana são em 2013, esta semana deve ser marcada como a primeira semana de 2013.

Não tenho certeza se este é o caminho certo a seguir. Essa semana começou em 2012 (na segunda-feira 31 de dezembro), por isso deve ser marcada como a última semana de 2012, portanto, deve ser o dia 53 de 2012. A primeira semana de 2013 deve começar na segunda-feira, dia 7.

Agora, você pode lidar com o caso específico de semanas de borda (primeira e última semana do ano) usando as informações do dia da semana. Tudo depende da sua 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)); 

Um ano tem 52 semanas e 1 dia ou 2 no caso de um ano de volta (52 x 7 = 364). 2012-12-31 seria a semana 53, uma semana que teria apenas 2 dias porque 2012 é um ano de volta.