Berechnen Sie das Datum aus der Wochennummer

Antworten:

251

Ich hatte Probleme mit der Lösung von @HenkHolterman, sogar mit dem Fix von @RobinAndersson.

Das Lesen des ISO 8601-Standards löst das Problem gut. Verwenden Sie den ersten Donnerstag als Ziel und nicht den Montag. Der folgende Code funktioniert auch für Woche 53 von 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);
}       
Mikael Svenson
quelle
3
Ich habe fast jede hier angegebene Lösung ausprobiert. Diese ist die einzige, die momentan für mich richtig funktioniert. Derzeit ist der 7. Februar 2012. Die Woche Nr. Ist # 6. Dieser Code gibt mir den 6. Februar als Startdatum der Woche korrekt an. Die anderen Lösungen gaben mir alle den 13. Februar, was eigentlich der Starttermin von Woche 7 ist.
HaukurHaf
1
Funktioniert wie ein Zauber .. hat ein paar Daten getestet: pastebin.com/mfx8s1vq Alle funktionieren einwandfrei! Danke Mikael!
Mittchel
1
@ RobinWassén-Andersson Gut, dass Sie diese Frage dann noch einmal beantwortet haben: D 6 weitere Stimmen und ich werde mich mit der "nicht" richtigen Antwort verbinden, hehe.
Mikael Svenson
2
Vorschlag: Machen Sie "ISO8601" pert des Methodennamens, da es einfach keine universelle "richtige" Antwort gibt.
Henk Holterman
1
@Muflix Ich weiß nicht genug über SQL und Daten / Kalender, um es zu wissen - aber CLR funktioniert.
Mikael Svenson
36

Ich mag die Lösung von Henk Holterman. Aber um ein bisschen kulturunabhängiger zu sein, muss man den ersten Tag der Woche für die aktuelle Kultur bekommen (es ist nicht immer Montag):

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);
}
Thomas
quelle
Ich habe dies zu Mannex hinzugefügt, als Erweiterungsmethode für Calendar und DateTimeFormatInfo. Ich habe auch auf diese Antwort verwiesen, um eine Gutschrift zu erhalten.
Atif Aziz
1
Auf meinem Computer funktioniert es nicht richtig. Es zeigt ein Datum mit 0010 anstelle von 2010. Ich weiß nicht, ob es ein Problem im .net-Framework oder in dieser Funktion ist. Jedenfalls ein guter Versuch ...
Eduardo Xavier
6
firstMondayist ein schlechter Variablenname für etwas, das möglicherweise kein Montag ist. =)
Mflodin
11

UPDATE : .NET Core 3.0 und .NET Standard 2.1 wurden mit diesem Typ ausgeliefert.

Gute Nachrichten! Eine Pull-Anfrage, die System.Globalization.ISOWeekzu .NET Core hinzugefügt wurde , wurde gerade zusammengeführt und ist derzeit für die Version 3.0 vorgesehen. Hoffentlich wird es in nicht allzu ferner Zukunft auf die anderen .NET-Plattformen übertragen.

Sie sollten in der Lage sein, die ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek)Methode zu verwenden, um dies zu berechnen.

Den Quellcode finden Sie hier .

Khellang
quelle
9

Am einfachsten ist es wahrscheinlich, den ersten Montag des Jahres zu finden und dann die entsprechende Anzahl von Wochen hinzuzufügen. Hier ist ein Beispielcode. Es wird übrigens eine Wochennummer angenommen, die bei 1 beginnt:

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);
    }
}
Jon Skeet
quelle
4
Ja, aber der erste Montag des Jahres könnte zur Woche 52 | 53 des Vorjahres gehören.
Henk Holterman
1
Es hängt davon ab, wie Sie Dinge definieren möchten. Leider haben wir hier nicht viele Informationen ... Ich hoffe, dass dies nützlich ist.
Jon Skeet
3

Persönlich würde ich die Kulturinformationen nutzen, um den Wochentag zu ermitteln und zum ersten Wochentag der Kultur zurückzukehren. Ich bin mir nicht sicher, ob ich es richtig erkläre. Hier ein Beispiel:

    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;
    }
Alexander Kahoun
quelle
3

Verwenden von 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);
Simon
quelle
2

Gemäß ISO 8601: 1988 , die in Schweden verwendet wird, ist die erste Woche des Jahres die erste Woche mit mindestens vier Tagen innerhalb des neuen Jahres.

Wenn Ihre Woche also an einem Montag beginnt, liegt der erste Donnerstag eines Jahres innerhalb der ersten Woche. Sie können DateAdd oder DateDiff daraus.

idstam
quelle
2

Angenommen, die Wochennummer beginnt bei 1

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

Dies sollte Ihnen den ersten Tag in einer bestimmten Woche geben. Ich habe nicht viel getestet, aber es sieht so aus, als ob es funktioniert. Es ist eine kleinere Lösung als die meisten anderen, die ich im Web gefunden habe, also wollte ich sie teilen.

QuinnG
quelle
falsche Antwort. Die Verwendung von DateTime.Parse ist nicht erforderlich, da DateTime über einen Konstruktor verfügt, der Jahr, Monat und Tag benötigt. new DateTime(1,1,YearNumber)
Jamiec
Der Code wurde aktualisiert, um eine neue DateTime zu erstellen, anstatt parse zu verwenden. Es war spät. ;)
QuinnG
2

Die freie Zeitperiode Bibliothek für .NET enthält die ISO - 8601 - Klasse entsprechen Woche :

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

quelle
2

Dieser hat bei mir funktioniert, er hat auch den Vorteil, dass er eine Kultur als Parameter erwartet, um die Formel mit verschiedenen Kulturen zu testen. Wenn leer, werden die aktuellen Kulturinformationen angezeigt. Gültige Werte sind: "it", "en-us", "fr", ... und so weiter. Der Trick besteht darin, die Wochennummer des ersten Tages des Jahres zu subtrahieren, dh 1, um anzuzeigen, dass der erste Tag innerhalb der ersten Woche liegt. Hoffe das hilft.

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
Paolo Marani
quelle
2

Hier ist eine Methode, die mit den Wochenzahlen von Google Analytics kompatibel ist, sowie das gleiche Nummerierungsschema, das wir intern bei Intel verwendet haben und das sicher auch in vielen anderen Kontexten verwendet wird.

// 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);
}
Samer Adra
quelle
2

Ich habe einige der oben genannten Codes ausprobiert und einige haben kleine Fehler. Wenn Sie verschiedene Jahre mit unterschiedlichen Starttagen der Woche ausprobieren, werden Sie sie sehen. Ich habe den Code von Jon Skeet genommen, ihn behoben und es funktioniert, sehr einfacher Code.

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
GEORGE BAXTER
quelle
1

Leicht geänderter Mikael Svenson Code. Ich fand die Woche des ersten Montags und änderte entsprechend die Wochennummer.

 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;
    }
Vitalij Roscinski
quelle
0

Woche 1 ist definiert als die Woche, die an einem Montag beginnt und den ersten Donnerstag des Jahres enthält.

Amaca
quelle
1
Das ist eine Definition, es gibt andere.
Henk Holterman
1
Die ISO-Norm definiert Woche 1 als die Woche mit dem ersten Donnerstag des Jahres.
RickardN
0

Ich habe die Lösung von Thomas mit einem Override ein wenig verbessert:

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

Ansonsten lag das Datum um eine Woche vor.

Danke Thomas, große Hilfe.

pasx
quelle
0

Ich habe eine der Lösungen verwendet, aber sie hat zu falschen Ergebnissen geführt, einfach weil der Sonntag als erster Tag der Woche gilt.

Ich habe mich verändert:

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

zu:

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

und jetzt wirkt es wie ein Zauber.

user1564287
quelle
1
Aber beantwortet dies die Frage des OP? Es ist 1/1 plus eine feste Anzahl von Tagen. Kein Konzept für den ersten Tag der Woche.
Gert Arnold
0

Die vorgeschlagene Lösung ist nicht vollständig - sie funktioniert nur für CalendarWeekRule.FirstFullWeek. Andere Arten von Wochenregeln funktionieren nicht. Dies kann anhand dieses Testfalls gesehen werden:

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);
    }
}
Colin Breame
quelle
0

Ich habe eine verfeinerte Version der vorgeschlagenen Lösung erstellt, die einfacher ist und den ersten Tag der Woche parametrisiert:

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;
}
Colin Breame
quelle
0

Dies ist meine Lösung, wenn wir ein Datum für ein bestimmtes Jahr, eine bestimmte Wochennummer und einen bestimmten Wochentag berechnen möchten.

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);
}
José Ignacio Becerra Becerra
quelle
0

Ich habe den von Mikael Svensson bereitgestellten Code vereinfacht, der für viele Länder in Europa korrekt ist.

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));        
}
Oskar Sjöberg
quelle
0

Ich habe den folgenden Code geschrieben und getestet und funktioniert für mich einwandfrei. Bitte lassen Sie mich wissen, wenn jemand Probleme damit hat. Ich habe auch eine Frage gestellt, um die bestmögliche Antwort zu erhalten. Jemand könnte es nützlich finden.

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;
        }
Lerner
quelle
Das ist falsch - zumindest für viele europäische Länder. GetFirstDateOfWeekByWeekNumber(2020, 29)für Woche 29 von 2020 kehrt dies zurück 07/20/2020. Aber der erste Tag der 29. Woche war07/13/2020
Matthias Burger
0

Derzeit gibt es keine C # -Klasse, die ISO 8601-Wochen-Nummern korrekt verarbeitet. Obwohl Sie eine Kultur instanziieren, nach dem nächsten suchen und das korrigieren können, ist es meiner Meinung nach besser, die vollständige Berechnung selbst durchzuführen:

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