293 Stimmen

Erhalte die korrekte Kalenderwoche eines bestimmten Datums

Ich habe viel gegoogelt und viele Lösungen gefunden, aber keine davon gibt mir die richtige Wochennummer für den 31.12.2012. Selbst das Beispiel auf MSDN (link) funktioniert nicht.

Der 31.12.2012 ist ein Montag, daher sollte es Woche 1 sein, aber jede Methode, die ich ausprobiert habe, gibt mir 53. Hier sind einige der Methoden, die ich ausprobiert habe:

Aus der MSDN-Bibliothek:

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

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

Lösung 2:

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

Lösung 3:

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

Aktualisierung

Die folgende Methode gibt tatsächlich 1 zurück, wenn das Datum der 31.12.2012 ist. Mit anderen Worten, mein Problem war, dass meine Methoden den ISO-8601-Standard nicht eingehalten haben.

// Das setzt voraus, dass die Wochen mit Montag beginnen.
// Woche 1 ist die 1. Woche des Jahres mit einem Donnerstag darin.
public static int GetIso8601WeekOfYear(DateTime time)
{
    // Ernsthaftes Schummeln. Wenn es Montag, Dienstag oder Mittwoch ist, wird es
    // die gleiche Wochennummer wie der Donnerstag, Freitag oder Samstag sein,
    // und wir bekommen diese immer richtig
    DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

    // Gib die Woche unseres angepassten Tages zurück
    return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}

384voto

il_guru Punkte 8073

Wie auf dieser MSDN-Seite festgestellt, gibt es einen leichten Unterschied zwischen der ISO8601-Woche und der .Net-Wochennummerierung.

Sie können in diesem Artikel im MSDN-Blog eine bessere Erklärung finden: "ISO 8601 Week of Year Format in Microsoft .Net"

Einfach ausgedrückt erlaubt .Net Wochen, die sich über Jahre hinweg erstrecken, während der ISO-Standard dies nicht tut. In dem Artikel gibt es auch eine einfache Funktion, um die korrekte ISO 8601 Wochennummer für die letzte Woche des Jahres zu erhalten.

Update Die folgende Methode gibt tatsächlich 1 für 2012-12-31 zurück, was in ISO 8601 (z.B. Deutschland) korrekt ist.

// Dies setzt voraus, dass Wochen mit Montag beginnen.
// Woche 1 ist die 1. Woche des Jahres mit einem Donnerstag darin.
public static int GetIso8601WeekOfYear(DateTime time)
{
    // Ganz schön gemein. Wenn es Montag, Dienstag oder Mittwoch ist, wird es die gleiche Wochennummer haben wie der Donnerstag, Freitag oder Samstag,
    // und dazu bekommen wir immer die richtigen Tage
    DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

    // Gib die Woche unseres angepassten Tages zurück
    return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}

74voto

khellang Punkte 16548

Gute Nachrichten! Ein Pull-Request zur Hinzufügung von System.Globalization.ISOWeek zu .NET Core wurde gerade zusammengeführt und ist derzeit für die Version 3.0 geplant. Hoffentlich wird es in naher Zukunft auch auf den anderen .NET-Plattformen verfügbar sein.

Der Typ hat die folgende Signatur, die die meisten ISO-Woche-Anforderungen abdecken sollte:

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

Sie können den Quellcode hier finden.

UPDATE: Diese APIs wurden auch in die Version 2.1 von .NET Standard aufgenommen.

37voto

Christophe Geers Punkte 7757

Es kann mehr als 52 Wochen in einem Jahr geben. Jedes Jahr hat 52 volle Wochen + 1 oder +2 (Schaltjahr) zusätzliche Tage. Sie ergeben eine 53. Woche.

  • 52 Wochen * 7 Tage = 364 Tage.

Also haben Sie für jedes Jahr mindestens einen zusätzlichen Tag. Zwei für Schaltjahre. Werden diese zusätzlichen Tage als separate Wochen gezählt?

Die Anzahl der Wochen hängt wirklich davon ab, an welchem Tag Ihre Woche beginnt. Lassen Sie uns das für das Jahr 2012 betrachten.

  • USA (Sonntag -> Samstag): 52 Wochen + eine kurze 2-Tage-Woche für 2012-12-30 & 2012-12-31. Dies ergibt insgesamt 53 Wochen. Die letzten beiden Tage dieses Jahres (Sonntag + Montag) bilden ihre eigene kurze Woche.

Überprüfen Sie die aktuellen Kultureinstellungen, um zu sehen, welcher Tag als erster Tag der Woche verwendet wird.

Wie Sie sehen, ist es normal, 53 als Ergebnis zu bekommen.

  • Europa (Montag -> Sonntag): Der 2. Januar (2012-1-2) ist der erste Montag, also ist dies der erste Tag der ersten Woche. Fragen Sie nach der Wochennummer für den 1. Januar und Sie erhalten zurück 52, da es als Teil der letzten Woche des Jahres 2011 betrachtet wird.

Es ist sogar möglich, eine 54. Woche zu haben. Alle 28 Jahre, wenn der 1. Januar und der 31. Dezember als separate Wochen behandelt werden. Es muss auch ein Schaltjahr sein.

Zum Beispiel hatte das Jahr 2000 54 Wochen. Der 1. Januar (Sa) war der erste Wochentag und der 31. Dezember (So) war der zweite Wochentag.

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("Jahr: {0} Woche: {1}", year, weekNum);

Gibt aus: Jahr: 2012 Woche: 54

Ändern Sie in obigem Beispiel CalendarWeekRule in FirstFullWeek oder FirstFourDayWeek um und Sie erhalten 53 zurück. Lassen Sie den Starttag auf Montag, da wir mit Deutschland arbeiten.

Also beginnt Woche 53 am Montag, 31. Dezember 2012, dauert einen Tag und endet dann.

53 ist die richtige Antwort. Ändern Sie die Kultur in Deutschland, wenn Sie es ausprobieren möchten.

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

26voto

daniele3004 Punkte 11818

So geht's:

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

Am wichtigsten ist der CalendarWeekRule Parameter.

Hier sehen Sie: [https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=IT-IT&k=k(System.Globalization.CalendarWeekRule);k(TargetFrameworkMoniker-.NETFramework](https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=IT-IT&k=k(System.Globalization.CalendarWeekRule);k(TargetFrameworkMoniker-.NETFramework)

13voto

realbart Punkte 2975

Da anscheinend keine .Net-Kultur existiert, die die korrekte ISO-8601-Wochennummer liefert, umgehe ich lieber die integrierte Wochenbestimmung vollständig und führe die Berechnung manuell durch, anstatt zu versuchen, ein teilweise korrektes Ergebnis zu korrigieren.

Was ich schließlich erstellt habe, ist die folgende Erweiterungsmethode:

/// 
/// Konvertiert ein Datum in eine Wochennummer.
/// ISO 8601 Woche 1 ist die Woche, die den ersten Donnerstag dieses Jahres enthält.
/// 
public static int ToIso8601Weeknumber(this DateTime date)
{
    var donnerstag = date.AddDays(3 - date.DayOfWeek.DayOffset());
    return (donnerstag.DayOfYear - 1) / 7 + 1;
}

/// 
/// Konvertiert eine Wochennummer in ein Datum.
/// Hinweis: Woche 1 eines Jahres kann im vorherigen Jahr beginnen.
/// ISO 8601 Woche 1 ist die Woche, die den ersten Donnerstag dieses Jahres enthält, also
/// wenn der 28. Dezember ein Montag ist, der 31. Dezember ein Donnerstag ist,
/// und Woche 1 beginnt am 4. Januar.
/// Wenn der 28. Dezember ein späterer Tag in der Woche ist, beginnt Woche 1 früher.
/// Wenn der 28. Dezember ein Sonntag ist, ist er in derselben Woche wie der Donnerstag, 1. Januar.
/// 
public static DateTime FromIso8601Weeknumber(int weekNumber, int? year = null, DayOfWeek day = DayOfWeek.Monday)
{
    var dez28 = new DateTime((year ?? DateTime.Today.Year) - 1, 12, 28);
    var montag = dez28.AddDays(7 * weekNumber - dez28.DayOfWeek.DayOffset());
    return montag.AddDays(day.DayOffset());
}

/// 
/// Iso8601 Wochen beginnen am Montag. Dies gibt 0 für Montag zurück.
/// 
private static int DayOffset(this DayOfWeek wochentag)
{
    return ((int)wochentag + 6) % 7;
}

Zunächst bestimmt ((int)date.DayOfWeek + 6) % 7) die Wochentagsnummer, 0=Montag, 6=Sonntag.

date.AddDays(-((int)date.DayOfWeek + 6) % 7) bestimmt das Datum des Montags vor der angeforderten Wochennummer.

Drei Tage später ist der Ziel-Donnerstag, der bestimmt, in welchem Jahr die Woche liegt.

Wenn Sie die (nullbasierte) Tagesnummer innerhalb des Jahres durch sieben teilen (abgerundet), erhalten Sie die (nullbasierte) Wochennummer im Jahr.

In c# erfolgt die Ganzzahldivision implizit abgerundet.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X