web-dev-qa-db-fra.com

Obtenir le bon numéro de semaine d'une date donnée

J'ai beaucoup cherché sur Google et trouvé de nombreuses solutions, mais aucune ne m'a donné le numéro de semaine correct pour le 2012-12-31. Même l'exemple sur MSDN ( link ) échoue.

2012-12-31 est lundi, donc cela devrait être la semaine 1, mais chaque méthode que j'ai essayée m'en donne 53. Voici certaines des méthodes que j'ai essayées:

De la bibliothèque MDSN:

DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
Calendar cal = dfi.Calendar;

return cal.GetWeekOfYear(date, dfi.CalendarWeekRule, dfi.FirstDayOfWeek);

Solution 2:

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

Solution 3:

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

Mettre à jour

La méthode suivante retourne effectivement 1 lorsque la date est 2012-12-31. En d'autres termes, mon problème était que mes méthodes ne suivaient pas la norme 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);
}
169
Amberlamps

Comme indiqué dans cette page MSDN , il existe une légère différence entre la numérotation semaine ISO8601 et la semaine .Net.

Vous pouvez vous reporter à cet article du blog MSDN pour une meilleure explication: " Format de la semaine de l'année ISO 8601 dans Microsoft .Net

Autrement dit, .Net permet aux semaines d'être divisées en plusieurs années, alors que la norme ISO ne le fait pas.

Update La méthode suivante renvoie en réalité 1 pour 2012-12-31, ce qui est correct dans ISO 8601 (par exemple, Allemagne).

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

Il peut y avoir plus de 52 semaines par an. Chaque année compte 52 semaines complètes + 1 ou +2 (année bissextile) supplémentaires. Ils compensent pour une 53ème semaine.

  • 52 semaines * 7 jours = 364 jours.

Donc, pour chaque année, vous avez au moins une journée supplémentaire. Deux pour les années bissextiles. Ces jours supplémentaires sont-ils comptés comme des semaines distinctes?

Le nombre de semaines dépend vraiment du jour de début de votre semaine. Considérons cela pour 2012.

  • États-Unis (dimanche -> samedi): 52 semaines + une semaine courte de 2 jours pour 2012-12-30 et 2012-12-31. Cela donne un total de 53 semaines. Les deux derniers jours de cette année (dimanche + lundi) constituent leur propre semaine courte.

Vérifiez les paramètres de votre culture actuelle pour voir ce qu'elle utilise comme premier jour de la semaine.

Comme vous le voyez, il est normal d’obtenir 53 à la suite.

  • Europe (lundi -> dimanche): Le 2 janvier, le 1er lundi (le 1er janvier 2012) est le premier jour de la première semaine. Demandez le numéro de la semaine pour le 1er janvier et vous recevrez 52 comme il est considéré comme faisant partie de la semaine de 2011 dernière.

Il est même possible d'avoir une 54ème semaine. Cela se produit tous les 28 ans lorsque le 1er janvier et le 31 décembre sont traités comme des semaines séparées. Ce doit être aussi une année bissextile. 

Par exemple, l’an 2000 comptait 54 semaines. Le 1er janvier (samedi) était le premier jour de la semaine et le 31 décembre (dimanche) était le deuxième jour de la semaine.

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 dehors: Année: 2012 Semaine: 54

Modifiez CalendarWeekRule dans l'exemple ci-dessus en FirstFullWeek ou FirstFourDayWeek et vous obtiendrez 53. Gardons la journée de début lundi puisque nous traitons avec l'Allemagne.

La semaine 53 commence donc le lundi 2012-12-31, dure un jour et s’arrête ensuite.

53 est la bonne réponse. Changez la culture en Allemagne si vous voulez l'essayer.

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

Je vais jouer au Nécromant ici :-)

Comme il ne semble pas exister de culture .Net qui donne le numéro de semaine ISO-8601 correct, je préférerais plutôt ignorer la détermination de la semaine intégrée et effectuer le calcul manuellement au lieu de tenter de corriger une erreur résultat. 

Ce que j'ai fini avec est la méthode d'extension suivante:

/// <summary>
/// Converts a date to a week number.
/// ISO 8601 week 1 is the week that contains the first Thursday that year.
/// </summary>
public static int ToIso8601Weeknumber(this DateTime date)
{
    var thursday = date.AddDays(3 - date.DayOfWeek.DayOffset());
    return (thursday.DayOfYear - 1) / 7 + 1;
}

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

/// <summary>
/// Iso8601 weeks start on Monday. This returns 0 for Monday.
/// </summary>
private static int DayOffset(this DayOfWeek weekDay)
{
    return ((int)weekDay + 6) % 7;
}

Tout d'abord, ((int)date.DayOfWeek + 6) % 7) détermine le numéro du jour de la semaine, 0 = lundi, 6 = dimanche.

date.AddDays(-((int)date.DayOfWeek + 6) % 7) détermine la date du lundi précédant le numéro de semaine demandé.

Trois jours plus tard, nous ciblons jeudi, ce qui détermine l’année de la semaine.

Si vous divisez le nombre de jours (base zéro) de l'année par sept (arrondi au nombre de jours inférieur), vous obtenez le numéro de semaine (base zéro) de l'année. 

En c #, les résultats du calcul des nombres entiers sont arrondis de manière implicite.

9
realbart

Bonnes nouvelles! Une demande pull ajoutant System.Globalization.ISOWeek à .NET Core vient d'être fusionnée et est actuellement destinée à la version 3.0. Espérons que cela se propage aux autres plates-formes .NET dans un avenir pas trop éloigné.

Le type comporte la signature suivante, qui devrait couvrir la plupart des besoins de la semaine 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);
    }
}

Vous pouvez trouver le code source ici .

4
khellang

C # vers le port Powershell depuis le code ci-dessus depuis 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
}
3
Rainer

Le moyen le plus simple de déterminer le style ISO 8601 correspondant au numéro de semaine à l'aide de c # et de la classe DateTime.

Demandez ceci: le jeudi de l'année, combien de fois sera le jeudi de cette semaine ... La réponse est égale au numéro de la semaine souhaitée.

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;
2
user6887101
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;

Cela fonctionne bien pour les cultures américaine et russe. ISO 8601 sera également correct, car la semaine russe commence le lundi. 

1
Donskikh Andrei

Voici une version extension et une version nullable de la réponse de il_guru .

Extension:

public static int GetIso8601WeekOfYear(this DateTime time)
{
    var day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

    return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}

Nullable:

public static int? GetIso8601WeekOfYear(this DateTime? time)
{
    return time?.GetIso8601WeekOfYear();
}

Coutumes:

new DateTime(2019, 03, 15).GetIso8601WeekOfYear(); //returns 11
((DateTime?) new DateTime(2019, 03, 15)).GetIso8601WeekOfYear(); //returns 11
((DateTime?) null).GetIso8601WeekOfYear(); //returns null
0
Jogge

La question qui se pose est la suivante: comment définissez-vous une semaine en 2012 ou 2013? . 

Pas sûr que ce soit la bonne façon de faire. Cette semaine a commencé en 2012 (le lundi 31 décembre), elle devrait donc être considérée comme la dernière semaine de 2012, donc elle devrait être la 53e année 2012. La première semaine de 2013 devrait commencer le lundi 7.

Maintenant, vous pouvez gérer le cas particulier des semaines Edge (première et dernière semaine de l'année) en utilisant les informations du jour de la semaine. Tout dépend de votre logique.

0
Samy Arous
  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));      
0
Imran