Calculer la date à partir du numéro de semaine


Réponses:


251

J'ai eu des problèmes avec la solution de @HenkHolterman même avec le correctif de @RobinAndersson.

La lecture de la norme ISO 8601 résout bien le problème. Utilisez le premier jeudi comme cible et non le lundi. Le code ci-dessous fonctionnera également pour la semaine 53 de 2009.

public static DateTime FirstDateOfWeekISO8601(int year, int weekOfYear)
{
    DateTime jan1 = new DateTime(year, 1, 1);
    int daysOffset = DayOfWeek.Thursday - jan1.DayOfWeek;

    // Use first Thursday in January to get first week of the year as
    // it will never be in Week 52/53
    DateTime firstThursday = jan1.AddDays(daysOffset);
    var cal = CultureInfo.CurrentCulture.Calendar;
    int firstWeek = cal.GetWeekOfYear(firstThursday, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

    var weekNum = weekOfYear;
    // As we're adding days to a date in Week 1,
    // we need to subtract 1 in order to get the right date for week #1
    if (firstWeek == 1)
    {
        weekNum -= 1;
    }

    // Using the first Thursday as starting week ensures that we are starting in the right year
    // then we add number of weeks multiplied with days
    var result = firstThursday.AddDays(weekNum * 7);

    // Subtract 3 days from Thursday to get Monday, which is the first weekday in ISO8601
    return result.AddDays(-3);
}       

3
J'ai essayé presque toutes les solutions données ici, celle-ci est la seule qui fonctionne correctement pour moi en ce moment. Actuellement, nous sommes le 7 février 2012. La semaine n ° 6 est la n ° 6. Ce code me donne correctement le 6 février comme date de début de la semaine. Les autres solutions m'ont toutes donné le 13 février, qui est en fait la date de début de la semaine 7.
HaukurHaf

1
Fonctionne comme un charme .. testé quelques données: pastebin.com/mfx8s1vq Tout fonctionne parfaitement! Merci Mikael!
Mittchel

1
@ RobinWassén-Andersson Bonne chose que vous avez revisité cette question alors: D 6 votes de plus et je vais attacher avec la réponse "pas" correcte hehe.
Mikael Svenson

2
Suggestion: indiquez «ISO8601» en rapport avec le nom de la méthode, car il n'y a tout simplement pas de réponse universelle «correcte».
Henk Holterman le

1
@Muflix Je ne connais pas assez SQL et les dates / calendriers pour savoir - mais faire CLR fonctionnera.
Mikael Svenson

36

J'aime la solution fournie par Henk Holterman. Mais pour être un peu plus indépendant de la culture, il faut avoir le premier jour de la semaine pour la culture actuelle (ce n'est pas toujours lundi):

using System.Globalization;

static DateTime FirstDateOfWeek(int year, int weekOfYear)
{
  DateTime jan1 = new DateTime(year, 1, 1);

  int daysOffset = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)jan1.DayOfWeek;

  DateTime firstMonday = jan1.AddDays(daysOffset);

  int firstWeek = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(jan1, CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule, CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek);

  if (firstWeek <= 1)
  {
    weekOfYear -= 1;
  }

  return firstMonday.AddDays(weekOfYear * 7);
}

J'ai ajouté ceci à Mannex, en tant que méthode d'extension à Calendar et DateTimeFormatInfo. J'ai également renvoyé à cette réponse pour le crédit.
Atif Aziz

1
Cela ne fonctionne pas correctement sur ma machine. Il montre une date avec 0010 au lieu de 2010. Je ne sais pas si c'est un problème dans le framework .net ou dans cette fonction. Nice try, anyway ...
Eduardo Xavier

6
firstMondayest un mauvais nom de variable pour quelque chose qui pourrait ne pas être un lundi. =)
mflodin

11

METTRE À JOUR : .NET Core 3.0 et .NET Standard 2.1 sont livrés avec ce type.

Bonnes nouvelles! Une pull request ajoutantSystem.Globalization.ISOWeek à .NET Core vient d'être fusionnée et est actuellement prévue pour la version 3.0. Espérons qu'il se propagera aux autres plates-formes .NET dans un avenir pas trop lointain.

Vous devriez pouvoir utiliser le ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek) méthode pour calculer cela.

Vous pouvez trouver le code source ici .


9

Le moyen le plus simple est probablement de trouver le premier lundi de l'année, puis d'ajouter le nombre de semaines pertinent. Voici un exemple de code. Il suppose un numéro de semaine commençant à 1, soit dit en passant:

using System;

class Test
{
    static void Main()
    {
        // Show the third Tuesday in 2009. Should be January 20th
        Console.WriteLine(YearWeekDayToDateTime(2009, DayOfWeek.Tuesday, 3));
    }

    static DateTime YearWeekDayToDateTime(int year, DayOfWeek day, int week)
    {
        DateTime startOfYear = new DateTime (year, 1, 1);

        // The +7 and %7 stuff is to avoid negative numbers etc.
        int daysToFirstCorrectDay = (((int)day - (int)startOfYear.DayOfWeek) + 7) % 7;

        return startOfYear.AddDays(7 * (week-1) + daysToFirstCorrectDay);
    }
}

4
Oui, mais le premier lundi de l'année pourrait appartenir à la semaine 52 | 53 de l'année précédente.
Henk Holterman

1
Cela dépend de la façon dont vous voulez définir les choses. Malheureusement, nous n'avons pas beaucoup d'informations sur ce sujet ... J'espère que cela vous sera utile.
Jon Skeet

3

Personnellement, je profiterais des informations sur la culture pour obtenir le jour de la semaine et revenir au premier jour de la semaine de la culture. Je ne sais pas si je l'explique correctement, voici un exemple:

    public DateTime GetFirstDayOfWeek(int year, int weekNumber)
    {
        return GetFirstDayOfWeek(year, weekNumber, Application.CurrentCulture);
    }

    public DateTime GetFirstDayOfWeek(int year, int weekNumber,
        System.Globalization.CultureInfo culture)
    {
        System.Globalization.Calendar calendar = culture.Calendar;
        DateTime firstOfYear = new DateTime(year, 1, 1, calendar);
        DateTime targetDay = calendar.AddWeeks(firstOfYear, weekNumber);
        DayOfWeek firstDayOfWeek = culture.DateTimeFormat.FirstDayOfWeek;

        while (targetDay.DayOfWeek != firstDayOfWeek)
        {
            targetDay = targetDay.AddDays(-1);
        }

        return targetDay;
    }

3

en utilisant Fluent DateTime http://fluentdatetime.codeplex.com/

        var year = 2009;
        var firstDayOfYear = new DateTime(year, 1, 1);
        var firstMonday = firstDayOfYear.Next(DayOfWeek.Monday);
        var weeksDateTime = 12.Weeks().Since(firstMonday);

2

Selon la norme ISO 8601: 1988 utilisée en Suède, la première semaine de l'année est la première semaine qui compte au moins quatre jours dans la nouvelle année.

Donc, si votre semaine commence un lundi, le premier jeudi de chaque année est dans la première semaine. Vous pouvez DateAdd ou DateDiff à partir de cela.


2

En supposant que le numéro de semaine commence à 1

DateTime dt =  new DateTime(YearNumber, 1, 1).AddDays((WeekNumber - 1) * 7 - (WeekNumber == 1 ? 0 : 1));
return dt.AddDays(-(int)dt.DayOfWeek);

Cela devrait vous donner le premier jour d'une semaine donnée. Je n'ai pas fait beaucoup de tests dessus, mais il semble que cela fonctionne. C'est une solution plus petite que la plupart des autres que j'ai trouvées sur le Web, je voulais donc la partager.


mauvaise réponse. L'utilisation de DateTime.Parse n'est pas requise car DateTime a un constructeur qui prend l'année, le mois et le jour. new DateTime(1,1,YearNumber)
Jamiec

Mise à jour du code pour créer un nouveau DateTime au lieu d'utiliser l'analyse. Il était tard. ;)
QuinnG

2

La bibliothèque de périodes de temps gratuite pour .NET comprend la semaine de classe conforme ISO 8601 :

// ----------------------------------------------------------------------
public static DateTime GetFirstDayOfWeek( int year, int weekOfYear )
{
  return new Week( year, weekOfYear ).FirstDayStart;
} // GetFirstDayOfWeek

2

Celui-ci a fonctionné pour moi, il a aussi l'avantage d'attendre un cultureinfo comme paramètre pour tester la formule avec différentes cultures. S'il est vide, il obtient les informations de culture actuelles ... les valeurs valides sont comme: "it", "en-us", "fr", ... et ainsi de suite. L'astuce consiste à soustraire le numéro de semaine du premier jour de l'année, qui peut être 1 pour indiquer que le premier jour se situe dans la première semaine. J'espère que cela t'aides.

Public Shared Function FirstDayOfWeek(ByVal year As Integer, ByVal weekNumber As Integer, ByVal culture As String) As Date
    Dim cInfo As System.Globalization.CultureInfo
    If culture = "" Then
        cInfo = System.Globalization.CultureInfo.CurrentCulture
    Else
        cInfo = System.Globalization.CultureInfo.CreateSpecificCulture(culture)
    End If
    Dim calendar As System.Globalization.Calendar = cInfo.Calendar
    Dim firstOfYear As DateTime = New DateTime(year, 1, 1, calendar)
    Dim firstDayWeek As Integer = calendar.GetWeekOfYear(firstOfYear, cInfo.DateTimeFormat.CalendarWeekRule, cInfo.DateTimeFormat.FirstDayOfWeek)
    weekNumber -= firstDayWeek
    Dim targetDay As DateTime = calendar.AddWeeks(firstOfYear, weekNumber)
    Dim fDayOfWeek As DayOfWeek = cInfo.DateTimeFormat.FirstDayOfWeek

    While (targetDay.DayOfWeek <> fDayOfWeek)
        targetDay = targetDay.AddDays(-1)
    End While
    Return targetDay
End Function

2

Voici une méthode compatible avec les numéros de semaine que Google Analytics, ainsi que le même schéma de numérotation que nous avons utilisé en interne chez Intel, et qui, j'en suis sûr, est également utilisé dans de nombreux autres contextes.

// Google Analytics does not follow ISO standards for date.
// It numbers week 1 starting on Jan. 1, regardless what day of week it starts on.
// It treats Sunday as the first day of the week.
// The first and last weeks of a year are usually not complete weeks.
public static DateTime GetStartDateTimeFromWeekNumberInYear(int year, uint weekOfYear)
{
  if (weekOfYear == 0 || weekOfYear > 54) throw new ArgumentException("Week number must be between 1 and 54! (Yes, 54... Year 2000 had Jan. 1 on a Saturday plus 53 Sundays.)");

  // January 1 -- first week.
  DateTime firstDayInWeek = new DateTime(year, 1, 1);
  if (weekOfYear == 1) return firstDayInWeek;

  // Get second week, starting on the following Sunday.      
  do
  {
    firstDayInWeek = firstDayInWeek.AddDays(1);
  } while (firstDayInWeek.DayOfWeek != DayOfWeek.Sunday);

  if (weekOfYear == 2) return firstDayInWeek;

  // Now get the Sunday of whichever week we're looking for.
  return firstDayInWeek.AddDays((weekOfYear - 2)*7);
}

2

J'ai essayé quelques codes ci-dessus et certains ont de petites erreurs, quand vous essayez différentes années avec différents jours de départ de la semaine, vous les verrez, j'ai pris le code de Jon Skeet, corrigez-le et ça marche, code très simple.

Public Function YearWeekDayToDateTime(ByVal year As Integer, ByVal weekDay As Integer, ByVal week As Integer) As DateTime
   ' weekDay, day you want
    Dim startOfYear As New DateTime(year, 1, 1)
    Dim startOfYearFixDay As Integer

    If startOfYear.DayOfWeek <> DayOfWeek.Sunday Then
        startOfYearFixDay = startOfYear.DayOfWeek
    Else
        startOfYearFixDay = 7
    End If

    Return startOfYear.AddDays((7 * (week)) - startOfYearFixDay + weekDay)
End Function

1

Code Mikael Svenson légèrement modifié. J'ai trouvé la semaine du premier lundi et changer le numéro de la semaine.

 DateTime GetFirstWeekDay(int year, int weekNum)
    {
        Calendar calendar = CultureInfo.CurrentCulture.Calendar;

        DateTime jan1 = new DateTime(year, 1, 1);

        int daysOffset = DayOfWeek.Monday - jan1.DayOfWeek;
        DateTime firstMonday = jan1.AddDays(daysOffset);
        int firstMondayWeekNum = calendar.GetWeekOfYear(firstMonday, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

        DateTime firstWeekDay = firstMonday.AddDays((weekNum-firstMondayWeekNum) * 7);

        return firstWeekDay;
    }

0

La semaine 1 est définie comme étant la semaine qui commence un lundi et contient le premier jeudi de l'année.


1
C'est une définition, il y en a d'autres.
Henk Holterman

1
La norme ISO définit la semaine 1 comme la semaine avec le premier jeudi de l'année.
RickardN


0

J'ai amélioré un peu la solution de Thomas avec un remplacement:

   public static DateTime FirstDateOfWeek(int year, int weekOfYear)
    {
      return Timer.FirstDateOfWeekOfMonth(year, 1, weekOfYear);
    }

    public static DateTime FirstDateOfWeekOfMonth(int year, int month, 
    int weekOfYear)
    {
      DateTime dtFirstDayOfMonth = new DateTime(year, month, 1);

       //I also commented out this part:
      /*
      if (firstWeek <= 1)
      {
        weekOfYear -= 1;
      }
      */

Sinon, la date précédait d'une semaine.

Merci Thomas, une aide précieuse.


0

J'ai utilisé l'une des solutions, mais elle m'a donné de mauvais résultats, simplement parce qu'elle compte le dimanche comme premier jour de la semaine.

J'ai changé:

var firstDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber - 1) * 7);
var lastDay = firstDay.AddDays(6);

à:

var lastDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber) * 7);
var firstDay = lastDay.AddDays(-6);

et maintenant cela fonctionne comme un charme.


1
Mais cela répond-il à la question du PO? C'est 1/1 plus un nombre fixe de jours. Pas de concept de premier jour de la semaine.
Gert Arnold

0

La solution proposée n'est pas complète - elle ne fonctionne que pour CalendarWeekRule.FirstFullWeek. Les autres types de règles hebdomadaires ne fonctionnent pas. Cela peut être vu en utilisant ce cas de test:

foreach (CalendarWeekRule rule in Enum.GetValues(typeof(CalendarWeekRule)))
{
    for (int year = 1900; year < 2000; year++)
    {
        DateTime date = FirstDateOfWeek(year, 1, rule);
        Assert(CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, rule, DayOfWeek.Monday) == 1);
        Assert(CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date.AddDays(-1), rule, DayOfWeek.Monday) != 1);
    }
}

0

J'ai fait une version raffinée de la solution proposée qui est plus simple et paramétre le firstDayOfWeek:

public static DateTime GetFirstDayOfWeek(int year, int week, DayOfWeek firstDayOfWeek)
{
    return GetWeek1Day1(year, firstDayOfWeek).AddDays(7 * (week - 1));
}

public static DateTime GetWeek1Day1(int year, DayOfWeek firstDayOfWeek)
{
    DateTime date = new DateTime(year, 1, 1);

    // Move towards firstDayOfWeek
    date = date.AddDays(firstDayOfWeek - date.DayOfWeek);

    // Either 1 or 52 or 53
    int weekOfYear = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFullWeek, firstDayOfWeek);

    // Move forwards 1 week if week is 52 or 53
    date = date.AddDays(7 * System.Math.Sign(weekOfYear - 1));

    return date;
}

0

c'est ma solution quand on veut calculer une date donnée année, numéro de semaine et jour de la semaine.

int Year = 2014;
int Week = 48;
int DayOfWeek = 4;

DateTime FecIni = new DateTime(Year, 1, 1);
FecIni = FecIni.AddDays(7 * (Week - 1));
if ((int)FecIni.DayOfWeek > DayOfWeek)
{
    while ((int)FecIni.DayOfWeek != DayOfWeek) FecIni = FecIni.AddDays(-1);
}
else
{
    while ((int)FecIni.DayOfWeek != DayOfWeek) FecIni = FecIni.AddDays(1);
}

0

J'ai simplifié le code fourni par Mikael Svensson qui est correct pour de nombreux pays d'Europe.

public static DateTime FirstDateOfWeekIso8601(int year, int week)
{
        var firstThursdayOfYear = new DateTime(year, 1, 1);
        while (firstThursdayOfYear.DayOfWeek != DayOfWeek.Thursday)
        {
            firstThursdayOfYear = firstThursdayOfYear.AddDays(1);
        }

        var startDateOfWeekOne = firstThursdayOfYear.AddDays(-(DayOfWeek.Thursday - DayOfWeek.Monday));

        return startDateOfWeekOne.AddDays(7 * (week - 1));        
}

0

J'ai écrit et testé le code suivant et fonctionne parfaitement bien pour moi. S'il vous plaît laissez-moi savoir si quelqu'un rencontre des problèmes avec cela, j'ai également publié une question afin d'obtenir la meilleure réponse possible. Quelqu'un peut le trouver utile.

public static DateTime GetFirstDateOfWeekByWeekNumber(int year, int weekNumber)
        {
            var date = new DateTime(year, 01, 01);
            var firstDayOfYear = date.DayOfWeek;
            var result = date.AddDays(weekNumber * 7);

            if (firstDayOfYear == DayOfWeek.Monday)
                return result.Date;
            if (firstDayOfYear == DayOfWeek.Tuesday)
                return result.AddDays(-1).Date;
            if (firstDayOfYear == DayOfWeek.Wednesday)
                return result.AddDays(-2).Date;
            if (firstDayOfYear == DayOfWeek.Thursday)
                return result.AddDays(-3).Date;
            if (firstDayOfYear == DayOfWeek.Friday)
                return result.AddDays(-4).Date;
            if (firstDayOfYear == DayOfWeek.Saturday)
                return result.AddDays(-5).Date;
            return result.AddDays(-6).Date;
        }

C'est faux - du moins pour de nombreux pays européens. GetFirstDateOfWeekByWeekNumber(2020, 29)pour la semaine 29 de 2020, cela revient 07/20/2020. Mais le premier jour de la 29e semaine était07/13/2020
Matthias Burger le

0

Actuellement, il n'existe aucune classe C # qui gère correctement les numéros de semaine ISO 8601. Même si vous pouvez instancier une culture, rechercher la chose la plus proche et corriger cela, je pense qu'il est préférable de faire le calcul complet vous-même:

    /// <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;
    }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.