rrgg
rrgg

Reputation: 43

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?

Upvotes: 1

Views: 2176

Answers (5)

Darrel K.
Darrel K.

Reputation: 1729

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

Upvotes: 0

Vic Espino
Vic Espino

Reputation: 41

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;

    }
}

}

Upvotes: 0

digitally_inspired
digitally_inspired

Reputation: 777

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,
    }

Upvotes: 0

Vito
Vito

Reputation: 644

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

Upvotes: 1

juharr
juharr

Reputation: 32276

Just start with the first of the month, increment until you get the the day of the week you want then increment by 7 times n-1 where n is the nth day of the week you want from the month.

public static DateTime(int year, int month, DayOfWeek weekDay, int nth)
{
    DateTime result = new DateTime(year, month, 1);
    while(result.DatOfWeek != weekDay)
        result = result.AddDays(1);
    return result.AddDays(7 * (nth-1));
}

Note you'll want to add checks on the parameters and make sure the ultimate result didn't go over to the next month.

If you also need to determine the values of DayOfWeek and nth from a DateTime then you can do the following.

DateTime now = DateTime.Now;
DayOfWeek dayOfWeek = now.DayOfWeek;
int nth = 0;
int month = now.Month;
int year = now.Year;
while(now.Year == year && now.Month == month)
{
    now = now.AddDays(-7);
    nth++;
}

Note that it is possible that one month will have a 5th Wednesday, but the next only has 4, so you'd have to determine what to do in those cases.

Upvotes: 2

Related Questions