Calculate date from week number
Asked Answered
D

26

160

Anyone know an easy way to get the date of the first day in the week (monday here in Europe). I know the year and the week number? I'm going to do this in C#.

Daemon answered 19/3, 2009 at 14:15 Comment(2)
Strongly related, perhaps duplicate: #659683Anaerobic
Possible duplicate of How do I get the month number from the year and week number in c#?Goldfarb
P
307

I had issues with the solution by @HenkHolterman even with the fix by @RobinAndersson.

Reading up on the ISO 8601 standard resolves the issue nicely. Use the first Thursday as the target and not Monday. The code below will work for Week 53 of 2009 as well.

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);
}       
Protochordate answered 30/1, 2012 at 13:55 Comment(13)
I've tried almost every solution given here, this one is the only one that correctly works for me right now. Currently, it's the 7th of Feb, 2012. The week nr is #6. This code correctly gives me the 6th of Feb as the start date of the week. The other solutions all gave me the 13th of Feb, which is actually the start date of week #7.Nil
Works Like a charm.. tested a few data: pastebin.com/mfx8s1vq All work flawless! Thanks Mikael!Dasha
My fix apparantly sucked since it broke for 2013. Thanks for a proper fix. :)Domitiladomonic
@RobinWassén-Andersson Good thing you revisited this question then :D 6 more votes and I'll tie with the "not" correct answer hehe.Protochordate
Suggestion: Make "ISO8601" pert of the method name, as there simply is no universal 'correct' answer.Masterstroke
Is it possible to rewrite this to SQL Server 2008 R2 or I have to use it as CLR assembly ?Orthoclase
@Orthoclase I don't know enough about SQL and dates/calendars to know - but doing CLR will work.Protochordate
The only issue I found is that this function does not take into account the DST. time The result date contains 1 hour in time part (for CET/CEST time zone). I think this amount of hours must be subtracted in result date.Zared
Shouldn't you use CultureInfo.InvariantCulture.Calendar since we specify the first day of the week anyway. (because ISO 8601 requires Monday as the start of the week)Footwork
@BojidarStanchev probably, but the result would be the same :)Protochordate
I had used this method and it bit me in the rear. This method works if the 1st day of the year is Sunday, Monday, Tuesday, Wednesday or Thursday. 2021 started on a Friday and 2022 started on a Saturday. Both 2021 and 2022 return the wrong date. Simple fix (confirmed with unit tests) was to change the following: if (firstWeek == 1) to if (firstWeek == 1 || firstWeek >= 52)Reticulation
@Reticulation can you explain a bite more? Passing in 2020-53, 2021-1, 2022-1, all return the correct date of the Monday for those weeks.Protochordate
Check also answers bellow as up-to-date approach is pushing upwards :-) !Percent
M
53

Note

The below answer uses the .NET Calendar rules. It does not promise ISO8601 conformance. See some of the other answers here when you need that. Week numbering is a mess, always try to find out what rules you need to follow first.


The code below correctly puts the start of week 1, 2009 at 29-12-2008. The CalendarWeekRule probably should be a parameter.

Note that the weekNum should be >= 1

static DateTime FirstDateOfWeek(int year, int weekNum, CalendarWeekRule rule)
{
    Debug.Assert(weekNum >= 1);

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

    int daysOffset = DayOfWeek.Monday - jan1.DayOfWeek;
    DateTime firstMonday = jan1.AddDays(daysOffset);
    Debug.Assert(firstMonday.DayOfWeek == DayOfWeek.Monday);

    var cal = CultureInfo.CurrentCulture.Calendar;
    int firstWeek = cal.GetWeekOfYear(firstMonday, rule, DayOfWeek.Monday);

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

    DateTime result = firstMonday.AddDays(weekNum * 7);

    return result;
}
Masterstroke answered 19/3, 2009 at 15:6 Comment(10)
I don't think this is entirely correct. Using CalendarWeekRule.FirstFourDayWeek and year 2006 and week 32, the correct date to be returned is 2006, 7th of august. But it returns the 14th of august. I changed the GetWeekOfYear call to use firstMonday instead of jan1 and it seemed to workAccidence
It doesn't work properly with the FirstDay rule; with this rule, week 1 should always start on the 1st of January. But FirstDateOfWeek(2011, 1, CalendarWeekRule.FirstDay) returns 27th of december 2010Amalee
Not only the mayans that had problems with 2012. Code misbehaved with the first week of 2012, changed cal.GetWeekOfYear(jan1, rule, DayOfWeek.Monday); to cal.GetWeekOfYear(firstMonday, rule, DayOfWeek.Monday); which corrected it.Domitiladomonic
@RobinAndersson This change will make it fail for Week 53 - 2009Protochordate
Posted an answer with code which apparently work for all boundary special cases.Protochordate
Thanks Mikael for the fix.Domitiladomonic
This code works well in some parts of the World I guess, but not for Iso 8601 calendars, like the Swedish one. Year 2008, week 1 will return jan 7 2008 but should return dec 31 2007 according to Iso 8601, even if CalendarWeekRule.FirstFourDayWeek is used as rule. Calendar coding is interesting ;)Galvanism
@Johan, see my answer and code below and you have working code for 2008, week 1 :)Protochordate
I've posted my answer that seems to work for even more boundary cases here I think I have an answer that works for all cultures. I had problems with all the other solutions proposed #16554378Villain
This works great. I use CalendarWeek.FirstFourdayWeek and a small test dataset, all ok. In addition, I added a DayOfWeek dayOfWeek parameter and returned modified result : return result.AddDays(dayOfWeek == DayOfWeek.Sunday ? 6 : (int)dayOfWeek - 1);Calling
G
44

UPDATE: .NET Core 3.0 and .NET Standard 2.1 has shipped with this type.

Good news! A pull request adding System.Globalization.ISOWeek to .NET Core was just merged and is currently slated for the 3.0 release. Hopefully it will propagate to the other .NET platforms in a not-too-distant future.

You should be able to use the ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek) method to calculate this.

You can find the source code here.

Goldfarb answered 19/6, 2018 at 10:29 Comment(0)
Z
38

I like the solution provided by Henk Holterman. But to be a little more culture independent, you have to get the first day of the week for the current culture ( it's not always monday ):

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);
}
Zuniga answered 27/5, 2009 at 10:7 Comment(3)
I've added this to Mannex, as an extension method to Calendar and DateTimeFormatInfo. I've also cross-referenced to this answer for credit.Alfreda
It doesn't work properly on my machine. It shows a date with 0010 instead of 2010. I don't know if it is a problem in .net framework or in this function. Nice try, anyway...Sciolism
firstMonday is a bad variable name for something that might not be a Monday. =)Theolatheologian
O
10

The easiest way is probably to find the first Monday of the year, and then add the relevant number of weeks. Here's some sample code. It assumes a week number starting at 1, by the way:

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);
    }
}
Organzine answered 19/3, 2009 at 14:18 Comment(2)
Yep, but the first monday of the year could belong to week 52|53 of the previous year.Masterstroke
It depends how you want to define things. Unfortunately we don't have a lot of information to go on here... I'm hoping this is useful though.Organzine
J
4

Personally I'd take advantage of the culture info to get the day of the week and loop down to the culture's first day of the week. I'm not sure if I'm explaining it properly, here's an example:

    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;
    }
Jecoa answered 19/3, 2009 at 15:32 Comment(0)
N
4

using 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);
Nephoscope answered 4/9, 2009 at 12:50 Comment(0)
I
4

Here is a method that is compatible with the week numbers that Google Analytics, and also the same numbering scheme we used internally at Intel, and which I'm sure is also used in a lot of other contexts.

// 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);
}
Infanticide answered 26/2, 2014 at 19:35 Comment(0)
B
3

Lightly changed Mikael Svenson code. I found the week of the first monday and appropriate change the week number.

 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;
    }
Bunker answered 6/2, 2017 at 13:25 Comment(0)
S
2

According to ISO 8601:1988 that is used in Sweden the first week of the year is the first week that has at least four days within the new year.

So if your week starts on a Monday the first Thursday any year is within the first week. You can DateAdd or DateDiff from that.

Sweet answered 19/3, 2009 at 16:10 Comment(0)
P
2

Assuming the week number starts at 1

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

This should give you the first day in any given week. I haven't done a lot of testing on it, but looks like it works. It's smaller solution than most other's I found on the web, so wanted to share.

Prelate answered 9/7, 2010 at 3:43 Comment(2)
bad answer. Using DateTime.Parse is not required as DateTime has a constructor which takes year, month & day. new DateTime(1,1,YearNumber)Deprived
Updated the code to create a new DateTime instead of using parse. It was late. ;)Prelate
D
2

The free Time Period Library for .NET includes the ISO 8601 conform class Week:

// ----------------------------------------------------------------------
public static DateTime GetFirstDayOfWeek( int year, int weekOfYear )
{
  return new Week( year, weekOfYear ).FirstDayStart;
} // GetFirstDayOfWeek
Doited answered 13/5, 2011 at 17:7 Comment(0)
L
2

I tried some codes above and some have small mistakes, when you try different years with different starting days of week you will see them, I took the code of Jon Skeet, fix it and it works, very simple 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
Loader answered 26/8, 2011 at 10:13 Comment(0)
A
2

This one worked for me, it also have the advantage of expecting a cultureinfo as parameter to test the formula with different cultures. If empty, it gets the current culture info... valid values are like: "it", "en-us", "fr", ... ando so on. The trick is to subtract the week number of the first day of the year, that may be 1 to indicate that the first day is within the first week. Hope this helps.

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
Anson answered 29/3, 2013 at 17:58 Comment(0)
G
1

I simplified the code Mikael Svensson provided which is correct for many countries in 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));        
}
Gigantean answered 10/7, 2015 at 10:39 Comment(0)
B
1

As suggested by khellang (sorry but I cannot add comments), with .NET Core is quite simple. This is my piece of code:

var today = DateTime.Today;
CultureInfo myCI = new CultureInfo("it-IT");
Calendar myCal = myCI.Calendar;
var weekNumber = myCal.GetWeekOfYear(today, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
var firstWeekDay = ISOWeek.ToDateTime(today.Year, weekNumber, DayOfWeek.Monday);
Berstine answered 17/3, 2022 at 19:56 Comment(0)
F
0

Week 1 is defined as being the week that starts on a Monday and contains the first Thursday of the year.

Freddie answered 19/3, 2009 at 15:3 Comment(2)
That's one definition, there are others.Masterstroke
ISO standard define week 1 as the week with the year's first Thursday in it.Zapata
B
0

To convert in both directions, see here: Wikipedia article on ISO week dates

Bewhiskered answered 5/9, 2009 at 5:35 Comment(0)
P
0

I improved a little on Thomas' solution with an override:

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

Otherwise the date was preceding by one week..

Thank you Thomas, great help.

Prosaism answered 30/3, 2011 at 5:29 Comment(0)
T
0

I used one of the solutions but it gave me wrong results, simply because it counts Sunday as a first day of the week.

I changed:

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

to:

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

and now it is working as a charm.

Trude answered 9/8, 2012 at 16:10 Comment(1)
But does this answer the OP's question? It's 1/1 plus a fixed number of days. No first day of the week concept.Vertex
M
0

The proposed solution is not complete - it only works for CalendarWeekRule.FirstFullWeek. Other types of week rules do not work. This can be seen using this test case:

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);
    }
}
Mello answered 21/9, 2012 at 17:57 Comment(0)
M
0

I have made a refined version of the proposed solution that is a simpler and parametrises the 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;
}
Mello answered 21/9, 2012 at 18:8 Comment(0)
S
0

this is my solution when we want to calculate a date given year, week number and day of the week.

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);
}
Sandy answered 27/11, 2014 at 15:2 Comment(0)
B
0

I have written and tested the following code and is working perfectly fine for me. Please let me know if anyone face trouble with this, I have posted a question as well in order to get the best possible answer. Someone may find it useful.

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;
        }
Broderickbrodeur answered 4/11, 2015 at 4:35 Comment(1)
This is wrong - at least for a lot of European countries. GetFirstDateOfWeekByWeekNumber(2020, 29) for Week 29 of 2020 this returns 07/20/2020. But first day of 29th week was 07/13/2020Felix
D
0

Currently, there is no C# class that correctly handles ISO 8601week numbers. Even though you can instantiate a culture, look for the closest thing and correct that, I think it is better to do the complete calculation yourself:

    /// <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;
    }
Dramatist answered 8/11, 2016 at 15:48 Comment(0)
D
0

One of the biggest problems I found was to convert from weeks to dates, and then from dates to weeks.

The main problem is when trying to get the correct week year from a date that belongs to a week of the previous year. Luckily System.Globalization.ISOWeek.GetYear handles this.

Here is my solution:

public class WeekOfYear
{
    public static (int Year, int Week) DateToWeekOfYear(DateTime date) =>
        (ISOWeek.GetYear(date), ISOWeek.GetWeekOfYear(date));

    public static bool ValidYearAndWeek(int year, int week) =>
           year >= 1 && year <= 9999 && week >= 1 && week <= 53 // bounds of year/week
        && !(year <= 1 && week <= 1) && !(year >= 9999 && week >= 53); // bounds of DateTime

    public int Year { get; }
    public int Week { get; }
    public virtual DateTime StartOfWeek { get; protected set; }
    public virtual DateTime EndOfWeek { get; protected set; }

    public virtual IEnumerable<DateTime> DaysInWeek =>
        Enumerable.Range(1, 10).Select(i => StartOfWeek.AddDays(i));

    public WeekOfYear(int year, int week)
    {
        if (!ValidYearAndWeek(year, week))
            throw new ArgumentException($"DateTime can't represent {week} of year {year}.");

        Year = year;
        Week = week;
        StartOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Monday);
        EndOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Sunday).AddDays(1).AddTicks(-1);
    }

    public WeekOfYear((int Year, int Week) week) : this(week.Year, week.Week) { }
    public WeekOfYear(DateTime date) : this(DateToWeekOfYear(date)) { }
}

The second biggest problem was the preference for weeks starting on Sundays in the US.

The solution I cam up with subclasses WeekOfYear from above, and manages the offset of the in the constructor (which converts week to dates) and DateToWeekOfYear (which converts from date to week).

public class UsWeekOfYear : WeekOfYear
{
    public static new (int Year, int Week) DateToWeekOfYear(DateTime date)
    {
        // if date is a sunday, return the next week
        if (date.DayOfWeek == DayOfWeek.Sunday) date = date.AddDays(1);
        return WeekOfYear.DateToWeekOfYear(date);
    }

    public UsWeekOfYear(int year, int week) : base(year, week)
    {
        StartOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Monday).AddDays(-1);
        EndOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Sunday).AddTicks(-1);
    }

    public UsWeekOfYear((int Year, int Week) week) : this(week.Year, week.Week) { }
    public UsWeekOfYear(DateTime date) : this(DateToWeekOfYear(date)) { }
}

Here is some test code:

public static void Main(string[] args)
{
    Console.WriteLine("== Last Week / First Week");
    Log(new WeekOfYear(2020, 53));
    Log(new UsWeekOfYear(2020, 53));

    Log(new WeekOfYear(2021, 1));
    Log(new UsWeekOfYear(2021, 1));

    Console.WriteLine("\n== Year Crossover (iso)");
    var start = new DateTime(2020, 12, 26);
    var i = 0;
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-26 - Sat
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-27 - Sun
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-28 - Mon
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-29 - Tue
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-30 - Wed
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-30 - Thu
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-01 - Fri
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-02 - Sat
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-03 - Sun
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-04 - Mon
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-05 - Tue
    Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-06 - Wed

    Console.WriteLine("\n== Year Crossover (us)");
    i = 0;
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-26 - Sat
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-27 - Sun
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-28 - Mon
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-29 - Tue
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-30 - Wed
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-30 - Thu
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-01 - Fri
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-02 - Sat
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-03 - Sun
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-04 - Mon
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-05 - Tue
    Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-06 - Wed

    var x = new UsWeekOfYear(2020, 53) as WeekOfYear;
}

public static void Log(WeekOfYear week)
{
    Console.WriteLine($"{week} - {week.StartOfWeek:yyyy-MM-dd} ({week.StartOfWeek:ddd}) - {week.EndOfWeek:yyyy-MM-dd} ({week.EndOfWeek:ddd})");
}

public static void Log(DateTime date, WeekOfYear week)
{
    Console.WriteLine($"{date:yyyy-MM-dd (ddd)} - {week} - {week.StartOfWeek:yyyy-MM-dd (ddd)} - {week.EndOfWeek:yyyy-MM-dd (ddd)}");
}
Deference answered 29/1, 2021 at 4:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.