'first occurrence of day of week in month

I want to configure a Event with a DateTime date.

I want to repeat the event every month in the same week and day of the week.

For examples, this event take place in April 2 2015, (thursday of the first week in April).

I want to repeat again every thursday on first week in every month.

How I can calculate the next date each time?



Solution 1:[1]

I developed a little function that finds any nth day of a month.

private static DateTime Next(int year, int month, DayOfWeek dayOfWeek, int occurrence)
        {
            return Enumerable.Range(1, 7).
                        Select(day => new DateTime(year, month, day)).
                        First(dateTime => (dateTime.DayOfWeek == dayOfWeek))
                        .AddDays(7 * (occurrence - 1));
        }

Solution 2:[2]

Method I developed for my project. It is having all edge cases added and working in production now.

        /// <summary>
        /// Calculates and returns the date from specifed month based on weekday and week number passed in.
        /// </summary>
        /// <param name="month">any date of the month as base referece</param>
        /// <param name="dayOfWeek">day of week Mon/Tues/Wed/Thu/Fri etc..</param>
        /// <param name="requiredWeek">!st wqeek = 1,2nd week =2,3,4,5,last etc</param>
        /// <returns></returns>
        public static DateTime GetFirstOccuringWeekDayFromMonth(DateTime month, DayOfWeek dayOfWeek, ReccurrenceEnum requiredWeek)
        {
            if (requiredWeek == ReccurrenceEnum.First || requiredWeek == ReccurrenceEnum.Second
                || requiredWeek == ReccurrenceEnum.Third || requiredWeek == ReccurrenceEnum.Forth || requiredWeek == ReccurrenceEnum.Last)
            {
                DateTime current;
                int requiredweekno = (int)requiredWeek;
                int foundDayInstanceCount = 0;
                if (requiredWeek == ReccurrenceEnum.Last)
                {
                    DateTime lastDayofMonth = new DateTime(month.AddMonths(1).Year, month.AddMonths(1).Month, 1).AddDays(-1);
                    DateTime firstDayofMonth = new DateTime(month.Year, month.Month, 1);
                    current = lastDayofMonth;
                    //skip until required day is reached.
                    while (current.DayOfWeek != dayOfWeek && current >= firstDayofMonth)
                        current = current.AddDays(-1);
                    return current;
                }
                else
                {
                    DateTime firstDayofMonth = new DateTime(month.Year, month.Month, 1);
                    DateTime lastDayofMonth = new DateTime(month.AddMonths(1).Year, month.AddMonths(1).Month, 1).AddDays(-1);
                    current = firstDayofMonth;
                    if (current.DayOfWeek == dayOfWeek)
                        foundDayInstanceCount++;
                    //skip until required week is reached.
                    while (foundDayInstanceCount < requiredweekno && current <= lastDayofMonth)
                    {
                        current = current.AddDays(1);
                        if (current.DayOfWeek == dayOfWeek)
                            foundDayInstanceCount++;
                    }
                    //skip until required day is reached.
                    while (current.DayOfWeek != dayOfWeek && current <= lastDayofMonth)
                        current = current.AddDays(1);
                    return current;
                }
            }
            else
                throw new Exception("Exception, wrong week recurrence specified!");
        }

public enum ReccurrenceEnum
    {
        First = 1,
        Second = 2,
        Third = 3,
        Forth = 4,
        Last = 5,
    }

Solution 3:[3]

Here is what I made, without a loop in the code, it's based on the question Find The date last sunday... which calculates the last (any day you want) week day of a month.

public static DateTime getFirstDayWeekOfMonth(DateTime fromDT, DayOfWeek wantedDay)
{
    int wantedDayIndex = (int)wantedDay;
    int firstDayIndex = (int)fromDT.DayOfWeek;

    DateTime newTime = fromDT.AddDays(
        firstDayIndex > wantedDayIndex
        ? (7 - firstDayIndex) + (wantedDayIndex)
        : wantedDayIndex - firstDayIndex
    );                

    return newTime;
}

The principle of this is the index of the days (a value from 0 to 6 in C# ) and the distance between those indices.

If you set a circular list using values from 0 to 6, you'll need to count the distance between two DayIndexs (always forward).

The solution is not a list, just get the concept.

You calculate the distance from start index to 6, plus the distance from 0 to the start index. This will be true if the DayIndex of the first day of the month is greater than the indexWanted. Else, just need the distance from 0 to DayIndex of the first day of the month.

Plus, I made the test cases for both functions (first and last day of a month). They were made in NUnit C#.

[TestFixture]
public class TESTLastFirst
{

[Test]
public void LastWeekDayOfMonthTest()
{
    for (int j = 1; j < 12; j++)
    {
        DateTime dateTimeExpected = new DateTime();
        switch (j)
        {
            case 1: dateTimeExpected = new DateTime(2022, j, 30); break;
            case 2: dateTimeExpected = new DateTime(2022, j, 27); break;
            case 3: dateTimeExpected = new DateTime(2022, j, 27); break;
            case 4: dateTimeExpected = new DateTime(2022, j, 24); break;
            case 5: dateTimeExpected = new DateTime(2022, j, 29); break;

            case 6: dateTimeExpected = new DateTime(2022, j, 26); break;
            case 7: dateTimeExpected = new DateTime(2022, j, 31); break;
            case 8: dateTimeExpected = new DateTime(2022, j, 28); break;
            case 9: dateTimeExpected = new DateTime(2022, j, 25); break;
            case 10: dateTimeExpected = new DateTime(2022, j, 30); break;
            case 11: dateTimeExpected = new DateTime(2022, j, 27); break;
            case 12: dateTimeExpected = new DateTime(2022, j, 25); break;

        }

        for (int i = 1; i < 28; i++)
        {
            DateTime dateTimeResult = ProgramV.getLastWeekDayOfMonth(new DateTime(2022, j, i), DayOfWeek.Sunday);

            Assert.That(dateTimeResult, Is.EqualTo(dateTimeExpected));
        }
    }



}

[Test]
public void FirstWeekDayOfMonthTest()
{
    for (int j = 1; j < 12; j++)
    {
        DateTime dateTimeExpected = new DateTime();
        switch (j)
        {
            case 1: dateTimeExpected = new DateTime(2022, j, 2 + 1); break;
            case 2: dateTimeExpected = new DateTime(2022, j, 6 + 1); break;
            case 3: dateTimeExpected = new DateTime(2022, j, 6 + 1); break;
            case 4: dateTimeExpected = new DateTime(2022, j, 3 + 1); break;
            case 5: dateTimeExpected = new DateTime(2022, j, 1 + 1); break;

            case 6: dateTimeExpected = new DateTime(2022, j, 5 + 1); break;
            case 7: dateTimeExpected = new DateTime(2022, j, 3 + 1); break;
            case 8: dateTimeExpected = new DateTime(2022, j, 7 + 1); break;
            case 9: dateTimeExpected = new DateTime(2022, j, 4 + 1); break;
            case 10: dateTimeExpected = new DateTime(2022, j, 2 + 1); break;
            case 11: dateTimeExpected = new DateTime(2022, j, 6 + 1); break;
            case 12: dateTimeExpected = new DateTime(2022, j, 4 + 1); break;

        }


        DateTime dateTimeResult = ProgramV.getFirstDayWeekOfMonth(new DateTime(2022, j, 1), DayOfWeek.Monday);

        Assert.That(dateTimeResult, Is.EqualTo(dateTimeExpected));


    }

}

public class ProgramV
{
    public static DateTime getLastWeekDayOfMonth(DateTime cuurentDate, DayOfWeek wantedDayObj)
    {


        DateTime lastDayMonth = new DateTime(cuurentDate.Year, cuurentDate.Month, 1).AddMonths(1).AddDays(-1);


        int wantedDay = (int)wantedDayObj;


        int lastDay = (int)lastDayMonth.DayOfWeek;


      
        DateTime newDate = lastDayMonth.AddDays(
             lastDay >= wantedDay ? wantedDay - lastDay : wantedDay - lastDay - 7
             );

        return newDate;

    }

    public static DateTime getFirstDayWeekOfMonth(DateTime fromDT, DayOfWeek wantedDay)
    {

        int wantedDayIndex = (int)wantedDay;

        int firstDayIndex = (int)fromDT.DayOfWeek;


        DateTime newTime = fromDT.AddDays(
           //                              
           firstDayIndex > wantedDayIndex ? (7 - firstDayIndex) + (wantedDayIndex ) : wantedDayIndex - firstDayIndex
            );

        

        return newTime;

    }
}

}

Solution 4:[4]

Here is an extension method to get the nth occurrence of a weekday in a month.

/// <summary>
/// Gets the Nth occurrence of the specified weekday in the month.
/// </summary>
public static DateTime GetNthOfWeekDayInMonth(this DateTime dt, DayOfWeek dayOfWeek, int n)
{
  //Get the first day of the month
  DateTime fd = dt.AddDays(-dt.Day).AddDays(1);
  // Get the first occurrence of the required week day.
  fd = dayOfWeek >= fd.DayOfWeek ? fd.AddDays(dayOfWeek - fd.DayOfWeek) : fd.AddDays((7 + ((int)dayOfWeek)) - ((int)fd.DayOfWeek));
  // Add the amount of "weeks" to get the correct nth date.
  fd = fd.AddDays((n-1) * 7);
  // Might want to handle it differently, but if the next nth day is not in the same month, give an exception?
  if (fd.Month != dt.Month) throw new Exception($"There is no {n} week in this month");
  return fd;
}

Sample:

new DateTime(2022,05,12).GetNthOfWeekDayInMonth(DayOfWeek.Tuesday, 2)
// out: 2022/05/10

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Vikto
Solution 2 digitally_inspired
Solution 3 Nick stands with Ukraine
Solution 4