Reputation: 24532
How can I get the number of weekdays between two given dates without just iterating through the dates between and counting the weekdays?
Seems fairly straightforward but I can't seem to find a conclusive correct answer that abides by the following:
I've found a similar question with an answer that comes close but is not correct
Upvotes: 26
Views: 34840
Reputation: 67
public static int GetWeekDays(DateTime startDay, DateTime endDate, bool countEndDate = true)
{
var daysBetween = (int)(endDate - startDay).TotalDays;
daysBetween = countEndDate ? daysBetween += 1 : daysBetween;
return Enumerable.Range(0, daysBetween).Count(d => !startDay.AddDays(d).DayOfWeek.In(DayOfWeek.Saturday, DayOfWeek.Sunday));
}
public static bool In<T>(this T source, params T[] list)
{
if (null == source)
{
throw new ArgumentNullException("source");
}
return list.Contains(source);
}
Upvotes: 2
Reputation: 1
This is an old question but I figured I would share a more flexible answer which allows to removes any day of the week.
I tried to keep the code clean and easy to read while remaining efficient by only looping through 6 days max.
So for the OP you can use it like this:
myDate.DaysUntill(DateTime.UtcNow, new List<DayOfWeek> { DayOfWeek.Saturday, DayOfWeek.Sunday });
/// <summary>
/// For better accuracy make sure <paramref name="startDate"/> and <paramref name="endDate"/> have the same time zone.
/// This is only really useful if we use <paramref name="daysOfWeekToExclude"/> - otherwise the computation is trivial.
/// </summary>
/// <param name="startDate"></param>
/// <param name="endDate"></param>
/// <param name="daysOfWeekToExclude"></param>
/// <returns></returns>
public static int DaysUntill(this DateTime startDate, DateTime endDate, IEnumerable<DayOfWeek> daysOfWeekToExclude = null)
{
if (startDate >= endDate) return 0;
daysOfWeekToExclude = daysOfWeekToExclude?.Distinct() ?? new List<DayOfWeek>();
int nbOfWeeks = (endDate - startDate).Days / 7;
int nbOfExtraDays = (endDate - startDate).Days % 7;
int result = nbOfWeeks * (7 - daysOfWeekToExclude.Count());
for (int i = 0; i < nbOfExtraDays; i++)
{
if (!daysOfWeekToExclude.Contains(startDate.AddDays(i + 1).DayOfWeek)) result++;
}
return result;
}
Upvotes: 0
Reputation: 117
var dates = new List<DateTime>();
for (var dt = YourStartDate; dt <= YourEndDate; dt = dt.AddDays(1))
{
if (dt.DayOfWeek != DayOfWeek.Sunday && dt.DayOfWeek != DayOfWeek.Saturday)
{ dates.Add(dt); }
}
in this code you could have a list that all buisness days between two dates.
if you want the count of these dates you could get dates.Count
as an integer.
or if you want to get the each day , you could join the list to a string.
Upvotes: 1
Reputation: 51
Here the function, that calculates count of DayOfWeek betwean two dates. Be carefully it calculates it inclusively (includes beginning day and ending day in calculation):
private int GetWeekdayCount(DayOfWeek dayOfWeek, DateTime begin, DateTime end)
{
if (begin < end)
{
var timeSpan = end.Subtract(begin);
var fullDays = timeSpan.Days;
var count = fullDays / 7; // количество дней равно как минимум количеству полных недель попавших в диапазон
var remain = fullDays % 7; // остаток от деления
// и вычислим попал ли еще один день в те куски недели, что остаются от полной
if (remain > 0)
{
var dowBegin = (int)begin.DayOfWeek;
var dowEnd = (int)end.DayOfWeek;
var dowDay = (int)dayOfWeek;
if (dowBegin < dowEnd)
{
// когда день недели начала меньше дня недели конца, например:
// начало конец
// \/ \/
// -- -- -- -- --
// Вс Пн Вт Ср Чт Пт Сб
if (dowDay >= dowBegin && dowDay <= dowEnd)
count++;
}
else
{
// когда день недели начала больше дня недели конца, например:
// конец начало
// \/ \/
// -- -- -- --
// Вс Пн Вт Ср Чт Пт Сб
if (dowDay <= dowEnd || dowDay >= dowBegin)
count++;
}
}
else if (begin.DayOfWeek == dayOfWeek)
count++;
return count;
}
return 0;
}
Here is another one simple analog of previous function:
private int GetWeekdayCountStupid(DayOfWeek dayOfWeek, DateTime begin, DateTime end)
{
if (begin < end)
{
var count = 0;
var day = begin;
while (day <= end)
{
if (day.DayOfWeek == dayOfWeek)
count++;
day = day.AddDays(1);
}
return count;
}
return 0;
}
And tests for both functions:
[TestMethod()]
public void TestWeekdayCount()
{
var init = new DateTime(2000, 01, 01);
for (int day = 0; day < 7; day++)
{
var dayOfWeek = (DayOfWeek)day;
for (int shift = 0; shift < 8; shift++)
{
for (int i = 0; i < 365; i++)
{
var begin = init.AddDays(shift);
var end = init.AddDays(shift + i);
var count1 = GetWeekdayCount(dayOfWeek, begin, end);
var count2 = GetWeekdayCountStupid(dayOfWeek, begin, end);
Assert.AreEqual(count1, count2);
}
}
}
}
Upvotes: 0
Reputation: 20796
O(1) solution:
// Count days from d0 to d1 inclusive, excluding weekends
public static int countWeekDays(DateTime d0, DateTime d1)
{
int ndays = 1 + Convert.ToInt32((d1 - d0).TotalDays);
int nsaturdays = (ndays + Convert.ToInt32(d0.DayOfWeek)) / 7;
return ndays - 2 * nsaturdays
- (d0.DayOfWeek == DayOfWeek.Sunday ? 1 : 0)
+ (d1.DayOfWeek == DayOfWeek.Saturday ? 1 : 0);
}
Examples for January 2014:
January 2014
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 1)); // 1
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 2)); // 2
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 3)); // 3
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 4)); // 3
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 5)); // 3
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 6)); // 4
N.B. The DateTime
inputs should be at around the same time of the day. If you are creating DateTime
objects based solely on year, month, and day as in the examples above, then you should be fine. As a counter example, 12:01am on Jan 1 to 11:59pm Jan 2 spans only 2 days, but the above function will count 3 if you use those times.
Upvotes: 19
Reputation: 276
Utility functions to get a range of dates:
public IEnumerable<DateTime> GetDates(DateTime begin, int count)
{
var first = new DateTime(begin.Year, begin.Month, begin.Day);
var maxYield = Math.Abs(count);
for (int i = 0; i < maxYield; i++)
{
if(count < 0)
yield return first - TimeSpan.FromDays(i);
else
yield return first + TimeSpan.FromDays(i);
}
yield break;
}
public IEnumerable<DateTime> GetDates(DateTime begin, DateTime end)
{
var days = (int)Math.Ceiling((end - begin).TotalDays);
return GetDates(begin, days);
}
LINQPad demo code:
var begin = DateTime.Now;
var end = begin + TimeSpan.FromDays(14);
var dates = GetDates(begin, end);
var weekdays = dates.Count(x => x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday);
var mondays = dates.Count(x => x.DayOfWeek == DayOfWeek.Monday);
var firstThursday = dates
.OrderBy(d => d)
.FirstOrDefault(d => d.DayOfWeek == DayOfWeek.Thursday);
dates.Dump("Dates in Range");
weekdays.Dump("Count of Weekdays");
mondays.Dump("Count of Mondays");
firstThursday.Dump("First Thursday");
Upvotes: 1
Reputation: 81
public static List<DateTime> Weekdays(DateTime startDate, DateTime endDate)
{
if (startDate > endDate)
{
Swap(ref startDate, ref endDate);
}
List<DateTime> days = new List<DateTime>();
var ts = endDate - startDate;
for (int i = 0; i < ts.TotalDays; i++)
{
var cur = startDate.AddDays(i);
if (cur.DayOfWeek != DayOfWeek.Saturday && cur.DayOfWeek != DayOfWeek.Sunday)
days.Add(cur);
//if (startingDate.AddDays(i).DayOfWeek != DayOfWeek.Saturday || startingDate.AddDays(i).DayOfWeek != DayOfWeek.Sunday)
//yield return startingDate.AddDays(i);
}
return days;
}
And swap dates
private static void Swap(ref DateTime startDate, ref DateTime endDate)
{
object a = startDate;
startDate = endDate;
endDate = (DateTime)a;
}
Upvotes: 1
Reputation: 2061
I needed positive / negatives (not absolute values) so here's how I solved it:
public static int WeekdayDifference(DateTime StartDate, DateTime EndDate)
{
DateTime thisDate = StartDate;
int weekDays = 0;
while (thisDate != EndDate)
{
if (thisDate.DayOfWeek != DayOfWeek.Saturday && thisDate.DayOfWeek != DayOfWeek.Sunday) { weekDays++; }
if (EndDate > StartDate) { thisDate = thisDate.AddDays(1); } else { thisDate = thisDate.AddDays(-1); }
}
/* Determine if value is positive or negative */
if (EndDate > StartDate) {
return weekDays;
}
else
{
return weekDays * -1;
}
}
Upvotes: 2
Reputation: 31
eFloh's answer had an extra day if the last day was a Saturday or Sunday. This would fix it.
public static int Weekdays(DateTime dtmStart, DateTime dtmEnd)
{
if (dtmStart > dtmEnd)
{
DateTime temp = dtmStart;
dtmStart = dtmEnd;
dtmEnd = temp;
}
/* Move border dates to the monday of the first full week and sunday of the last week */
DateTime startMonday = dtmStart;
int startDays = 1;
while (startMonday.DayOfWeek != DayOfWeek.Monday)
{
if (startMonday.DayOfWeek != DayOfWeek.Saturday && startMonday.DayOfWeek != DayOfWeek.Sunday)
{
startDays++;
}
startMonday = startMonday.AddDays(1);
}
DateTime endSunday = dtmEnd;
int endDays = 0;
while (endSunday.DayOfWeek != DayOfWeek.Sunday)
{
if (endSunday.DayOfWeek != DayOfWeek.Saturday && endSunday.DayOfWeek != DayOfWeek.Sunday)
{
endDays++;
}
endSunday = endSunday.AddDays(1);
}
int weekDays;
/* calculate weeks between full week border dates and fix the offset created by moving the border dates */
weekDays = (Math.Max(0, (int)Math.Ceiling((endSunday - startMonday).TotalDays + 1)) / 7 * 5) + startDays - endDays;
if (dtmEnd.DayOfWeek == DayOfWeek.Saturday || dtmEnd.DayOfWeek == DayOfWeek.Sunday)
{
weekDays -= 1;
}
return weekDays;
}
Upvotes: 3
Reputation: 2158
This should do better than the solution by dcp:
/// <summary>
/// Count Weekdays between two dates
/// </summary>
/// <param name="dtmStart">first date</param>
/// <param name="dtmEnd">second date</param>
/// <returns>weekdays between the two dates, including the start and end day</returns>
internal static int getWeekdaysBetween(DateTime dtmStart, DateTime dtmEnd)
{
if (dtmStart > dtmEnd)
{
DateTime temp = dtmStart;
dtmStart = dtmEnd;
dtmEnd = temp;
}
/* Move border dates to the monday of the first full week and sunday of the last week */
DateTime startMonday = dtmStart;
int startDays = 1;
while (startMonday.DayOfWeek != DayOfWeek.Monday)
{
if (startMonday.DayOfWeek != DayOfWeek.Saturday && startMonday.DayOfWeek != DayOfWeek.Sunday)
{
startDays++;
}
startMonday = startMonday.AddDays(1);
}
DateTime endSunday = dtmEnd;
int endDays = 0;
while (endSunday.DayOfWeek != DayOfWeek.Sunday)
{
if (endSunday.DayOfWeek != DayOfWeek.Saturday && endSunday.DayOfWeek != DayOfWeek.Sunday)
{
endDays++;
}
endSunday = endSunday.AddDays(1);
}
int weekDays;
/* calculate weeks between full week border dates and fix the offset created by moving the border dates */
weekDays = (Math.Max(0, (int)Math.Ceiling((endSunday - startMonday).TotalDays + 1)) / 7 * 5) + startDays - endDays;
return weekDays;
}
Upvotes: 2
Reputation: 55467
From this link:
public static int Weekdays(DateTime dtmStart, DateTime dtmEnd)
{
// This function includes the start and end date in the count if they fall on a weekday
int dowStart = ((int)dtmStart.DayOfWeek == 0 ? 7 : (int)dtmStart.DayOfWeek);
int dowEnd = ((int)dtmEnd.DayOfWeek == 0 ? 7 : (int)dtmEnd.DayOfWeek);
TimeSpan tSpan = dtmEnd - dtmStart;
if (dowStart <= dowEnd)
{
return (((tSpan.Days / 7) * 5) + Math.Max((Math.Min((dowEnd + 1), 6) - dowStart), 0));
}
return (((tSpan.Days / 7) * 5) + Math.Min((dowEnd + 6) - Math.Min(dowStart, 6), 5));
}
[1]: http://www.eggheadcafe.com/community/aspnet/2/44982/how-to-calculate-num-of-w.aspx
Tests (each test returns 5):
int ndays = Weekdays(new DateTime(2009, 11, 30), new DateTime(2009, 12, 4));
System.Console.WriteLine(ndays);
// leap year test
ndays = Weekdays(new DateTime(2000, 2,27), new DateTime(2000, 3, 5));
System.Console.WriteLine(ndays);
// non leap year test
ndays = Weekdays(new DateTime(2007, 2, 25), new DateTime(2007, 3, 4));
System.Console.WriteLine(ndays);
Upvotes: 15