Veckonummer i .NET

Inledning

Att jobba med datum och tid kan vara en utmaning. Att räkna med datum och tider kan vara helt galet komplicerat. Som videon nedan går igenom så kan det vara löjligt och underhållande att ens sätta sig in i alla utmaningar.

Nu ska vi i denna artikel analysera lite om hur veckonummer beräknas och höja ett varningens finger för att använda färdiga lösningar utan att kontrollera om de stämmer. Men först titta gärna på denna klassiker från Computerphile.

Svenska veckonummer

Okej, hur svårt kan det vara med veckonummer?

I Sverige, och en del andra länder, så gillar vi att använda veckonummer. I USA så är inte detta speciellt vanligt. Det finns såklart en standard för hur veckonumreringen ska gå till. Den heter ISO8601. På Wikipedia finns en förtydande förklaring.

Problemet med numreringen sker såklart vid årsskiftet.

  • Vilken dag är veckans första dag?
  • När börjar egentligen vecka 1?

Dessa är viktiga frågor kopplade till reglerna för att sätta korrekt veckonummer. Dagarna innan nyår och direkt efter kan ha nummer 52, 53 eller 1 lite beroende på reglerna.

ChatGTP

Om vi frågar ChatGTP så erbjuds en lösning som även förekommer om du Googlar en lösning på detta nämligen:

VARNING
var info = new System.Globalization.CultureInfo("sv-SE");
int weekNr = info.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

Detta borde ju fungera eller? Det är ju .NET och vi använder Svenska inställningar för att beräkna veckonummer?

Svar: NEJ

Som vi snart ska se så kommer denna lösning att ge fel svar ibland. Minns ni de två frågor som listades tidigare i artikeln? Det stämmer att Måndag är den dag som börjar en vecka. Men inställningen FirstFourDayWeek har förklaringen ”Indicates that the first week of the year is the first week with four or more days before the designated first day of the week”. Stämmer inte med ISO8601 regeln (förenklat); att den vecka som innehåller den första torsdagen på året är vecka 1.

Förvirrande? Det handlar om när vecka 53 ska användas eller om sista dagarna på året innan istället hör till vecka 1.

Lösning

Nu för tiden så används ChatGTP med all rätt för att snabbare ge kodlösningar. Nyligen kom en analys som sa att ChatGTP faktiskt svarar fel i ungefär 50% av alla frågor om kod. Det lär ju ändra på sig snabbt men som jag sa tidigare så bör man vara vaksam i vissa situationer.

Det roliga är att om man pressar ChatGTP och är specifik så kan man få ett svar som faktiskt fungerar:

Veckonummer - Fungerande!
// Function to get ISO-8601 week number for a given date
public int GetIso8601WeekOfYear(DateTime date)
{
    DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(date);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        date = date.AddDays(3);
    }

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

Här justeras helt enkelt datumet med att lägga till 3 dagar om datumet som efterfrågas är en måndag, tisdag eller onsdag. Detta då första veckan är den veckan som innehåller första torsdagen på året.

Verifiering

Låt oss avsluta med att analyser skillnaderna mellan lösningarna. Vi skriver en liten snurra som testar dagarna innan årsskiftet och dagarna efter.

Veckonummer - Test
var info = new System.Globalization.CultureInfo("sv-SE");

for (int year = 1980; year <= 2060; year++)
{
    int month = 12;

    for (int day = 27; day <= 31; day++)
    {
        var date= new DateTime(year, month, day);
        int t1 = GetIso8601WeekOfYear(date);
        int t2 = info.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

        if(t1 != t2)
            Console.WriteLine($"Error: {t1} - {t2} : {date}");
    }
    month = 1;

    for (int day = 1; day <= 7; day++)
    {
        var date = new DateTime(year, month, day);
        int t1 = GetIso8601WeekOfYear(date);
        int t2 = info.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

        if (t1 != t2)
            Console.WriteLine($"Error: {t1} - {t2} : {date}");
    }
}

Det är inte den snyggaste test-koden men den avslöjar ändå skillnader:

Fellogg
Error: 1 - 53 : 2018-12-31 00:00:00
Error: 1 - 53 : 2019-12-30 00:00:00
Error: 1 - 53 : 2019-12-31 00:00:00
Error: 1 - 53 : 2024-12-30 00:00:00
Error: 1 - 53 : 2024-12-31 00:00:00
Error: 1 - 53 : 2025-12-29 00:00:00
Error: 1 - 53 : 2025-12-30 00:00:00
Error: 1 - 53 : 2025-12-31 00:00:00

Alltså den ”enklare” lösningen som .NET står för säger vecka 53 i en del fall då det är vecka 1 som gäller! Då vecka 1 börjar före nyår.

Vi avslutar med en bild från veckonr.se i slutet av 2024 som faktiskt visar att det är vecka 1 och inte vecka 53 som gäller för de sista dagarna 2024!

Scroll to top