Robin
Robin

Reputation: 800

Get number of ISO weeks in given year in C#

I'm trying to get the number of ISO weeks in given year/leap year. I saw that .Net Core 3.0 have ISOWeek.GetWeeksInYear class, but we're running .Net Core 2.2.1... and it's not possible to update at the moment...

I've tried this:

var cultInfo = CultureInfo.CurrentCulture;
var lastDayOfYear = new DateTime(2019, 12, 31);
var numberOfWeeks = cultInfo.Calendar.GetWeekOfYear(
                                    lastDayOfYear,
                                    cultInfo.DateTimeFormat.CalendarWeekRule,
                                    DayOfWeek.Monday);

With 2019 it returns 53, it's not correct by the result I wanted (52). And if I change the date to 2020 it is 53, which is correct, but yeah, not by ISO which is what I want.

Thanks in advance.

Upvotes: 1

Views: 503

Answers (2)

Pavel Anikhouski
Pavel Anikhouski

Reputation: 23228

You can have a look at ISOWeek sources and implement this method by yourself (since you can't target .NET Core 3.x)

private const int WeeksInLongYear = 53;
private const int WeeksInShortYear = 52;

public static int GetWeeksInYear(int year)
{
    if (year < 1 || year > 9999)
    {
        throw new ArgumentOutOfRangeException(nameof(year));
    }

    static int P(int y) => (y + (y / 4) - (y / 100) + (y / 400)) % 7;

    if (P(year) == 4 || P(year - 1) == 3)
    {
        return WeeksInLongYear;
    }

    return WeeksInShortYear;
}

Then use it

var weeks = GetWeeksInYear(2019); //returns 52
weeks = GetWeeksInYear(2020); //returns 53

Please, keep in mind, that static local functions are supported starting from C# 8, so you should switch to this version or rewrite a code a little bit

Upvotes: 3

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186668

A year contains 53 ISO weeks if and only if it contains 53 Thursdays; so in order to return 53 a year must either

  1. Start from Thursday
  2. Be a leap year and starts either from Thursday or Wednesday

Code:

public static int GetWeeksInYear(int year) {
  if (year < DateTime.MinValue.Year || year > DateTime.MaxValue.Year)
    throw new ArgumentOutOfRangeException(nameof(year));

  var dw = new DateTime(year, 1, 1).DayOfWeek;

  return (dw == DayOfWeek.Thursday) || 
         (dw == DayOfWeek.Wednesday) && DateTime.IsLeapYear(year) 
    ? 53
    : 52;
}

Demo:

  var report = string.Join(Environment.NewLine, Enumerable
    .Range(1990, 2020 - 1990 + 1)
    .Select(year => $"{year} : {GetWeeksInYear(year)}"));

  Console.Write(report);

Outcome:

1990 : 52
1991 : 52
1992 : 53
1993 : 52
1994 : 52
1995 : 52
1996 : 52
1997 : 52
1998 : 53
1999 : 52
2000 : 52
2001 : 52
2002 : 52
2003 : 52
2004 : 53
2005 : 52
2006 : 52
2007 : 52
2008 : 52
2009 : 53
2010 : 52
2011 : 52
2012 : 52
2013 : 52
2014 : 52
2015 : 53
2016 : 52
2017 : 52
2018 : 52
2019 : 52
2020 : 53

Upvotes: 2

Related Questions