'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 |
