2
\$\begingroup\$
public static DateTime NextByDayOfWeek(this DateTime target, IEnumerable<DayOfWeek> validDaysOfWeek)
{
    validDaysOfWeek = validDaysOfWeek
        .OrderBy(item => item);

    var nextDayOfWeek = validDaysOfWeek
        .Where(item => item > target.DayOfWeek)
        .FirstOrDefault();

    return nextDayOfWeek != 0
        ? target.AddDays(nextDayOfWeek - target.DayOfWeek)
        : target.AddDays((validDaysOfWeek.First() + 7) - target.DayOfWeek);
}

Usage:

DateTime.Today.NextByDayOfWeek(new[] { DayOfWeek.Monday, DayOfWeek.Friday })

I don't think there are any possible edge cases here, at least some initial tests don't reveal any problem.

I think it's short and readable, but readability is often hard to judge when you're the author.

Any remarks?

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

I think a more readable way to write this would be to get the dates of the 7 days following the given date and return first one that matches the given days of week:

public static DateTime NextByDayOfWeek(
    this DateTime target, params DayOfWeek[] validDaysOfWeek)
{
    return Enumerable.Range(1, 7)
        .Select(n => target.AddDays(n))
        .First(date => validDaysOfWeek.Contains(date.DayOfWeek));
}

Notice that I also changes the validDaysOfWeek parameter to params DayOfWeek[], because that allows you to call the method like this:

DateTime.Today.NextByDayOfWeek(DayOfWeek.Monday, DayOfWeek.Friday)

If you also want the method to accept any IEnumerable<DayOfWeek>, you could add an overload for that.

\$\endgroup\$
1
  • \$\begingroup\$ This way hadn't crossed my mind, I like it! \$\endgroup\$
    – Stijn
    Commented Oct 30, 2013 at 14:51
4
\$\begingroup\$

You can just step forward one day at a time, until you find a week day that is valid. If you change the second parameter to a params parameter, as svick suggested, the code gets very straight forward:

public static DateTime NextByDayOfWeek(this DateTime target, params DayOfWeek[] validDaysOfWeek) {
  do {
    target = target.AddDays(1);
  } while (!validDaysOfWeek.Contains(target.DayOfWeek));
  return target;
}

You can also make a bitmask out of the valid days, so that you don't have to use the Contains method, which would loop through the collection of valid days for each iteration:

public static DateTime NextByDayOfWeek(this DateTime target, IEnumerable<DayOfWeek> validDaysOfWeek) {
  int valid = 0;
  foreach (DayOfWeek d in validDaysOfWeek) {
    valid |= 1 << (int)d;
  }
  do {
    target = target.AddDays(1);
  } while ((valid & 1 << (int)target.DayOfWeek) == 0);
  return target;
}

I tested both these functions with a range of different values, and they seem to give the correct result in all cases.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ This one too is a clean solution. The second one is clever, but I don't want the next maintainer to curse me :) \$\endgroup\$
    – Stijn
    Commented Oct 30, 2013 at 15:24
  • 1
    \$\begingroup\$ I'm not sure it makes much sense trying to optimize this. After all, there are only 7 days in week. \$\endgroup\$
    – svick
    Commented Oct 30, 2013 at 15:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.