AddBusinessDays and GetBusinessDays - c#

I need to find 2 elegant complete implementations of
public static DateTime AddBusinessDays(this DateTime date, int days)
{
// code here
}
and
public static int GetBusinessDays(this DateTime start, DateTime end)
{
// code here
}
O(1) preferable (no loops).
EDIT:
By business days i mean working days (Monday, Tuesday, Wednesday, Thursday, Friday). No holidays, just weekends excluded.
I already have some ugly solutions that seem to work but i wonder if there are elegant ways to do this. Thanks
This is what i've written so far. It works in all cases and does negatives too.
Still need a GetBusinessDays implementation
public static DateTime AddBusinessDays(this DateTime startDate,
int businessDays)
{
int direction = Math.Sign(businessDays);
if(direction == 1)
{
if(startDate.DayOfWeek == DayOfWeek.Saturday)
{
startDate = startDate.AddDays(2);
businessDays = businessDays - 1;
}
else if(startDate.DayOfWeek == DayOfWeek.Sunday)
{
startDate = startDate.AddDays(1);
businessDays = businessDays - 1;
}
}
else
{
if(startDate.DayOfWeek == DayOfWeek.Saturday)
{
startDate = startDate.AddDays(-1);
businessDays = businessDays + 1;
}
else if(startDate.DayOfWeek == DayOfWeek.Sunday)
{
startDate = startDate.AddDays(-2);
businessDays = businessDays + 1;
}
}
int initialDayOfWeek = (int)startDate.DayOfWeek;
int weeksBase = Math.Abs(businessDays / 5);
int addDays = Math.Abs(businessDays % 5);
if((direction == 1 && addDays + initialDayOfWeek > 5) ||
(direction == -1 && addDays >= initialDayOfWeek))
{
addDays += 2;
}
int totalDays = (weeksBase * 7) + addDays;
return startDate.AddDays(totalDays * direction);
}

Latest attempt for your first function:
public static DateTime AddBusinessDays(DateTime date, int days)
{
if (days < 0)
{
throw new ArgumentException("days cannot be negative", "days");
}
if (days == 0) return date;
if (date.DayOfWeek == DayOfWeek.Saturday)
{
date = date.AddDays(2);
days -= 1;
}
else if (date.DayOfWeek == DayOfWeek.Sunday)
{
date = date.AddDays(1);
days -= 1;
}
date = date.AddDays(days / 5 * 7);
int extraDays = days % 5;
if ((int)date.DayOfWeek + extraDays > 5)
{
extraDays += 2;
}
return date.AddDays(extraDays);
}
The second function, GetBusinessDays, can be implemented as follows:
public static int GetBusinessDays(DateTime start, DateTime end)
{
if (start.DayOfWeek == DayOfWeek.Saturday)
{
start = start.AddDays(2);
}
else if (start.DayOfWeek == DayOfWeek.Sunday)
{
start = start.AddDays(1);
}
if (end.DayOfWeek == DayOfWeek.Saturday)
{
end = end.AddDays(-1);
}
else if (end.DayOfWeek == DayOfWeek.Sunday)
{
end = end.AddDays(-2);
}
int diff = (int)end.Subtract(start).TotalDays;
int result = diff / 7 * 5 + diff % 7;
if (end.DayOfWeek < start.DayOfWeek)
{
return result - 2;
}
else{
return result;
}
}

using Fluent DateTime:
var now = DateTime.Now;
var dateTime1 = now.AddBusinessDays(3);
var dateTime2 = now.SubtractBusinessDays(5);
internal code is as follows
/// <summary>
/// Adds the given number of business days to the <see cref="DateTime"/>.
/// </summary>
/// <param name="current">The date to be changed.</param>
/// <param name="days">Number of business days to be added.</param>
/// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
public static DateTime AddBusinessDays(this DateTime current, int days)
{
var sign = Math.Sign(days);
var unsignedDays = Math.Abs(days);
for (var i = 0; i < unsignedDays; i++)
{
do
{
current = current.AddDays(sign);
}
while (current.DayOfWeek == DayOfWeek.Saturday ||
current.DayOfWeek == DayOfWeek.Sunday);
}
return current;
}
/// <summary>
/// Subtracts the given number of business days to the <see cref="DateTime"/>.
/// </summary>
/// <param name="current">The date to be changed.</param>
/// <param name="days">Number of business days to be subtracted.</param>
/// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
public static DateTime SubtractBusinessDays(this DateTime current, int days)
{
return AddBusinessDays(current, -days);
}

I created an extension that allows you to add or subtract business days.
Use a negative number of businessDays to subtract. I think it's quite an elegant solution. It seems to work in all cases.
namespace Extensions.DateTime
{
public static class BusinessDays
{
public static System.DateTime AddBusinessDays(this System.DateTime source, int businessDays)
{
var dayOfWeek = businessDays < 0
? ((int)source.DayOfWeek - 12) % 7
: ((int)source.DayOfWeek + 6) % 7;
switch (dayOfWeek)
{
case 6:
businessDays--;
break;
case -6:
businessDays++;
break;
}
return source.AddDays(businessDays + ((businessDays + dayOfWeek) / 5) * 2);
}
}
}
Example:
using System;
using System.Windows.Forms;
using Extensions.DateTime;
namespace AddBusinessDaysTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.Text = DateTime.Now.AddBusinessDays(5).ToString();
label2.Text = DateTime.Now.AddBusinessDays(-36).ToString();
}
}
}

For me I had to have a solution that would skip weekends and go in either negative or positive. My criteria was if it went forward and landed on a weekend it would need to advance to Monday. If it was going back and landed on a weekend it would have to jump to Friday.
For example:
Wednesday - 3 business days = Last Friday
Wednesday + 3 business days = Monday
Friday - 7 business days = Last Wednesday
Tuesday - 5 business days = Last Tuesday
Well you get the idea ;)
I ended up writing this extension class
public static partial class MyExtensions
{
public static DateTime AddBusinessDays(this DateTime date, int addDays)
{
while (addDays != 0)
{
date = date.AddDays(Math.Sign(addDays));
if (MyClass.IsBusinessDay(date))
{
addDays = addDays - Math.Sign(addDays);
}
}
return date;
}
}
It uses this method I thought would be useful to use elsewhere...
public class MyClass
{
public static bool IsBusinessDay(DateTime date)
{
switch (date.DayOfWeek)
{
case DayOfWeek.Monday:
case DayOfWeek.Tuesday:
case DayOfWeek.Wednesday:
case DayOfWeek.Thursday:
case DayOfWeek.Friday:
return true;
default:
return false;
}
}
}
If you don't want to bother with that you can just replace out if (MyClass.IsBusinessDay(date)) with if if ((date.DayOfWeek != DayOfWeek.Saturday) && (date.DayOfWeek != DayOfWeek.Sunday))
So now you can do
var myDate = DateTime.Now.AddBusinessDays(-3);
or
var myDate = DateTime.Now.AddBusinessDays(5);
Here are the results from some testing:
Test Expected Result
Wednesday -4 business days Thursday Thursday
Wednesday -3 business days Friday Friday
Wednesday +3 business days Monday Monday
Friday -7 business days Wednesday Wednesday
Tuesday -5 business days Tuesday Tuesday
Friday +1 business days Monday Monday
Saturday +1 business days Monday Monday
Sunday -1 business days Friday Friday
Monday -1 business days Friday Friday
Monday +1 business days Tuesday Tuesday
Monday +0 business days Monday Monday

public static DateTime AddBusinessDays(this DateTime date, int days)
{
date = date.AddDays((days / 5) * 7);
int remainder = days % 5;
switch (date.DayOfWeek)
{
case DayOfWeek.Tuesday:
if (remainder > 3) date = date.AddDays(2);
break;
case DayOfWeek.Wednesday:
if (remainder > 2) date = date.AddDays(2);
break;
case DayOfWeek.Thursday:
if (remainder > 1) date = date.AddDays(2);
break;
case DayOfWeek.Friday:
if (remainder > 0) date = date.AddDays(2);
break;
case DayOfWeek.Saturday:
if (days > 0) date = date.AddDays((remainder == 0) ? 2 : 1);
break;
case DayOfWeek.Sunday:
if (days > 0) date = date.AddDays((remainder == 0) ? 1 : 0);
break;
default: // monday
break;
}
return date.AddDays(remainder);
}

I'm coming late for the answer, but I made a little library with all the customization needed to do simple operations on working days... I leave it here : Working Days Management

The only real solution is to have those calls access a database table that defines the calendar for your business. You could code it for a Monday to Friday workweek without too much difficult but handling holidays would be a challenge.
Edited to add non-elegant and non-tested partial solution:
public static DateTime AddBusinessDays(this DateTime date, int days)
{
for (int index = 0; index < days; index++)
{
switch (date.DayOfWeek)
{
case DayOfWeek.Friday:
date = date.AddDays(3);
break;
case DayOfWeek.Saturday:
date = date.AddDays(2);
break;
default:
date = date.AddDays(1);
break;
}
}
return date;
}
Also I violated the no loops requirement.

I am resurrecting this post because today I had to find a way to exclude not only Saturday and Sunday weekdays but also holidays. More specifically, I needed to handle various sets of possible holidays, including:
country-invariant holidays (at least for the Western countries - such as January, 01).
calculated holidays (such as Easter and Easter monday).
country-specific holidays (such as the Italian liberation day or the United States ID4).
town-specific holidays (such as the Rome St. Patron Day).
any other custom-made holiday (such as "tomorrow our office wil be closed").
Eventually, I came out with the following set of helper/extensions classes: although they aren't blatantly elegant, as they do make a massive use of unefficient loops, they are decent enough to solve my issues for good. I'm dropping the whole source code here in this post, hoping it will be useful to someone else as well.
Source code
/// <summary>
/// Helper/extension class for manipulating date and time values.
/// </summary>
public static class DateTimeExtensions
{
/// <summary>
/// Calculates the absolute year difference between two dates.
/// </summary>
/// <param name="dt1"></param>
/// <param name="dt2"></param>
/// <returns>A whole number representing the number of full years between the specified dates.</returns>
public static int Years(DateTime dt1,DateTime dt2)
{
return Months(dt1,dt2)/12;
//if (dt2<dt1)
//{
// DateTime dt0=dt1;
// dt1=dt2;
// dt2=dt0;
//}
//int diff=dt2.Year-dt1.Year;
//int m1=dt1.Month;
//int m2=dt2.Month;
//if (m2>m1) return diff;
//if (m2==m1 && dt2.Day>=dt1.Day) return diff;
//return (diff-1);
}
/// <summary>
/// Calculates the absolute year difference between two dates.
/// Alternative, stand-alone version (without other DateTimeUtil dependency nesting required)
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
public static int Years2(DateTime start, DateTime end)
{
return (end.Year - start.Year - 1) +
(((end.Month > start.Month) ||
((end.Month == start.Month) && (end.Day >= start.Day))) ? 1 : 0);
}
/// <summary>
/// Calculates the absolute month difference between two dates.
/// </summary>
/// <param name="dt1"></param>
/// <param name="dt2"></param>
/// <returns>A whole number representing the number of full months between the specified dates.</returns>
public static int Months(DateTime dt1,DateTime dt2)
{
if (dt2<dt1)
{
DateTime dt0=dt1;
dt1=dt2;
dt2=dt0;
}
dt2=dt2.AddDays(-(dt1.Day-1));
return (dt2.Year-dt1.Year)*12+(dt2.Month-dt1.Month);
}
/// <summary>
/// Returns the higher of the two date time values.
/// </summary>
/// <param name="dt1">The first of the two <c>DateTime</c> values to compare.</param>
/// <param name="dt2">The second of the two <c>DateTime</c> values to compare.</param>
/// <returns><c>dt1</c> or <c>dt2</c>, whichever is higher.</returns>
public static DateTime Max(DateTime dt1,DateTime dt2)
{
return (dt2>dt1?dt2:dt1);
}
/// <summary>
/// Returns the lower of the two date time values.
/// </summary>
/// <param name="dt1">The first of the two <c>DateTime</c> values to compare.</param>
/// <param name="dt2">The second of the two <c>DateTime</c> values to compare.</param>
/// <returns><c>dt1</c> or <c>dt2</c>, whichever is lower.</returns>
public static DateTime Min(DateTime dt1,DateTime dt2)
{
return (dt2<dt1?dt2:dt1);
}
/// <summary>
/// Adds the given number of business days to the <see cref="DateTime"/>.
/// </summary>
/// <param name="current">The date to be changed.</param>
/// <param name="days">Number of business days to be added.</param>
/// <param name="holidays">An optional list of holiday (non-business) days to consider.</param>
/// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
public static DateTime AddBusinessDays(
this DateTime current,
int days,
IEnumerable<DateTime> holidays = null)
{
var sign = Math.Sign(days);
var unsignedDays = Math.Abs(days);
for (var i = 0; i < unsignedDays; i++)
{
do
{
current = current.AddDays(sign);
}
while (current.DayOfWeek == DayOfWeek.Saturday
|| current.DayOfWeek == DayOfWeek.Sunday
|| (holidays != null && holidays.Contains(current.Date))
);
}
return current;
}
/// <summary>
/// Subtracts the given number of business days to the <see cref="DateTime"/>.
/// </summary>
/// <param name="current">The date to be changed.</param>
/// <param name="days">Number of business days to be subtracted.</param>
/// <param name="holidays">An optional list of holiday (non-business) days to consider.</param>
/// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
public static DateTime SubtractBusinessDays(
this DateTime current,
int days,
IEnumerable<DateTime> holidays)
{
return AddBusinessDays(current, -days, holidays);
}
/// <summary>
/// Retrieves the number of business days from two dates
/// </summary>
/// <param name="startDate">The inclusive start date</param>
/// <param name="endDate">The inclusive end date</param>
/// <param name="holidays">An optional list of holiday (non-business) days to consider.</param>
/// <returns></returns>
public static int GetBusinessDays(
this DateTime startDate,
DateTime endDate,
IEnumerable<DateTime> holidays)
{
if (startDate > endDate)
throw new NotSupportedException("ERROR: [startDate] cannot be greater than [endDate].");
int cnt = 0;
for (var current = startDate; current < endDate; current = current.AddDays(1))
{
if (current.DayOfWeek == DayOfWeek.Saturday
|| current.DayOfWeek == DayOfWeek.Sunday
|| (holidays != null && holidays.Contains(current.Date))
)
{
// skip holiday
}
else cnt++;
}
return cnt;
}
/// <summary>
/// Calculate Easter Sunday for any given year.
/// src.: https://stackoverflow.com/a/2510411/1233379
/// </summary>
/// <param name="year">The year to calcolate Easter against.</param>
/// <returns>a DateTime object containing the Easter month and day for the given year</returns>
public static DateTime GetEasterSunday(int year)
{
int day = 0;
int month = 0;
int g = year % 19;
int c = year / 100;
int h = (c - (int)(c / 4) - (int)((8 * c + 13) / 25) + 19 * g + 15) % 30;
int i = h - (int)(h / 28) * (1 - (int)(h / 28) * (int)(29 / (h + 1)) * (int)((21 - g) / 11));
day = i - ((year + (int)(year / 4) + i + 2 - c + (int)(c / 4)) % 7) + 28;
month = 3;
if (day > 31)
{
month++;
day -= 31;
}
return new DateTime(year, month, day);
}
/// <summary>
/// Retrieve holidays for given years
/// </summary>
/// <param name="years">an array of years to retrieve the holidays</param>
/// <param name="countryCode">a country two letter ISO (ex.: "IT") to add the holidays specific for that country</param>
/// <param name="cityName">a city name to add the holidays specific for that city</param>
/// <returns></returns>
public static IEnumerable<DateTime> GetHolidays(IEnumerable<int> years, string countryCode = null, string cityName = null)
{
var lst = new List<DateTime>();
foreach (var year in years.Distinct())
{
lst.AddRange(new[] {
new DateTime(year, 1, 1), // 1 gennaio (capodanno)
new DateTime(year, 1, 6), // 6 gennaio (epifania)
new DateTime(year, 5, 1), // 1 maggio (lavoro)
new DateTime(year, 8, 15), // 15 agosto (ferragosto)
new DateTime(year, 11, 1), // 1 novembre (ognissanti)
new DateTime(year, 12, 8), // 8 dicembre (immacolata concezione)
new DateTime(year, 12, 25), // 25 dicembre (natale)
new DateTime(year, 12, 26) // 26 dicembre (s. stefano)
});
// add easter sunday (pasqua) and monday (pasquetta)
var easterDate = GetEasterSunday(year);
lst.Add(easterDate);
lst.Add(easterDate.AddDays(1));
// country-specific holidays
if (!String.IsNullOrEmpty(countryCode))
{
switch (countryCode.ToUpper())
{
case "IT":
lst.Add(new DateTime(year, 4, 25)); // 25 aprile (liberazione)
break;
case "US":
lst.Add(new DateTime(year, 7, 4)); // 4 luglio (Independence Day)
break;
// todo: add other countries
case default:
// unsupported country: do nothing
break;
}
}
// city-specific holidays
if (!String.IsNullOrEmpty(cityName))
{
switch (cityName)
{
case "Rome":
case "Roma":
lst.Add(new DateTime(year, 6, 29)); // 29 giugno (s. pietro e paolo)
break;
case "Milano":
case "Milan":
lst.Add(new DateTime(year, 12, 7)); // 7 dicembre (s. ambrogio)
break;
// todo: add other cities
default:
// unsupported city: do nothing
break;
}
}
}
return lst;
}
}
Usage info
The code is quite self-explanatory, however here's a couple examples to explain how you can use it.
Add 10 business days (skipping only saturday and sunday week days)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10);
Add 10 business days (skipping saturday, sunday and all country-invariant holidays for 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019));
Add 10 business days (skipping saturday, sunday and all italian holidays for 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT"));
Add 10 business days (skipping saturday, sunday, all italian holidays and the Rome-specific holidays for 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT", "Rome"));
The above functions and code examples are further explained in this post of my blog.

Ok so this solution is slightly different (has some good and bad points):
Solves weekends depending on the country (i.e. Arab countries have Friday-Saturday weekends)
Depends on external library https://www.nuget.org/packages/Nager.Date/
Only deals with adding business days
Skips holidays as well (this can be removed)
Uses recursion
public static class DateTimeExtensions
{
public static DateTime AddBusinessDays(this DateTime date, int days, CountryCode countryCode)
{
if (days < 0)
{
throw new ArgumentException("days cannot be negative", "days");
}
if (days == 0)
{
return date;
}
date = date.AddDays(1);
if (DateSystem.IsWeekend(date, countryCode) || DateSystem.IsPublicHoliday(date, countryCode))
{
return date.AddBusinessDays(days, countryCode);
}
days -= 1;
return date.AddBusinessDays(days, countryCode);
}
}
Usage:
[TestFixture]
public class BusinessDaysTests
{
[TestCase("2021-06-04", 5, "2021-06-11", Nager.Date.CountryCode.GB)]
[TestCase("2021-06-04", 6, "2021-06-14", Nager.Date.CountryCode.GB)]
[TestCase("2021-05-28", 6, "2021-06-08", Nager.Date.CountryCode.GB)] // UK holidays 2021-05-31
[TestCase("2021-06-01", 3, "2021-06-06", Nager.Date.CountryCode.KW)] // Friday-Saturday weekend in Kuwait
public void AddTests(DateTime initDate, int delayDays, DateTime expectedDate, Nager.Date.CountryCode countryCode)
{
var outputDate = initDate.AddBusinessDays(delayDays, countryCode);
Assert.AreEqual(expectedDate, outputDate);
}
}

public static DateTime AddBusinessDays(DateTime date, int days)
{
if (days == 0) return date;
int i = 0;
while (i < days)
{
if (!(date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday)) i++;
date = date.AddDays(1);
}
return date;
}

I wanted an "AddBusinessDays" that supported negative numbers of days to add, and I ended up with this:
// 0 == Monday, 6 == Sunday
private static int epochDayToDayOfWeek0Based(long epochDay) {
return (int)Math.floorMod(epochDay + 3, 7);
}
public static int daysBetween(long fromEpochDay, long toEpochDay) {
// http://stackoverflow.com/questions/1617049/calculate-the-number-of-business-days-between-two-dates
final int fromDOW = epochDayToDayOfWeek0Based(fromEpochDay);
final int toDOW = epochDayToDayOfWeek0Based(toEpochDay);
long calcBusinessDays = ((toEpochDay - fromEpochDay) * 5 + (toDOW - fromDOW) * 2) / 7;
if (toDOW == 6) calcBusinessDays -= 1;
if (fromDOW == 6) calcBusinessDays += 1;
return (int)calcBusinessDays;
}
public static long addDays(long epochDay, int n) {
// https://alecpojidaev.wordpress.com/2009/10/29/work-days-calculation-with-c/
// NB: in .NET, Sunday == 0, but in our code Monday == 0
final int dow = (epochDayToDayOfWeek0Based(epochDay) + 1) % 7;
final int wds = n + (dow == 0 ? 1 : dow); // Adjusted number of working days to add, given that we now start from the immediately preceding Sunday
final int wends = n < 0 ? ((wds - 5) / 5) * 2
: (wds / 5) * 2 - (wds % 5 == 0 ? 2 : 0);
return epochDay - dow + // Find the immediately preceding Sunday
wds + // Add computed working days
wends; // Add weekends that occur within each complete working week
}
No loop required, so it should be reasonably fast even for "big" additions.
It works with days expressed as a number of calendar days since the epoch, since that's exposed by the new JDK8 LocalDate class and I was working in Java. Should be simple to adapt to other settings though.
The fundamental properties are that addDays always returns a weekday, and that for all d and n, daysBetween(d, addDays(d, n)) == n
Note that theoretically speaking adding 0 days and subtracting 0 days should be different operations (if your date is a Sunday, adding 0 days should take you to Monday, and subtracting 0 days should take you to Friday). Since there's no such thing as negative 0 (outside of floating point!), I've chosen to interpret an argument n=0 as meaning add zero days.

I believe this could be a simpler way to GetBusinessDays:
public int GetBusinessDays(DateTime start, DateTime end, params DateTime[] bankHolidays)
{
int tld = (int)((end - start).TotalDays) + 1; //including end day
int not_buss_day = 2 * (tld / 7); //Saturday and Sunday
int rest = tld % 7; //rest.
if (rest > 0)
{
int tmp = (int)start.DayOfWeek - 1 + rest;
if (tmp == 6 || start.DayOfWeek == DayOfWeek.Sunday) not_buss_day++; else if (tmp > 6) not_buss_day += 2;
}
foreach (DateTime bankHoliday in bankHolidays)
{
DateTime bh = bankHoliday.Date;
if (!(bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday) && (start <= bh && bh <= end))
{
not_buss_day++;
}
}
return tld - not_buss_day;
}

public static DateTime AddWorkingDays(this DateTime date, int daysToAdd)
{
while (daysToAdd > 0)
{
date = date.AddDays(1);
if (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday)
{
daysToAdd -= 1;
}
}
return date;
}

public static int GetBusinessDays(this DateTime start, DateTime end)
{
return Enumerable.Range(0, (end- start).Days)
.Select(a => start.AddDays(a))
.Where(a => a.DayOfWeek != DayOfWeek.Sunday)
.Where(a => a.DayOfWeek != DayOfWeek.Saturday)
.Count();
}

Here is my code with both departure date, and delivery date at customer.
// Calculate departure date
TimeSpan DeliveryTime = new TimeSpan(14, 30, 0);
TimeSpan now = DateTime.Now.TimeOfDay;
DateTime dt = DateTime.Now;
if (dt.TimeOfDay > DeliveryTime) dt = dt.AddDays(1);
if (dt.DayOfWeek == DayOfWeek.Saturday) dt = dt.AddDays(1);
if (dt.DayOfWeek == DayOfWeek.Sunday) dt = dt.AddDays(1);
dt = dt.Date + DeliveryTime;
string DepartureDay = "today at "+dt.ToString("HH:mm");
if (dt.Day!=DateTime.Now.Day)
{
DepartureDay = dt.ToString("dddd at HH:mm", new CultureInfo(WebContextState.CurrentUICulture));
}
Return DepartureDay;
// Caclulate delivery date
dt = dt.AddDays(1);
if (dt.DayOfWeek == DayOfWeek.Saturday) dt = dt.AddDays(1);
if (dt.DayOfWeek == DayOfWeek.Sunday) dt = dt.AddDays(1);
string DeliveryDay = dt.ToString("dddd", new CultureInfo(WebContextState.CurrentUICulture));
return DeliveryDay;

You can use the Fluent DateTime library easily. Please refer to this Github library repository. https://github.com/FluentDateTime/FluentDateTime
var dateTime = DateTime.Now.AddBusinessDays(5);
You can consider weekdays as BusinessDays

Hope this helps someone.
private DateTime AddWorkingDays(DateTime addToDate, int numberofDays)
{
addToDate= addToDate.AddDays(numberofDays);
while (addToDate.DayOfWeek == DayOfWeek.Saturday || addToDate.DayOfWeek == DayOfWeek.Sunday)
{
addToDate= addToDate.AddDays(1);
}
return addToDate;
}

Related

How to Subtract Business days?

How do you subtract Business days in C# based on this for Adding Business days:
public static DateTime AddBusinessDays(DateTime date, int days = 5)
{
if (days < 0)
{
throw new ArgumentException("days cannot be negative", "days");
}
if (days == 0) return date;
if (date.DayOfWeek == DayOfWeek.Saturday)
{
date = date.AddDays(2);
days -= 1;
}
else if (date.DayOfWeek == DayOfWeek.Sunday)
{
date = date.AddDays(1);
days -= 1;
}
date = date.AddDays(days / 5 * 7);
int extraDays = days % 5;
if ((int)date.DayOfWeek + extraDays > 5)
{
extraDays += 2;
}
return date.AddDays(extraDays);
}
That cannot take negative numbers, so need another one specifically for subtracting business days.
Edit: That "duplicate" question is measuring the difference between two dates with business days. I just want a starting date, and amount of days to subtract to come up with the result.
This to be done as a function, not as a extension, as you see above for AddDays.
And a method without loops as you see above would be the most efficient.
See this Answer
(Linked answer has been duplicated below for convenience)
using Fluent DateTime:
var now = DateTime.Now;
var dateTime1 = now.AddBusinessDays(3);
var dateTime2 = now.SubtractBusinessDays(5);
internal code is as follows
/// <summary>
/// Adds the given number of business days to the <see cref="DateTime"/>.
/// </summary>
/// <param name="current">The date to be changed.</param>
/// <param name="days">Number of business days to be added.</param>
/// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
public static DateTime AddBusinessDays(this DateTime current, int days)
{
var sign = Math.Sign(days);
var unsignedDays = Math.Abs(days);
for (var i = 0; i < unsignedDays; i++)
{
do
{
current = current.AddDays(sign);
}
while (current.DayOfWeek == DayOfWeek.Saturday ||
current.DayOfWeek == DayOfWeek.Sunday);
}
return current;
}
/// <summary>
/// Subtracts the given number of business days to the <see cref="DateTime"/>.
/// </summary>
/// <param name="current">The date to be changed.</param>
/// <param name="days">Number of business days to be subtracted.</param>
/// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
public static DateTime SubtractBusinessDays(this DateTime current, int days)
{
return AddBusinessDays(current, -days);
}

Compute the DateTime of an upcoming weekday

How can I get the date of next Tuesday?
In PHP, it's as simple as strtotime('next tuesday');.
How can I achieve something similar in .NET
As I've mentioned in the comments, there are various things you could mean by "next Tuesday", but this code gives you "the next Tuesday to occur, or today if it's already Tuesday":
DateTime today = DateTime.Today;
// The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
int daysUntilTuesday = ((int) DayOfWeek.Tuesday - (int) today.DayOfWeek + 7) % 7;
DateTime nextTuesday = today.AddDays(daysUntilTuesday);
If you want to give "a week's time" if it's already Tuesday, you can use:
// This finds the next Monday (or today if it's Monday) and then adds a day... so the
// result is in the range [1-7]
int daysUntilTuesday = (((int) DayOfWeek.Monday - (int) today.DayOfWeek + 7) % 7) + 1;
... or you could use the original formula, but from tomorrow:
DateTime tomorrow = DateTime.Today.AddDays(1);
// The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
int daysUntilTuesday = ((int) DayOfWeek.Tuesday - (int) tomorrow.DayOfWeek + 7) % 7;
DateTime nextTuesday = tomorrow.AddDays(daysUntilTuesday);
EDIT: Just to make this nice and versatile:
public static DateTime GetNextWeekday(DateTime start, DayOfWeek day)
{
// The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
int daysToAdd = ((int) day - (int) start.DayOfWeek + 7) % 7;
return start.AddDays(daysToAdd);
}
So to get the value for "today or in the next 6 days":
DateTime nextTuesday = GetNextWeekday(DateTime.Today, DayOfWeek.Tuesday);
To get the value for "the next Tuesday excluding today":
DateTime nextTuesday = GetNextWeekday(DateTime.Today.AddDays(1), DayOfWeek.Tuesday);
This should do the trick:
static DateTime GetNextWeekday(DayOfWeek day)
{
DateTime result = DateTime.Now.AddDays(1);
while( result.DayOfWeek != day )
result = result.AddDays(1);
return result;
}
There are less verbose and more clever/elegant solutions to this problem, but the following C# function works really well for a number of situations.
/// <summary>
/// Find the closest weekday to the given date
/// </summary>
/// <param name="includeStartDate">if the supplied date is on the specified day of the week, return that date or continue to the next date</param>
/// <param name="searchForward">search forward or backward from the supplied date. if a null parameter is given, the closest weekday (ie in either direction) is returned</param>
public static DateTime ClosestWeekDay(this DateTime date, DayOfWeek weekday, bool includeStartDate = true, bool? searchForward=true)
{
if (!searchForward.HasValue && !includeStartDate)
{
throw new ArgumentException("if searching in both directions, start date must be a valid result");
}
var day = date.DayOfWeek;
int add = ((int)weekday - (int)day);
if (searchForward.HasValue)
{
if (add < 0 && searchForward.Value)
{
add += 7;
}
else if (add > 0 && !searchForward.Value)
{
add -= 7;
}
else if (add == 0 && !includeStartDate)
{
add = searchForward.Value ? 7 : -7;
}
}
else if (add < -3)
{
add += 7;
}
else if (add > 3)
{
add -= 7;
}
return date.AddDays(add);
}
#Jon Skeet good answer.
For previous Day:
private DateTime GetPrevWeekday(DateTime start, DayOfWeek day) {
// The (... - 7) % 7 ensures we end up with a value in the range [0, 6]
int daysToRemove = ((int) day - (int) start.DayOfWeek - 7) % 7;
return start.AddDays(daysToRemove);
}
Thanks!!
DateTime nextTuesday = DateTime.Today.AddDays(((int)DateTime.Today.DayOfWeek - (int)DayOfWeek.Tuesday) + 7);
Very simple sample to include or exclude current date, you specify the date and the day the week you are interested in.
public static class DateTimeExtensions
{
/// <summary>
/// Gets the next date.
/// </summary>
/// <param name="date">The date to inspected.</param>
/// <param name="dayOfWeek">The day of week you want to get.</param>
/// <param name="exclDate">if set to <c>true</c> the current date will be excluded and include next occurrence.</param>
/// <returns></returns>
public static DateTime GetNextDate(this DateTime date, DayOfWeek dayOfWeek, bool exclDate = true)
{
//note: first we need to check if the date wants to move back by date - Today, + diff might move it forward or backwards to Today
//eg: date - Today = 0 - 1 = -1, so have to move it forward
var diff = dayOfWeek - date.DayOfWeek;
var ddiff = date.Date.Subtract(DateTime.Today).Days + diff;
//note: ddiff < 0 : date calculates to past, so move forward, even if the date is really old, it will just move 7 days from date passed in
//note: ddiff >= (exclDate ? 6 : 7) && diff < 0 : date is into the future, so calculated future weekday, based on date
if (ddiff < 0 || ddiff >= (exclDate ? 6 : 7) && diff < 0)
diff += 7;
//note: now we can get safe values between 0 - 6, especially if past dates is being used
diff = diff % 7;
//note: if diff is 0 and we are excluding the date passed, we will add 7 days, eg: 1 week
diff += diff == 0 & exclDate ? 7 : 0;
return date.AddDays(diff);
}
}
some test cases
[TestMethod]
public void TestNextDate()
{
var date = new DateTime(2013, 7, 15);
var start = date;
//testing same month - forwardOnly
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday)); //16
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday)); //17
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Thursday)); //18
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Friday)); //19
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Saturday)); //20
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Sunday)); //21
Assert.AreEqual(start.AddDays(1), date.GetNextDate(DayOfWeek.Monday)); //22
//testing same month - include date
Assert.AreEqual(start = date, date.GetNextDate(DayOfWeek.Monday, false)); //15
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday, false)); //16
Assert.AreEqual(start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday, false)); //17
//testing month change - forwardOnly
date = new DateTime(2013, 7, 29);
start = date;
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday)); //30
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday)); //31
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Thursday)); //2013/09/01-month increased
Assert.AreEqual(start.AddDays(1), date.GetNextDate(DayOfWeek.Friday)); //02
//testing year change
date = new DateTime(2013, 12, 30);
start = date;
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday)); //31
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday)); //2014/01/01 - year increased
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Thursday)); //02
}
DateTime nexttuesday=DateTime.Today.AddDays(1);
while(nexttuesday.DayOfWeek!=DayOfWeek.Tuesday)
nexttuesday = nexttuesday.AddDays(1);
Now in oneliner flavor - in case you need to pass it as parameter into some mechanism.
DateTime.Now.AddDays(((int)yourDate.DayOfWeek - (int)DateTime.Now.DayOfWeek + 7) % 7).Day
In this specific case:
DateTime.Now.AddDays(((int)DayOfWeek.Tuesday - (int)DateTime.Now.DayOfWeek + 7) % 7).Day
It could be an extension also, it all depends
public static class DateTimeExtensions
{
public static IEnumerable<DateTime> Next(this DateTime date, DayOfWeek day)
{
// This loop feels expensive and useless, but the point is IEnumerable
while(true)
{
if (date.DayOfWeek == day)
{
yield return date;
}
date = date.AddDays(1);
}
}
}
Usage
var today = DateTime.Today;
foreach(var monday in today.Next(DayOfWeek.Monday))
{
Console.WriteLine(monday);
Console.ReadKey();
}
I want to get next day from this time include today
also I want it to be at 12:00 AM
public static DateTime GetNextWeekday(DateTime start, DayOfWeek day , bool includetoday = false) {
if (includetoday && start.DayOfWeek == day) return start.Date;
int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7;
return start.Date.AddDays(daysToAdd);
}
Objective C Version:
+(NSInteger) daysUntilNextWeekday: (NSDate*)startDate withTargetWeekday: (NSInteger) targetWeekday
{
NSInteger startWeekday = [[NSCalendar currentCalendar] component:NSCalendarUnitWeekday fromDate:startDate];
return (targetWeekday - startWeekday + 7) % 7;
}

Counting regular working days in a given period of time

need some help. I need to count regular working days for a given date period, for example, in our country, we have 5 regular working days monday to friday, then in code i need to exclude saturdays and sundays when I use it on my computations.
I need an algorithm something like this in C#:
int GetRegularWorkingDays(DateTime startDate, DateTime endDate)
{
int nonWorkingDays = ((endDate - startDate) % 7) * 2;
return (endDate - startDate) - nonWorkingDays;
}
I know my draft is way way off :(. Thanks in advance. =)
PS: Guys please up-vote the best/fastest/most efficient answer below. Thanks =)
Check out this example on Code Project that uses a very efficient way that doesn't involve any looping ;)
It uses this alogrithm:
Calculate the number of time span in terms of weeks. Call it, W.
Deduct the first week from the number of weeks. W= W-1
Multiply the number of weeks with the number of working days per
week. Call it, D.
Find out the holidays during the specified time span. Call it, H.
Calculate the days in the first week. Call it, SD.
Calculate the days in the last week. Call it, ED.
Sum up all the days. BD = D + SD + ED - H.
One-liner!
int workingDays = Enumerable.Range(0, Convert.ToInt32(endDate.Subtract(startDate).TotalDays)).Select(i=>new [] { DayOfWeek.Saturday, DayOfWeek.Sunday }.Contains(startDate.AddDays(i).DayOfWeek) ? 0 : 1).Sum();
Or more efficient:
DayOfWeek currDay = startDate.DayOfWeek;
int nonWorkingDays = 0;
foreach(var i in Enumerable.Range(0, Convert.ToInt32(endDate.Subtract(startDate).TotalDays)))
{
if(currDay == DayOfWeek.Sunday || currDay == DayOfWeek.Saturday)
nonWorkingDays++;
if((int)++currDay > 6) currDay = (DayOfWeek)0;
}
I wrote a type extender to allow me to add (or subtract) weekdays to a given date. Maybe this will help you. Works great, so please vote for my post if this helped you.
/// <summary>
/// Adds weekdays to date
/// </summary>
/// <param name="value">DateTime to add to</param>
/// <param name="weekdays">Number of weekdays to add</param>
/// <returns>DateTime</returns>
public static DateTime AddWeekdays(this DateTime value, int weekdays)
{
int direction = Math.Sign(weekdays);
int initialDayOfWeek = Convert.ToInt32(value.DayOfWeek);
//---------------------------------------------------------------------------
// if the day is a weekend, shift to the next weekday before calculating
if ((value.DayOfWeek == DayOfWeek.Sunday && direction < 0)
|| (value.DayOfWeek == DayOfWeek.Saturday && direction > 0))
{
value = value.AddDays(direction * 2);
weekdays += (direction * -1); // adjust days to add by one
}
else if ((value.DayOfWeek == DayOfWeek.Sunday && direction > 0)
|| (value.DayOfWeek == DayOfWeek.Saturday && direction < 0))
{
value = value.AddDays(direction);
weekdays += (direction * -1); // adjust days to add by one
}
//---------------------------------------------------------------------------
int weeksBase = Math.Abs(weekdays / 5);
int addDays = Math.Abs(weekdays % 5);
int totalDays = (weeksBase * 7) + addDays;
DateTime result = value.AddDays(totalDays * direction);
//---------------------------------------------------------------------------
// if the result is a weekend, shift to the next weekday
if ((result.DayOfWeek == DayOfWeek.Sunday && direction > 0)
|| (result.DayOfWeek == DayOfWeek.Saturday && direction < 0))
{
result = result.AddDays(direction);
}
else if ((result.DayOfWeek == DayOfWeek.Sunday && direction < 0)
|| (result.DayOfWeek == DayOfWeek.Saturday && direction > 0))
{
result = result.AddDays(direction * 2);
}
//---------------------------------------------------------------------------
return result;
}
Not very fast, but this will do the trick:
int GetRegularWorkingDays(DateTime start, DateTime end)
{
return (
from day in Range(start, end)
where day.DayOfWeek != DayOfWeek.Saturday
where day.DayOfWeek != DayOfWeek.Sunday
select day).Count();
}
IEnumerable<DateTime> Range(DateTime start, DateTime end)
{
while (start <= end)
{
yield return start;
start = start.AddDays(1);
}
}
You could try a simple method of just counting the days. If this is usually done for periods of time like months and not years and isn't called repeatedly then this will not be a performance hit to just walk it.
int GetWorkingDays(DateTime startDate, DateTime endDate)
{
int count = 0;
for (DateTime currentDate = startDate; currentDate < endDate; currentDate = currentDate.AddDays(1))
{
if (currentDate.DayOfWeek == DayOfWeek.Sunday || currentDate.DayOfWeek == DayOfWeek.Saturday)
{
continue;
}
count++;
}
return count;
}
You could do it with a time line helper class - this class also allows for other intervals:
public class TimeLine
{
public static IEnumerable<DateTime> CreateTimeLine(DateTime start, TimeSpan interval) {
return TimeLine.CreateTimeLine(start, interval, DateTime.MaxValue);
}
public static IEnumerable<DateTime> CreateTimeLine(DateTime start, TimeSpan interval, DateTime end) {
var currentVal = start;
var endVal = end.Subtract(interval);
do {
currentVal = currentVal.Add(interval);
yield return currentVal;
} while (currentVal <= endVal);
}
}
To solve your problem you can do the following:
var workingDays = TimeLine.CreateTimeLine(DateTime.Now.Date, TimeSpan.FromDays(1), DateTime.Now.Date.AddDays(30))
.Where(x => x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday);
var noOfWorkingDays = workingDays.Count();
This time line class can be used for any continuous time line of any interval.
Simple method to get work days:
int GetRegularWorkingDays(DateTime startDate, DateTime endDate)
{
int total = 0;
if (startDate < endDate)
{
var days = endDate - startDate;
for( ; startDate < endDate; startDate = startDate.AddDays(1) )
{
switch(startDate.DayOfWeek)
{
case DayOfWeek.Saturday :
case DayOfWeek.Sunday :
break;
default:
total++;
break;
}
}
}
return total;
}
int count = 0;
switch (dateTimePicker2.Value.DayOfWeek.ToString())
{
case "Saturday": count--; break;
case "Sunday": count--; break;
default:break;
}
switch (dateTimePicker3.Value.DayOfWeek.ToString())
{
case "Saturday": count--; break;
case "Sunday": count--; break;
default:break;
}
if (count == -2)
count = -1;
int weeks = t.Days / 7;
int daycount =count+ t.Days - (2 * weeks)+1;
label7.Text = "No of Days : " + daycount.ToString();

Get the number of calendar weeks between 2 dates in C#

For the purposes of this question, let's assume the user will be from the US and will use the standard Gregorian calendar. So, a calendar week starts on Sunday and ends on Saturday.
What I'm trying to do is determine the number of calendar weeks that exist between two dates. A perfect example of my problem exists in October 2010. Between 10/16 and 10/31 there are 4 calendar weeks.
October 10 - October 16
October 17 - October 23
October 24 - October 30
October 31 - November 6
I'd prefer to stay away from any hardcoded logic like:
if (Day == DayOfWeek.Saturday && LastDayOfMonth == 31) { ... }
Can anyone think of a logical way to do this?
UPDATE:
Thanks for all the great responses, after some consideration here is the solution I used:
//get the start and end dates of the current pay period
DateTime currentPeriodStart = SelectedPeriod.Model.PeriodStart;
DateTime currentPeriodEnd = SelectedPeriod.Model.PeriodEnd;
//get the first sunday & last saturday span that encapsulates the current pay period
DateTime firstSunday = DayExtensions.SundayBeforePeriodStart(currentPeriodStart);
DateTime lastSaturday = DayExtensions.SaturdayAfterPeriodEnd(currentPeriodEnd);
//get the number of calendar weeks in the span
int numberOfCalendarWeeks = DayExtensions.CalendarWeeks(firstSunday, lastSaturday);
And here are the methods from the helper class:
/// <summary>
/// Get the first Sunday before the pay period start date
/// </summary>
/// <param name="periodStartDate">Date of the pay period start date</param>
/// <returns></returns>
public static DateTime SundayBeforePeriodStart(DateTime periodStartDate)
{
DateTime firstDayOfWeekBeforeStartDate;
int daysBetweenStartDateAndPreviousFirstDayOfWeek = (int)periodStartDate.DayOfWeek - (int)DayOfWeek.Sunday;
if (daysBetweenStartDateAndPreviousFirstDayOfWeek >= 0)
{
firstDayOfWeekBeforeStartDate = periodStartDate.AddDays(-daysBetweenStartDateAndPreviousFirstDayOfWeek);
}
else
{
firstDayOfWeekBeforeStartDate = periodStartDate.AddDays(-(daysBetweenStartDateAndPreviousFirstDayOfWeek + 7));
}
return firstDayOfWeekBeforeStartDate;
}
/// <summary>
/// Get the first Saturday after the period end date
/// </summary>
/// <param name="periodEndDate">Date of the pay period end date</param>
/// <returns></returns>
public static DateTime SaturdayAfterPeriodEnd(DateTime periodEndDate)
{
DateTime lastDayOfWeekAfterEndDate;
int daysBetweenEndDateAndFollowingLastDayOfWeek = (int)DayOfWeek.Saturday - (int)periodEndDate.DayOfWeek;
if (daysBetweenEndDateAndFollowingLastDayOfWeek >= 0)
{
lastDayOfWeekAfterEndDate = periodEndDate.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek);
}
else
{
lastDayOfWeekAfterEndDate = periodEndDate.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek + 7);
}
return lastDayOfWeekAfterEndDate;
}
/// <summary>
/// Get the calendar weeks between 2 dates
/// </summary>
/// <param name="d1">First day of date span</param>
/// <param name="d2">Last day of date span</param>
/// <returns></returns>
public static int CalendarWeeks(DateTime d1, DateTime d2)
{
return 1 + (int)((d2 - d1).TotalDays / 7);
}
And if you're curious, this is what I end up doing with the dates:
//create an array of all the sundays in this span
DateTime[] _sundays = new DateTime[numberOfCalendarWeeks];
//put the first sunday in the period
_sundays[0] = firstSunday;
//step through each week and get each sunday until you reach the last saturday
for (int i = 1; i <= numberOfCalendarWeeks - 1; i++)
{
DateTime d = new DateTime();
d = firstSunday.AddDays(i * 7);
_sundays[i] = d;
}
for (int i = 0; i <= _sundays.Length-1; i++)
{
//bind my view model with each sunday.
}
Here's a general solution which I believe should work for any choice of week starting and ending days. You could simplify it for your case, but this code gives you the option of changing the week's start and end (e.g. to Monday to Sunday) if it becomes necessary. It's not uncommon in payroll applications for the definition of a calendar week to change.
DateTime periodStart = new DateTime(2010, 10, 17);
DateTime periodEnd = new DateTime(2010, 11, 14);
const DayOfWeek FIRST_DAY_OF_WEEK = DayOfWeek.Monday;
const DayOfWeek LAST_DAY_OF_WEEK = DayOfWeek.Sunday;
const int DAYS_IN_WEEK = 7;
DateTime firstDayOfWeekBeforeStartDate;
int daysBetweenStartDateAndPreviousFirstDayOfWeek = (int)periodStart.DayOfWeek - (int)FIRST_DAY_OF_WEEK;
if (daysBetweenStartDateAndPreviousFirstDayOfWeek >= 0)
{
firstDayOfWeekBeforeStartDate = periodStart.AddDays(-daysBetweenStartDateAndPreviousFirstDayOfWeek);
}
else
{
firstDayOfWeekBeforeStartDate = periodStart.AddDays(-(daysBetweenStartDateAndPreviousFirstDayOfWeek + DAYS_IN_WEEK));
}
DateTime lastDayOfWeekAfterEndDate;
int daysBetweenEndDateAndFollowingLastDayOfWeek = (int)LAST_DAY_OF_WEEK - (int)periodEnd.DayOfWeek;
if (daysBetweenEndDateAndFollowingLastDayOfWeek >= 0)
{
lastDayOfWeekAfterEndDate = periodEnd.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek);
}
else
{
lastDayOfWeekAfterEndDate = periodEnd.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek + DAYS_IN_WEEK);
}
int calendarWeeks = 1 + (int)((lastDayOfWeekAfterEndDate - firstDayOfWeekBeforeStartDate).TotalDays / DAYS_IN_WEEK);
The way I would tackle this is to get the beginning of the week for any given date. Using that, you would subtract the result of the start date from the result of the end date. Your day difference would then always be a multiple of 7, so divide and add 1 (0 days => 1 week, 7 days => 2 weeks, etc.). This would tell you how many calendar weeks were covered or represented by any two dates.
static int GetWeeksCovered(DateTime startDate, DateTime endDate)
{
if (endDate < startDate)
throw new ArgumentException("endDate cannot be less than startDate");
return (GetBeginningOfWeek(endDate).Subtract(GetBeginningOfWeek(startDate)).Days / 7) + 1;
}
static DateTime GetBeginningOfWeek(DateTime date)
{
return date.AddDays(-1 * (int)date.DayOfWeek).Date;
}
16-Oct-2010 and 16-Oct-2010 => 1 week covered (or represented).
16-Oct-2010 and 31-Oct-2010 => 4 weeks covered, as per the spec.
Bear in mind, that week calculations are done differently in different cultures and there is not a bug if you see week number 53!
using System.Globalization;
CultureInfo cultInfo = CultureInfo.CurrentCulture;
int weekNumNow = cultInfo.Calendar.GetWeekOfYear(DateTime.Now,
cultInfo.DateTimeFormat.CalendarWeekRule,
cultInfo.DateTimeFormat.FirstDayOfWeek);
private static int weekDifference(DateTime startDate, DateTime endDate)
{
int monthsApart = 12 * (startDate.Year - endDate.Year) + startDate.Month - endDate.Month;
return Math.Abs(monthsApart*4);
}
Cant See a better way right off the bat.
Saturday is the last day of the week huh?
public int CalendarWeeks(DateTime from, DateTime to) {
// number of multiples of 7
// (rounded up, since 15 days would span at least 3 weeks)
// and if we end on a day before we start, we know it's another week
return (int)Math.Ceiling(to.Subtract(from).Days / 7.0) +
(to.DayOfWeek <= from.DayOfWeek) ? 1 : 0;
}
The following seems to work for any date range. It should be culturally sound, and should account for leap years/days or other calendar oddities:
private static int getWeeksSpannedBy(DateTime first, DateTime last)
{
var calendar = CultureInfo.CurrentCulture.Calendar;
var weekRule = CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule;
var firstDayOfWeek = DayOfWeek.Sunday;
int lastWeek = calendar.GetWeekOfYear(last, weekRule, firstDayOfWeek);
int firstWeek = calendar.GetWeekOfYear(first, weekRule, firstDayOfWeek);
int weekDiff = lastWeek - firstWeek + 1;
return weekDiff;
}
static void Main(string[] args)
{
int weeks1 = getWeeksSpannedBy(new DateTime(2010, 1, 3), new DateTime(2010, 1, 9));
int weeks2 = getWeeksSpannedBy(new DateTime(2010, 10, 16), new DateTime(2010, 10, 31));
int weeks3 = getWeeksSpannedBy(new DateTime(2008, 2, 1), new DateTime(2008, 2, 29));
int weeks4 = getWeeksSpannedBy(new DateTime(2012, 2, 1), new DateTime(2012, 2, 29));
Console.WriteLine("Weeks Difference #1: " + weeks1);
Console.WriteLine("Weeks Difference #2: " + weeks2);
Console.WriteLine("Weeks Difference #3: " + weeks3);
Console.WriteLine("Weeks Difference #4: " + weeks4);
Console.ReadLine();
}
Prints out the following, which is correct for all date ranges, past, present, or future (leap year 2008 and 2012 both have 5 weeks between Feb 1 and Feb 29):
Weeks Difference #1: 1
Weeks Difference #2: 4
Weeks Difference #3: 5
Weeks Difference #4: 5
private static int weekDifference(DateTime startDate, DateTime endDate)
{
const int firstDayOfWeek = 0; // Sunday
int wasteDaysStart = (7+startDate.DatOfWeek-firstDayOfWeek)%7;
return (int)(((endDate-startDate).TotalDays() + wasteDaysStart + 6)/7);
}
Warning: untested code. Please test and remove note.

Calculate the number of weekdays between two dates in C#

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:
The total should be inclusive, so GetNumberOfWeekdays(new DateTime(2009,11,30), new DateTime(2009,12,4)) should equal 5, that's Monday to Friday.
Should allow for leap days
does NOT just iterate through all the dates between whilst counting the weekdays.
I've found a similar question with an answer that comes close but is not correct
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.
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);
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;
}
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;
}
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;
}
}
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);
}
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;
}
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");
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.
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);
}
}
}
}
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;
}

Categories