How to test if a DateTime is between 2 days of week (DayOfWeek)
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开发者_如何学运维!
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.
Every date falls between any two given days of the week (think about it)...
You would need to get the endpoint dates for the dayOfWeek closest to the date in question (where day difference < 7). Then, you do a simple comparison.
NOTE: the following solution assumes a week is Sunday to Saturday
Given the following extension methods:
/// <summary>
/// Gets the date of the next occurrence of the day of week provided
/// </summary>
/// <param name="value"></param>
/// <param name="nextDay"></param>
/// <returns></returns>
public static DateTime NextOccurance(this DateTime value, DayOfWeek nextDay)
{
if (value.DayOfWeek == nextDay) { return value; }
else if (value.DayOfWeek > nextDay) { return value.AddDays(7 - (value.DayOfWeek - nextDay)); }
else { return value.AddDays(nextDay - value.DayOfWeek); }
}
/// <summary>
/// Gets the date of the last occurrence of the day of week provided
/// </summary>
/// <param name="value"></param>
/// <param name="lastDay"></param>
/// <returns></returns>
public static DateTime LastOccurance(this DateTime value, DayOfWeek lastDay)
{
if (value.DayOfWeek == lastDay) { return value; }
else if (value.DayOfWeek > lastDay) { return value.AddDays(-(value.DayOfWeek - lastDay)); }
else { return value.AddDays((lastDay - value.DayOfWeek) - 7); }
}
/// <summary>
/// Gets the date of the closest occurrence of the day of week provided
/// </summary>
/// <param name="value"></param>
/// <param name="day"></param>
/// <returns></returns>
public static DateTime ClosestOccurance(this DateTime value, DayOfWeek day)
{
DateTime before = value.LastOccurance(day);
DateTime after = value.NextOccurance(day);
return ((value - before) < (after - value))
? before
: after;
}
You can find out if the dayOfWeek in question falls within two dates like this: (this is the part that assumes a week is Sunday to Saturday)
DayOfWeek dayOne = DayOfWeek.Tuesday;
DayOfWeek dayTwo = DayOfWeek.Friday;
DateTime doesDateFallWithin = DateTime.Today;
bool fallsWithin =
doesDateFallWithin.ClosestOccurance(dayOne) <= doesDateFallWithin
&& doesDateFallWithin <= doesDateFallWithin.ClosestOccurance(dayTwo);
My results:
dayOne = Friday, dayTwo = Tuesday
10/27/2010 (Wednesday) does not fall within the closest occurrences of Friday (10/29/2010) and Tuesday (10/26/2010)
10/28/2010 (Thursday) does not fall within the closest occurrences of Friday (10/29/2010) and Tuesday (10/26/2010)
10/29/2010 (Friday) does not fall within the closest occurrences of Friday (10/29/2010) and Tuesday (10/26/2010)
10/30/2010 (Saturday) falls within the closest occurrences of Friday (10/29/2010) and Tuesday (11/2/2010)
10/31/2010 (Sunday) falls within the closest occurrences of Friday (10/29/2010) and Tuesday (11/2/2010)
11/1/2010 (Monday) falls within the closest occurrences of Friday (10/29/2010) and Tuesday (11/2/2010)
11/2/2010 (Tuesday) does not fall within the closest occurrences of Friday (11/5/2010) and Tuesday (11/2/2010)
11/3/2010 (Wednesday) does not fall within the closest occurrences of Friday (11/5/2010) and Tuesday (11/2/2010)
dayOne = Monday, dayTwo = Wednesday
10/27/2010 (Wednesday) falls within the closest occurrences of Monday (10/25/2010) and Wednesday (10/27/2010)
10/28/2010 (Thursday) does not fall within the closest occurrences of Monday (10/25/2010) and Wednesday (10/27/2010)
10/29/2010 (Friday) does not fall within the closest occurrences of Monday (11/1/2010) and Wednesday (10/27/2010)
10/30/2010 (Saturday) does not fall within the closest occurrences of Monday (11/1/2010) and Wednesday (10/27/2010)
10/31/2010 (Sunday) does not fall within the closest occurrences of Monday (11/1/2010) and Wednesday (11/3/2010)
11/1/2010 (Monday) falls within the closest occurrences of Monday (11/1/2010) and Wednesday (11/3/2010)
11/2/2010 (Tuesday) falls within the closest occurrences of Monday (11/1/2010) and Wednesday (11/3/2010)
11/3/2010 (Wednesday) falls within the closest occurrences of Monday (11/1/2010) and Wednesday (11/3/2010)
@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));
}
}
}
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 ;)
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.
Stuart Lauge say: ...perform the "walk" in an integer space. I understand:
private bool InBetweenDaysInclusive(DateTime date, DayOfWeek day1, DayOfWeek day2)
{
int d1 = (int)day1;
int d2 = (int)day2;
int dx = (int)date.DayOfWeek;
if (d2 < d1) d2 += 7;
if (dx < d1) dx += 7;
return (dx >= d1 && dx <= d2);
}
精彩评论