Obtenez le numéro de semaine correct d'une date donnée

222

J'ai beaucoup cherché sur Google et trouvé beaucoup de solutions, mais aucune ne me donne le bon numéro de semaine pour le 2012-12-31. Même l'exemple sur MSDN ( lien ) échoue.

2012-12-31 est lundi, donc ce devrait être la semaine 1, mais chaque méthode que j'ai essayée me donne 53. Voici quelques-unes 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 renvoie en fait 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);
}
Amberlamps
la source
4
Comment se passe la semaine 1 à la fin de l'année? Je veux dire, je vois où tu l'as. Mais 53 a du sens pour moi.
benjer3
1
Dans mes extraits de code, j'obtiens le CultureInfo et tout ça. Je pensais que mon programme sait d'ici là quel calendrier j'utilise. (Ici en Allemagne, le 31 décembre 2012 est dans la semaine 1 de 2013)
Amberlamps
Ce code ne fonctionne pas tout à fait car il devrait essayer les dates du 31 décembre 2016 par exemple ou du 1er
janvier
@ cavej03 Le 31 décembre 2016 est la semaine 52 et le GetIso8601WeekOfYear renvoie 52, donc je suppose que cela fonctionne correctement.
Muflix

Réponses:

311

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

Vous pouvez vous référer à cet article dans le blog MSDN pour une meilleure explication: « Format semaine ISO 8601 de l'année dans Microsoft .Net »

En termes simples, .Net permet de répartir les semaines sur plusieurs années, contrairement à la norme ISO. Dans l'article, il existe également une fonction simple pour obtenir le numéro de semaine ISO 8601 correct pour la dernière semaine de l'année.

Mise à jour La méthode suivante renvoie en fait 1 pour 2012-12-31ce qui est correct dans ISO 8601 (par exemple en 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);
} 
il_guru
la source
1
@il_guru Et si je souhaite que la semaine commence avec dimanche ?? Dois-je remplacer tous les "lundi" par "dimanche" ??
User2012384
2
@ User2012384 fyi, si vous voulez suivre la norme ISO8601, le premier jour de la semaine devrait toujours être lundi.
Starceaker
1
@il_guru Vous n'avez même pas besoin de cette "triche" si, n'est-ce pas? Pour autant que je sache "GetWeekOfYear (time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);" fonctionne très bien.
Starceaker
2
Si vous recherchez le deuxième lien (format ISO 8601 Semaine de l'année dans Microsoft .Net), vous pouvez trouver la réponse à votre question directement auprès de Microsoft. Un exemple est le lundi 31/12/2007: avec la fonction .Net, il retournera la semaine numéro 53 de 2007 tandis que pour la norme ISO c'est la semaine numéro 1 de 2008
il_guru
3
J'ai vérifié manuellement que cela était correct pour les années 2012 à 2018 (en supposant que epochconverter.com est correct). Nous devrions tous nous réjouir cette année que la semaine 1 de 2018 a réellement commencé le 1er janvier pour une fois!
Aidan
32

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

  • 52 semaines * 7 jours = 364 jours.

Donc, pour chaque année, vous en avez au moins un par jour 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. Examinons cela pour 2012.

  • États-Unis (dimanche -> samedi): 52 semaines + une courte semaine de 2 jours pour 2012-12-30 et 2012-12-31. Cela se traduit par 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 en conséquence.

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

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

Par exemple, l'année 2000 comptait 54 semaines. Le 1er janvier (samedi) était le premier jour d'une semaine et le 31 décembre (soleil) était le deuxième jour d'une 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);

Tirages: Année: 2012 Semaine: 54

Remplacez CalendarWeekRule dans l'exemple ci-dessus par FirstFullWeek ou FirstFourDayWeek et vous en reviendrez 53. Gardons le jour de début lundi puisque nous avons affaire à l'Allemagne.

La semaine 53 commence donc le lundi 31/12/2012, dure une journée puis s'arrête.

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

CultureInfo cul = CultureInfo.GetCultureInfo("de-DE");
Christophe Geers
la source
Je sais qu'il est possible qu'il y ait 53 semaines. Mais quand vous avez dimanche (12/30) et lundi (12/31), ne comptez-vous pas le mardi (01/01) également dans le cadre de la 53e semaine aux États-Unis?
Amberlamps
Je n'ai jamais vu de 54ème semaine!
Amberlamps
Certains progiciels fiscaux en ont.
Christophe Geers
D'accord, j'ai découvert que c'est possible dans les calendriers américains, mais cela ne se produira pas dans les calendriers allemands.
Amberlamps
1
+1 pour vos réflexions approfondies sur le problème, mais l'extrait de code dans l'entrée de blog fournie dans une réponse différente a en fait résolu mon problème.
Amberlamps
22

Ceci est le chemin:

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

Le plus important pour est le CalendarWeekRuleparamètre.

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

daniele3004
la source
3
"Plusieurs personnes ont remarqué que Calendar.GetWeekOfYear () est presque comme la semaine ISO 8601 lorsqu'elle est passée à CalendarWeekRule.FirstFourDayWeek et DayOfWeek.Monday, mais elle est un peu différente. Spécifiquement, ISO 8601 a toujours 7 semaines." Blogs.msdn. microsoft.com/shawnste/2006/01/24/…
Juha Palomäki
1
Votre lien est en italien
radbyx
pour le nouveau DateTime (2000,12,31), il renvoie seulement 52?
Leszek P
18

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

Le type a 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 .

MISE À JOUR : Ces API ont également été incluses dans la version 2.1 de .NET Standard .

khellang
la source
Une idée de la sortie de la v 3.0?
Jacques
"Nous prévoyons de publier un premier aperçu de .NET Core 3 plus tard cette année et la version finale en 2019." (extrait du blog de la feuille de route )
khellang
@khellang comment pourrais-je obtenir la semaine ordinale d'un mois en fonction de datetime? Au lieu d'avoir la semaine 33, j'aimerais voir la semaine 2 (car la semaine 33 est la semaine 2 en août). stackoverflow.com/questions/58039103/…
Roxy'Pro
11

Puisqu'il ne semble pas y avoir de culture .Net qui donne le bon numéro de semaine ISO-8601, je préfère ignorer complètement la détermination de la semaine intégrée et faire le calcul manuellement, au lieu d'essayer de corriger un correct partiellement résultat.

Je me suis retrouvé avec 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 de 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, c'est le jeudi cible, qui détermine l'année de la semaine.

Si vous divisez le nombre de jours (basé sur zéro) dans l'année par sept (arrondi vers le bas), vous obtenez le numéro de semaine (basé sur zéro) dans l'année.

En c #, les résultats des calculs entiers sont arrondis implicitement.

realbart
la source
1
C'est de loin la meilleure solution mais je soulignerai une manière légèrement différente de le faire: DateTime est basé sur le calendrier grégorien proleptique - c'est-à-dire qu'il étend le calendrier grégorien à l'année 1. Le 1er janvier 0001 se trouve être un Lundi. Ticksest le nombre d'incréments de 100 nanosecondes depuis le 1er janvier 0001. Étant donné qu'il y a 86 400 secondes dans une journée, nous pouvons écrire:var thursday = date.AddDays(3 - date.Ticks / 86400 / 10_000_000 % 7); return (thursday.DayOfYear - 1) / 7 + 1;
Adrian S
5

Dans .NET 3.0 et ISOWeek.GetWeekOfDateversions ultérieures, vous pouvez utiliser la méthode -Met .

Notez que l'année dans le format de numéro année + semaine peut différer de l'année du en DateTimeraison des semaines qui traversent la limite de l'année.

johh
la source
3

C # vers le port Powershell à partir du code ci-dessus d' 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
}
Rainer
la source
1
Pourriez-vous expliquer votre code? Les réponses uniquement codées ne sont généralement pas acceptées sur StackOverflow et, en tant que telles, sont susceptibles d'être supprimées.
Wai Ha Lee
@Wai Ha Lee le code est déjà expliqué. Voir le post ci-dessus de il_guru. J'ai porté son code sur PowerShell, afin que d'autres personnes puissent l'utiliser dans PowerShell car il n'y a pas de bonne solution dans PowerShell jusqu'à présent.
Rainer
Si c'est ce que vous avez fait, vous devez attribuer à l'auteur d'origine la réponse elle-même. La réponse que vous avez transmise contient de meilleurs commentaires que vous avez probablement supprimés. De plus, ce n'est pas une réponse à la question car la question ne mentionnait pas PowerShell.
Wai Ha Lee
1
Je l'ai fait maintenant @Wai Ha Lee. La question est déjà marquée comme ayant été répondue, ce serait donc la même solution écrite dans une autre langue. J'ai rendu service aux personnes qui recherchent une solution en langage Powershell (je cherchais dur une solution en Powershell, mais ce que j'ai trouvé était une solution en C #). L'idée reste la même et l'auteur s'en mérite.
Rainer
2

La façon la plus simple de déterminer le style de numéro de semaine ISO 8601 à l'aide de c # et de la classe DateTime.

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

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;
user6887101
la source
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;

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

Donskikh Andrei
la source
1

Voici une version d'extension et une version nullable de la réponse d' 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
Jogge
la source
0

La question est: comment définissez-vous si une semaine est en 2012 ou en 2013? Votre supposition, je suppose, est que puisque 6 jours de la semaine sont en 2013, cette semaine devrait être marquée comme la première semaine de 2013.

Je ne sais pas si c'est la bonne façon de procéder. Cette semaine a commencé en 2012 (le lundi 31 décembre), elle devrait donc être marquée comme la dernière semaine de 2012, elle devrait donc être le 53 décembre 2012. La première semaine de 2013 devrait commencer le lundi 7 décembre.

Maintenant, vous pouvez gérer le cas particulier des semaines marginales (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.

Samy Arous
la source
D'accord, je peux voir votre point. Le truc, c'est quand j'utilise le 01/07/2013 dans n'importe laquelle de mes méthodes, c'est-à-dire la semaine 2. Donc, 2012/12/31 est la semaine 53 et 2013/01/07 est la semaine 2. Vous pourriez penser qu'il n'y a pas de semaine 1 de 2013. Mais quand j'essaie 01/01/2013, il est dit la semaine 1.
Amberlamps
0
  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));      
Imran
la source
0

Sur la base de la réponse d'il_guru, j'ai créé cette version pour mes propres besoins qui renvoie également le composant année.

    /// <summary>
    /// This presumes that weeks start with Monday.
    /// Week 1 is the 1st week of the year with a Thursday in it.
    /// </summary>
    /// <param name="time">The date to calculate the weeknumber for.</param>
    /// <returns>The year and weeknumber</returns>
    /// <remarks>
    /// Based on Stack Overflow Answer: https://stackoverflow.com/a/11155102
    /// </remarks>
    public static (short year, byte week) 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
        var week = (byte)CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
        return ((short)(week >= 52 & time.Month == 1 ? time.Year - 1 : time.Year), week);
    }
NKCSS
la source
0

Ces deux méthodes vous aideront, en supposant que notre semaine commence lundi

/// <summary>
    /// Returns the weekId
    /// </summary>
    /// <param name="DateTimeReference"></param>
    /// <returns>Returns the current week id</returns>
    public static DateTime GetDateFromWeek(int WeekReference)
    {
        //365 leap
        int DaysOffset = 0;
        if (WeekReference > 1)
        {
            DaysOffset = 7;
            WeekReference = WeekReference - 1;
        }
        DateTime DT = new DateTime(DateTime.Now.Year, 1, 1);
        int CurrentYear = DT.Year;
        DateTime SelectedDateTime = DateTime.MinValue;

        while (CurrentYear == DT.Year)
        {
            int TheWeek = WeekReportData.GetWeekId(DT);
            if (TheWeek == WeekReference)
            {
                SelectedDateTime = DT;
                break;
            }
            DT = DT.AddDays(1.0D);
        }

        if (SelectedDateTime == DateTime.MinValue)
        {
            throw new Exception("Please check week");
        }

        return SelectedDateTime.AddDays(DaysOffset);
    }
/// <summary>
    /// Returns the weekId
    /// </summary>
    /// <param name="DateTimeReference"></param>
    /// <returns>Returns the current week id</returns>
    public static int GetWeekId(DateTime DateTimeReference)
    {
        CultureInfo ciCurr = CultureInfo.InvariantCulture;
        int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTimeReference,
        CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
        return weekNum;
    }
Mnyikka
la source
-1

Une année compte 52 semaines et 1 jour ou 2 en cas d'année par tour (52 x 7 = 364). 2012-12-31 serait la semaine 53, une semaine qui n'aurait que 2 jours car 2012 est une année-tour.

CarlosJ
la source
Ceci est une erreur. Le premier jour de la semaine de l'année peut tomber n'importe quel jour de la semaine, selon la façon dont vous comptez les semaines, l'année peut avoir une 54e semaine.
krowe2
-2
public int GetWeekNumber()
{
   CultureInfo ciCurr = CultureInfo.CurrentCulture;
   int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, 
   CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
   return weekNum;
}
Meghnath Das
la source