views:

133

answers:

5

In C#, given an arbitrary set of DayOfWeek end points (like, DayOfWeek.Friday and DayOfWeek.Sunday) how would one test if an arbitrary date falls between those two days, inclusive?

Example:

// result == true; Oct 23, 2010 is a Saturday
var result = InBetweenDaysInclusive(new DateTime(2010, 10, 23),
                                    DayOfWeek.Friday,
                                    DayOfWeek.Sunday);

// result == true; Oct 22, 2010 is a Friday
result = InBetweenDaysInclusive(new DateTime(2010, 10, 22),
                                DayOfWeek.Friday,
                                DayOfWeek.Sunday);

// result == true; Oct 24, 2010 is a Sunday
result = InBetweenDaysInclusive(new DateTime(2010, 10, 24),
                                DayOfWeek.Friday,
                                DayOfWeek.Sunday);

// result == false; Oct 25, 2010 is a Monday
result = InBetweenDaysInclusive(new DateTime(2010, 10, 25),
                                DayOfWeek.Friday,
                                DayOfWeek.Sunday);

Thanks!

A: 

Thomas Levesque's answer is good here. Remember that DayOfWeek.Sunday is 0 and not 6 inside this enum.

This is problematic for me since in Norway Monday is the first day of the week, and not Sunday.

In that case, you should consider checking if the enum equals DayOfWeek.Sunday, and if it is to add 7 to the value before you do the comparison to make sure that Sunday is considered in the correct way.

If you have Sunday as the first day of week where you live, it's not a problem anyway ;)

Øyvind Bråthen
No, it's not good! @Thomas' algorithm will NEVER equate to true in the case presented by the OP.
Brad
Yes, that is why I said that you have to modify DayOfWeek.Sunday before the comparion takes place, because then it WILL be true for the cases that OP wants. Also, some countries count Sunday as the first day of week, so in that case, yes, Tuesday is between Sunday and Wednesday (think about it)... ;)
Øyvind Bråthen
The issue is not whether `Sunday` is the first day of the week or not, the problem is that if you pick a second day that occurs **earlier** in the week than the first day, you will NEVER get a **positive** result. To give another example, using `DayOfWeek` enum math, `Tuesday` (2) will *never* be between `Monday` (1) and `Sunday` (0)
Brad
If you do the proper conversion first, Sunday will not be 0, it will be 7, so it will check if Tuesday(2) is between Monday(1) and Sunday(7) which it is.
Øyvind Bråthen
+5  A: 
Brad
I wish I could give you another vote. Seems everyone else (except Øyvind Bråthen) had stupid for breakfast :)
leppie
No kidding.... they all think that Sunday (0) is greater than Saturday(6) *sigh*
Brad
@leppie, looks like I scared off all the others! @OP, I'm working on a real solution for you...
Brad
apologies - today I learnt not to post answers on stackoverflow with a raging hangover
AdamRalph
+1 to Adam for being a good sport.
Brad
@Brad: have a look at my suggestion below. I believe it doesn't require that a week is Saturday - Sunday, but a pair of extra eyes on it wouldn't hurt :)
Nailuj
IMHO, your overcomplicating the problem here.
R. Bemrose
@R. Bemrose, you are correct. I've tested your solution and it works beautifully, but is culture ignorant.
Brad
@Bemrose, rescinding my previous comment about your code not being considering culture. It's very elegant!
Brad
A: 

I think this should work. It should also work regardless of what you consider to be the first day of the week:

private bool InBetweenDaysInclusive(DateTime dateToCheck, DayOfWeek lowerLimit, DayOfWeek upperLimit)
{
    CultureInfo ci = CultureInfo.CurrentCulture;

    int diffDateToCheckFirstDayOfWeek = dateToCheck.DayOfWeek - ci.DateTimeFormat.FirstDayOfWeek;
    if (diffDateToCheckFirstDayOfWeek < 0)
        diffDateToCheckFirstDayOfWeek += 7;

    int diffLowerLimitFirstDayOfWeek = lowerLimit - ci.DateTimeFormat.FirstDayOfWeek;
    if (diffLowerLimitFirstDayOfWeek < 0)
        diffLowerLimitFirstDayOfWeek += 7;

    int diffUpperLimitFirstDayOfWeek = upperLimit - ci.DateTimeFormat.FirstDayOfWeek;
    if (diffUpperLimitFirstDayOfWeek < 0)
        diffUpperLimitFirstDayOfWeek += 7;

    if (diffUpperLimitFirstDayOfWeek < diffLowerLimitFirstDayOfWeek)
        throw new Exception("The lower-limit day must be earlier in the week than the upper-limit day");

    return diffDateToCheckFirstDayOfWeek >= diffLowerLimitFirstDayOfWeek && diffDateToCheckFirstDayOfWeek <= diffUpperLimitFirstDayOfWeek;
}

Edit:
In case you wonder, the if < 0 and += 7 is to get around that subtracting two days isn't culture aware. The code could probably be made a bit cleaner, but you get the point I think.

Nailuj
@Nailuj, based on the OP's examples, they want to be able to pass in a `upperLimit` that occurs "earlier" in the week than the `lowerLimit`. Can you handle this case?
Brad
@Brad: where in the OP does it say that the `upperLimit` should be able to occur "earlier" than the `lowerLimit`? And even if that's the case, my code would easily handle that as well, by adding a bit more logic instead of throwing an exception. My gives the desired result for all examples given in the OP at least.
Nailuj
The OP uses the days `Friday` and `Sunday`, respectively. Your code would throw an error.
Brad
@Brad: and what makes you believe that `Sunday` is the first day of the week in the OP's culture, and not `Monday`?
Nailuj
+6  A: 

This function is going to need two separate branches depending on whether the difference between start and end date is negative or positive/zero.

I could be totally off-base, but I think this works for all cases:

// No state in method, so made it static
public static bool InBetweenDaysInclusive(DateTime date, DayOfWeek start, DayOfWeek end)
{
    DayOfWeek curDay = date.DayOfWeek;

    if (start <= end)
    {
        // Test one range: start to end
        return (start <= curDay && curDay <= end);
    }
    else
    {
        // Test two ranges: start to 6, 0 to end
        return (start <= curDay || curDay <= end);
    }
}

For reference, your test data returned the following when I ran it and added Console.WriteLine for each result:

True
True
True
False

Edit: My last explanation was too vague. Here's a fixed one.

The trick is that if end < start, then you have two valid ranges: start to upper bound and lower bound to end. This would result in (start <= curDay && curDay <= upperBound) || curDay <= end && lowerBound <= curDay)

However, since they are bounds, curDay is always <= upperBound and >= lowerBound, thus we omit that code.

R. Bemrose
I tested this with my same sample data: it works just as mine does. Can you make it culture-aware?I'll let you all keep my extension methods for free :)
Brad
@Brad: I'm not really sure what I'd need to do to make it culture aware in this case... I just assumed it would work in other cultures, although I haven't tested it.
R. Bemrose
@Bemrose, wouldn't all the evaluations of whether `someDayOfWeek is <= to anotherDayOfWeek` presume upon the order of the Sun-Sat (0-6) DayOfWeek enum?
Brad
@Brad: [MSDN](http://msdn.microsoft.com/en-us/library/system.dayofweek.aspx) implies 0 is always Sunday in this enum.
R. Bemrose
@Bemrose, yeah, the enum is the same in every culture. I guess you're right that it doesn't matter what day if the `FirstDayOfTheWeek` in your solution. Don't worry, you got the only upvote I can give you LONG ago! :)
Brad
@Brad: I actually had to check that... at first I didn't stop and think that the DateTime could have a different culture than the current culture. It's morning, I can tell. ;)
R. Bemrose
+1 I think this is the cleanest solution that's been posted. I agree that the culture should be irrelevant to the solution, as long as the DayOfWeek enum always goes from 0 to 6 and consecutive days are next to eachother. My solution is more complex because I figured we needed to solve the intermediate problem of "finding all days of the week between two days of the week". Perhaps the solution to that problem is useful in another circumstance, but it is not the simplest way to solve the OP's question.
Stuart Lange
Also, your solution passes all my tests as well.
Stuart Lange
Wow, I wasn't expecting this many responses, many thanks to all!
russcollier
+2  A: 

@Brad's point that any day of the week falls between any two days of the week is valid. However, we are assuming the two days of the week in question are ordered.

That is, when we say, "is October 30, 2010 (a Saturday) between Friday and Sunday?", we are really asking, "is October 30, 2010 either a Friday, a Saturday, or a Sunday?".

This observation allows us to break down the problem into two components and solve the full problem easily:

1) Determine if a particular day of the week is one of a particular set of days of the week (this is trivial).

2) Determine the set of days of the week that take you from one day to another. That is, we want a function that returns "Friday, Saturday, Sunday" when given "Friday" and "Sunday", and that returns "Monday, Tuesday, Wednesday, Thursday, Friday" when given "Monday" and "Friday". This is the tricky part of the problem.

To solve the second problem, we basically walk from the first day to the second day, returning all days in-between. To do this correctly, we have to account for the fact that the second day may be less than the first day (in the representational sense of Sunday = 0 being less than Friday = 5). So, we perform the "walk" in an integer space, and add 7 to the second day if it is less than the first day. We convert to days-of-the-week space (which is the integers modulo 7) on the "way out".

Below is the code and a series of tests that solve this. The "GetDaysBetweenInclusive" method solves problem #2, and "IsDayOfWeekBetween" adds the solution to problem #1 and solves the OP's problem.

Enjoy.

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;

namespace DayOfWeekUtilities
{
    public static class DayOfWeekHelpers
    {
        /// <summary>
        /// returns all days of the week, inclusive, from day1 to day2
        /// </summary>
        public static IEnumerable<DayOfWeek> GetDaysBetweenInclusive(DayOfWeek day1,
                                                                     DayOfWeek day2)
        {
            var final = (int)day2;
            if(day2 < day1)
            {
                final += 7;
            }
            var curr = (int)day1;
            do
            {
                yield return (DayOfWeek) (curr%7);
                curr++;
            } while (curr <= final);
        }

        /// <summary>
        /// returns true if the provided date falls on a day of the 
        /// week between day1 and day2, inclusive
        /// </summary>
        public static bool IsDayOfWeekBetween(this DateTime date,
                                              DayOfWeek day1,
                                              DayOfWeek day2)
        {
            return GetDaysBetweenInclusive(day1, day2).Contains(date.DayOfWeek);
        }
    }

    [TestFixture]
    public class Tests
    {
        [Test]
        public void Test()
        {
            Assert.IsTrue(new DateTime(2010, 10, 22).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Sunday));
            Assert.IsTrue(new DateTime(2010, 10, 23).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Sunday));
            Assert.IsTrue(new DateTime(2010, 10, 24).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Sunday));
            Assert.IsFalse(new DateTime(2010, 10, 25).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Sunday));
            Assert.IsFalse(new DateTime(2010, 10, 26).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Sunday));
            Assert.IsFalse(new DateTime(2010, 10, 27).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Sunday));
            Assert.IsFalse(new DateTime(2010, 10, 28).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Sunday));
            Assert.IsTrue(new DateTime(2010, 10, 29).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Sunday));

            Assert.IsTrue(new DateTime(2010, 10, 22).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Friday));
            Assert.IsFalse(new DateTime(2010, 10, 23).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Friday));
            Assert.IsFalse(new DateTime(2010, 10, 24).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Friday));
            Assert.IsFalse(new DateTime(2010, 10, 25).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Friday));
            Assert.IsFalse(new DateTime(2010, 10, 26).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Friday));
            Assert.IsFalse(new DateTime(2010, 10, 27).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Friday));
            Assert.IsFalse(new DateTime(2010, 10, 28).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Friday));
            Assert.IsTrue(new DateTime(2010, 10, 29).IsDayOfWeekBetween(DayOfWeek.Friday, DayOfWeek.Friday));

            Assert.IsTrue(new DateTime(2010, 10, 22).IsDayOfWeekBetween(DayOfWeek.Monday, DayOfWeek.Friday));
            Assert.IsFalse(new DateTime(2010, 10, 23).IsDayOfWeekBetween(DayOfWeek.Monday, DayOfWeek.Friday));
            Assert.IsFalse(new DateTime(2010, 10, 24).IsDayOfWeekBetween(DayOfWeek.Monday, DayOfWeek.Friday));
            Assert.IsTrue(new DateTime(2010, 10, 25).IsDayOfWeekBetween(DayOfWeek.Monday, DayOfWeek.Friday));
            Assert.IsTrue(new DateTime(2010, 10, 26).IsDayOfWeekBetween(DayOfWeek.Monday, DayOfWeek.Friday));
            Assert.IsTrue(new DateTime(2010, 10, 27).IsDayOfWeekBetween(DayOfWeek.Monday, DayOfWeek.Friday));
            Assert.IsTrue(new DateTime(2010, 10, 28).IsDayOfWeekBetween(DayOfWeek.Monday, DayOfWeek.Friday));
            Assert.IsTrue(new DateTime(2010, 10, 29).IsDayOfWeekBetween(DayOfWeek.Monday, DayOfWeek.Friday));

            Assert.IsTrue(new DateTime(2010, 10, 22).IsDayOfWeekBetween(DayOfWeek.Thursday, DayOfWeek.Tuesday));
            Assert.IsTrue(new DateTime(2010, 10, 23).IsDayOfWeekBetween(DayOfWeek.Thursday, DayOfWeek.Tuesday));
            Assert.IsTrue(new DateTime(2010, 10, 24).IsDayOfWeekBetween(DayOfWeek.Thursday, DayOfWeek.Tuesday));
            Assert.IsTrue(new DateTime(2010, 10, 25).IsDayOfWeekBetween(DayOfWeek.Thursday, DayOfWeek.Tuesday));
            Assert.IsTrue(new DateTime(2010, 10, 26).IsDayOfWeekBetween(DayOfWeek.Thursday, DayOfWeek.Tuesday));
            Assert.IsFalse(new DateTime(2010, 10, 27).IsDayOfWeekBetween(DayOfWeek.Thursday, DayOfWeek.Tuesday));
            Assert.IsTrue(new DateTime(2010, 10, 28).IsDayOfWeekBetween(DayOfWeek.Thursday, DayOfWeek.Tuesday));
            Assert.IsTrue(new DateTime(2010, 10, 29).IsDayOfWeekBetween(DayOfWeek.Thursday, DayOfWeek.Tuesday));
        }
    }
}
Stuart Lange
+1 for a solution that doesn't only solve the problem completely, but does it in a very elegant way. It might not be the cleanest solution, but it is definitely the one which is easiest to understand. Nice way of analysing the problem Stuart :)
Nailuj