简体   繁体   中英

Adding Days to a Date but Excluding Weekends

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.

使用 Fluent DateTime https://github.com/FluentDateTime/FluentDateTime

var dateTime = DateTime.Now.AddBusinessDays(4);
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);
}

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;
}
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 (ie is a weekend day).

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

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

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();
        }
    }
}

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 

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

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)

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);
    }
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);
}

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

Try this. This will help.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM