Ответ 1
используя Fluent DateTime https://github.com/FluentDateTime/FluentDateTime
var dateTime = DateTime.Now.AddBusinessDays(4);
Учитывая дату, как я могу добавить к ней несколько дней, но исключаю выходные. Например, учитывая 11/12/2008 (среда), а добавление пятерки будет результатом 11/19/2008 (среда), а не 11/17/2008 (понедельник).
Я могу подумать о простом решении, например, прокручивать каждый день, чтобы добавить и проверить, есть ли уик-энд, но я хотел бы посмотреть, есть ли что-то более элегантное. Меня также интересовало бы любое решение F #.
используя 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);
}
Без чрезмерного усложнения алгоритма вы можете просто создать метод расширения следующим образом:
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;
}
Я бы использовал это расширение, помню, потому что это метод расширения, чтобы поместить его в статический класс.
Использование:
var dateTime = DateTime.Now.AddBusinessDays(5);
код:
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;
}
}
}
int daysToAdd = weekDaysToAdd + ((weekDaysToAdd / 5) * 2) + (((origDate.DOW + (weekDaysToAdd % 5)) >= 5) ? 2 : 0);
Для остроумия; количество "реальных" дней для добавления - это количество рабочих дней, которые вы указываете, плюс количество полных недель, которые находятся в этой сумме (следовательно, weekDaysToAdd/5) раз два (два дня в выходные); плюс возможное смещение в течение двух дней, если исходный день недели плюс число будних дней, чтобы добавить "в пределах" недели (следовательно, weekDaysToAdd mod 5) больше или равно 5 (т.е. это день выходного дня).
Примечание: это работает, полагая, что 0 = понедельник, 2 = вторник,... 6 = воскресенье. Также; это не работает с отрицательными интервалами в будние дни.
Я создал расширение, которое позволяет добавлять или вычитать рабочие дни. Используйте отрицательное число businessDays для вычитания. Кажется, что он работает во всех случаях.
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);
}
}
}
Пример:
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();
}
}
}
Это лучше, если кто-то ищет решение TSQL
. Одна строка кода и работает с негативами.
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)[email protected])/5)*2*SIGN(@n)[email protected]/7,@Date)END
F # вкус ответа fooobar.com/questions/76814/...:
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
Вот как я это сделал.
Мне нужно было рассчитать сроки исполнения SLA (Соглашение об уровне обслуживания) на основе даты начала и количества дней, а также для учета выходных и праздничных дней:
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() является кэшированным списком праздничных дней в памяти.
(примечание: это урезанная версия для общего доступа, есть причина, по которой это не метод расширения)
Формула будет: Рабочий день (дата, число дней, (день недели (1)))
Попробуйте это. Это поможет.
Учитывая номер первоначального дня в году D и оригинального дня недели W и количества рабочих дней, чтобы добавить N, следующий номер дня недели -
W + N % 5.
На следующий день в году (без проверки обхода)
D + ((N / 5) * 7) + N % 5).
Это предполагает, что у вас есть целочисленное деление.