35

Given a date how can I add a number of days to it, but exclude weekends. For example, given 11/12/2008 (Wednesday) and adding five will result in 11/19/2008 (Wednesday) rather than 11/17/2008 (Monday).

I can think of a simple solution like looping through each day to add and checking to see if it is a weekend, but I'd like to see if there is something more elegant. I'd also be interested in any F# solution.

Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
Bob
  • 97,670
  • 29
  • 122
  • 130
  • Are you going to be adding a small number of days or millions of days? – LeppyR64 Nov 10 '08 at 21:51
  • 10
    Just letting you know - as soon as you tell them that excluding weekends is no problem, they're going to want holidays, too. Think about that before you implement anything. – dnord Nov 10 '08 at 22:12
  • There is a library on NuGet named TimePeriodLibrary.NET by Jani Giannoudis that has an enormous amount of functionality for these kinds of calculations. If they later want holidays too, it will be easy. – Bent Tranberg Aug 11 '21 at 15:17

13 Answers13

29

using Fluent DateTime https://github.com/FluentDateTime/FluentDateTime

var dateTime = DateTime.Now.AddBusinessDays(4);
Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
Simon
  • 33,714
  • 21
  • 133
  • 202
9
public DateTime AddBusinessDays(DateTime dt, int nDays)
{
    int weeks = nDays / 5;
    nDays %= 5;
    while(dt.DayOfWeek == DayOfWeek.Saturday || dt.DayOfWeek == DayOfWeek.Sunday)
        dt = dt.AddDays(1);

    while (nDays-- > 0)
    {
        dt = dt.AddDays(1);
        if (dt.DayOfWeek == DayOfWeek.Saturday)
            dt = dt.AddDays(2);
    }
    return dt.AddDays(weeks*7);
}
LeppyR64
  • 5,251
  • 2
  • 30
  • 35
  • 1
    It may be intentional for your scenario but for any dates inputted on a Friday Saturday or Sunday are not Calculated properly. Marco M. Comment will work for this. – Frazer Jan 20 '17 at 12:44
6

Without over-complicating the algorithm, you could just create an extension method like this:

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;
}
tocqueville
  • 5,270
  • 2
  • 40
  • 54
6

I would use this extension, remember since it is an extension method to put it in a static class.

Usage:

var dateTime = DateTime.Now.AddBusinessDays(5);

Code:

namespace ExtensionMethods
{
    public static class MyExtensionMethods
    {
        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;
        }
    }
}

Source:

https://github.com/FluentDateTime/FluentDateTime/blob/master/src/FluentDateTime/DateTime/DateTimeExtensions.cs

Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • 8
    A pity you don't link to the FluentDateTime library instead of copy-pasting the code (https://github.com/FluentDateTime/FluentDateTime/blob/master/FluentDateTime/DateTime/DateTimeExtensions.cs). Give the original developer some credit! – Jules Aug 28 '17 at 12:04
  • @Jules Credit should definitively be given. Copy pasted from a solution if I remember correctly and my source code did not have any reference. Updated now. – Ogglas Jul 13 '20 at 21:32
5

I created an extension that allows you to add or subtract business days. Use a negative number of businessDays to subtract. 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();
        }
    }
}
Arjen
  • 290
  • 3
  • 6
  • 2
    This solution accurately presumes that if your source date is on a weekend, Adding or Subtracting 1 will allow one weekday to pass, resulting in the Tuesday or Thursday respectively. Plus zero will return the Monday. Very efficient, without any iterations/looping required. Thanks! – Riegardt Steyn Feb 02 '16 at 09:54
5
int daysToAdd = weekDaysToAdd + ((weekDaysToAdd / 5) * 2) + (((origDate.DOW + (weekDaysToAdd % 5)) >= 5) ? 2 : 0);

To wit; the number of "real" days to add is the number of weekdays you're specifying, plus the number of complete weeks that are in that total (hence the weekDaysToAdd / 5) times two (two days in the weekend); plus a potential offset of two days if the original day of the week plus the number of weekdays to add "within" the week (hence the weekDaysToAdd mod 5) is greater than or equal to 5 (i.e. is a weekend day).

Note: this works assuming that 0 = Monday, 2 = Tuesday, ... 6 = Sunday. Also; this does not work on negative weekday intervals.

Paul Sonier
  • 38,903
  • 3
  • 77
  • 117
  • 1
    This would work perfectly if the value was rounded to an integer. It is better than looping like all the other responses. – dfmiller Feb 27 '20 at 16:45
1

F# flavor of http://stackoverflow.com/questions/1044688 's answer:

namespace FSharpBasics

module BusinessDays =

    open System;

    let private weekLength = 5

    (*operation*)
    let addBusinessDays (numberOfBusinessDays: int) (startDate: DateTime) =
        let startWeekDay = startDate.DayOfWeek
        let sign = Math.Sign(numberOfBusinessDays) 
        let weekendSlide, businessDaysSlide = 
            match startWeekDay with
            | DayOfWeek.Saturday when sign > 0 -> (2, -1)
            | DayOfWeek.Saturday when sign < 0 -> (-1, 1)   
            | DayOfWeek.Sunday when sign > 0 -> (1, -1)
            | DayOfWeek.Sunday when sign < 0 -> (-2, 1)
            | _ -> (0, 0)
        let baseStartDate = startDate.AddDays (float weekendSlide)        
        let days = Math.Abs (numberOfBusinessDays + businessDaysSlide) % weekLength
        let weeks = Math.Abs (numberOfBusinessDays + businessDaysSlide) / weekLength
        let baseWeekDay = int baseStartDate.DayOfWeek
        let oneMoreWeekend =
            if sign = 1 && days + baseWeekDay > 5 || sign = -1 && days >= baseWeekDay then 2
            else 0
        let totalDays = (weeks * 7) + days + oneMoreWeekend
        baseStartDate.AddDays (float totalDays)

    [<EntryPoint>]
    let main argv =
        let now = DateTime.Now 
        printfn "Now is %A" now
        printfn "13 business days from now would be %A" (addBusinessDays 13 now)
        System.Console.ReadLine() |> ignore
        0 
0

This is better if anyone is looking for a TSQL solution. One line of code and works with negatives.

CREATE FUNCTION[dbo].[AddBusinessDays](@Date date,@n INT)RETURNS DATE AS BEGIN 
DECLARE @d INT;SET @d=4-SIGN(@n)*(4-DATEPART(DW,@Date));
RETURN DATEADD(D,@n+((ABS(@n)+@d-2)/5)*2*SIGN(@n)-@d/7,@Date)END
Andrei V
  • 7,306
  • 6
  • 44
  • 64
ElmerMiller
  • 447
  • 1
  • 4
  • 3
0

Here is how I did it.

I had to calculate SLA (Service Level Agreement) due dates based on a start date and number of days, and account for weekends and public holidays:

    public DateTime? CalculateSLADueDate(DateTime slaStartDateUTC, double slaDays)
    {
        if (slaDays < 0)
        {
            return null;
        }

        var dayCount = slaDays;
        var dueDate = slaStartDateUTC;

        var blPublicHoliday = new PublicHoliday();
        IList<BusObj.PublicHoliday> publicHolidays = blPublicHoliday.SelectAll();

        do
        {
            dueDate = dueDate.AddDays(1);

            if ((dueDate.DayOfWeek != DayOfWeek.Saturday)
            && (dueDate.DayOfWeek != DayOfWeek.Sunday)
            && !publicHolidays.Any(x => x.HolidayDate == dueDate.Date))
            {
                dayCount--;
            }
        }
        while (dayCount > 0);

        return dueDate;
    }

blPublicHoliday.SelectAll() is a cached in-memory list of public holidays.

(note: this is a cut down version for sharing publicly, there is a reason its not an extension method)

Mark Worrall
  • 290
  • 3
  • 9
0
enter code public static DateTime AddWorkDays(DateTime dt,int daysToAdd)
    {
        int temp = daysToAdd;
        DateTime endDateOri = dt.AddDays(daysToAdd);
        while (temp !=0)
        {
            if ((dt.AddDays(temp).DayOfWeek == DayOfWeek.Saturday)|| (dt.AddDays(temp).DayOfWeek == DayOfWeek.Sunday))
            {
                daysToAdd++;
                temp--;
            }
            else
            {
                temp--;
            }
        }
        while (endDateOri.AddDays(temp) != dt.AddDays(daysToAdd))
        {
            if ((dt.AddDays(temp).DayOfWeek == DayOfWeek.Saturday) || (dt.AddDays(temp).DayOfWeek == DayOfWeek.Sunday))
            {
                daysToAdd++;
            }
            temp++;
        }
        // final enddate check
        if (dt.AddDays(daysToAdd).DayOfWeek == DayOfWeek.Saturday)
        {
            daysToAdd = daysToAdd + 2;
        }
        else if (dt.AddDays(daysToAdd).DayOfWeek == DayOfWeek.Sunday)
        {
            daysToAdd++;
        }
        return dt.AddDays(daysToAdd);
    }
Joerg
  • 1
0
DateTime oDate2 = DateTime.Now;

int days = 8;

for(int i = 1; i <= days; i++)
{
  if (oDate.DayOfWeek == DayOfWeek.Saturday) 
  {
    oDate = oDate.AddDays(2);
  }
  if (oDate.DayOfWeek == DayOfWeek.Sunday) 
  {
    oDate = oDate.AddDays(1);
  }
  oDate = oDate.AddDays(1);
}
Dominique
  • 16,450
  • 15
  • 56
  • 112
  • Will not run: missing declaration of oDate (https://stackoverflow.com/help/minimal-reproducible-example) – Kraego Aug 07 '21 at 13:50
-2

Formula will be: Workday(date,no.of days,(weekday(1)))

Try this. This will help.

Adam Lear
  • 38,111
  • 12
  • 81
  • 101
-2

Given the number of the original day in the year D and original day in the week W and the number of workdays to add N, the next weekday number is

W + N % 5.

The next day in the year (with no wraparound check) is

D + ((N / 5) * 7) + N % 5).

This is assuming that you have integer division.

gnud
  • 77,584
  • 5
  • 64
  • 78
  • I don't see any obvious flaw with my answer. Leaving a comment saying what's wrong would be helpful. – gnud Nov 11 '08 at 01:07
  • I didn't downvote but several people have suggested this algorithm and it looks wrong to me. What happens when you add 1 business day to a Friday? `n/5*7` is zero and `n%5` is one so you add one and get a Saturday but the correct answer is Monday. – J D Mar 26 '11 at 10:15
  • Assuming he meant `(W + N) % 5` for the first one, @JonHarrop's example now works, but it breaks if you add more than 5 days to Friday. -1 – jwg Mar 08 '13 at 16:22