I've seen this question, which looks for the Nth weekday in a specific month, but I want something that will look for the Nth [weekday list] in a given month. Is there an easy way to get that without looping through every day in the month?
For example, if I say I want the 3rd [Mon, Wed, Fri]
in a month, and the month starts on a Wednesday, it should return the 1st Monday (1 = Wed, 2 = Fri, 3 = Mon). If I say I want the 2nd last [Mon, Tue, Wed, Thurs]
and the last day of the month is a Sunday, it shou开发者_StackOverflowld return the previous Wednesday.
Meh I mean you might be able to come up with something ugly and incomprehensible that is formula-based and avoids looping, but it will be ugly and incomprehensible so bug-prone and a maintenance disaster, and not at all fun to try to come up with.
Instead, just come up with what is easy to write using loops, and then hide the loops by using LINQ:
static class DateTimeExtensions {
private static IEnumerable<DateTime> DaysOfMonth(int year, int month) {
return Enumerable.Range(1, DateTime.DaysInMonth(year, month))
.Select(day => new DateTime(year, month, day));
}
public static DateTime NthDayOfMonthFrom(
this HashSet<DayOfWeek> daysOfWeek,
int year,
int month,
int nth
) {
return
DateTimeExtensions.DaysOfMonth(year, month)
.Where(
date => daysOfWeek.Contains(date.DayOfWeek)
)
.Skip(nth - 1)
.First();
}
}
Usage:
var daysOfWeek = new HashSet<DayOfWeek> {
DayOfWeek.Monday, DayOfWeek.Wednesday, DayOfWeek.Friday
};
DateTime date = daysOfWeek.NthDayOfMonthFrom(2011, 6, 3));
Assert.Equal(date, new DateTime(2011, 6, 6));
Similarly, using Reverse
, you can easily write NthLastDayOfWeekFrom
. I'll leave it to you to come up with a better name.
I think I'm accepting Jason's answer because I like his solution, however here's what I had come up with before I saw his post. I thought I'd post it because I like the idea of not looping thorough every date, especially if N is a higher value and I'd like to get a date outside the current month.
Instead of looping through all days, I fast-forwarded (or rewind if I'm going backwards) to the closest week, then looped through the next couple of days to find the Nth instance the weekdays specified.
public static DateTime? GetNthWeekDayInMonth(DateTime month, int nth, List<DayOfWeek> weekdays)
{
var multiplier = (nth < 0 ? -1 : 1);
var day = new DateTime(month.Year, month.Month, 1);
// If we're looking backwards, start at end of month
if (multiplier == -1) day = day.AddMonths(1).AddDays(-1);
var dayCount = weekdays.Count;
if (dayCount == 0)
return null;
// Fast forward (or rewind) a few days to appropriate week
var weeks = ((nth - (1 * multiplier)) / dayCount);
day = day.AddDays(weeks * 7);
// Current Nth value
var currentNth = (1 * multiplier) + (weeks * dayCount);
// Loop through next week looking for Nth value
for (var x = 0; x <= 7; x++)
{
if (weekdays.Contains(day.DayOfWeek))
{
if (currentNth == nth)
{
// Verify the nth instance is still in the current month
if (day.Month == month.Month)
return day;
else
return null;
}
currentNth += multiplier;
}
day = day.AddDays(multiplier);
}
return null;
}
精彩评论