AddBusinessDays e GetBusinessDays

93

Eu preciso encontrar 2 elegantes implementações completas de

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) preferível (sem loops).

EDITAR: Por dias úteis quero dizer dias úteis (segunda, terça, quarta, quinta, sexta). Sem feriados, apenas fins de semana excluídos.

Já tenho algumas soluções feias que parecem funcionar, mas me pergunto se existem maneiras elegantes de fazer isso. obrigado


Isso é o que escrevi até agora. Funciona em todos os casos e também faz negativos. Ainda precisa de uma implementação GetBusinessDays

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);
}
Adrian Zanescu
fonte
14
Existem soluções elegantes quando se trata de algo tão ilógico como datas?
Wyatt Barnett
Você está preocupado com feriados? - James Conigliaro. Não
Adrian Zanescu
9
Votar contra as pessoas que estão tentando ajudar não é uma estratégia vencedora.
Jamie Ide
1
Breve nota sobre a AddBusinessDaysimplementação na pergunta acima (que na verdade foi uma resposta excluída que propus desfazer a exclusão; um mod copiou essa resposta para a pergunta): Na minha opinião, esta solução é melhor do que todas as respostas até agora porque é a única aquele que lida corretamente com valores negativos, sábado e domingo como fonte e não precisa de uma biblioteca de terceiros. (Eu fiz um pequeno programa para testar as diferentes soluções aqui.) Eu adicionaria apenas if (businessDays == 0) return startDate;no início do método para obter o resultado correto para este caso extremo também.
Slauma
1
@AZ .: A primeira exclusão era bem antiga. Após meu pedido para desfazer a exclusão de sua resposta, um mod desfez a exclusão da resposta (por 30 segundos) para copiar o conteúdo abaixo de sua pergunta e, em seguida, excluiu novamente. É por isso que sua resposta tem este carimbo de hora de exclusão recente. Escrevi o comentário acima porque para o meu propósito sua AddBusinessDaysfoi a solução mais geral aqui que funcionou em todos os casos que eu preciso. Copiei para um dos meus projetos atuais (após uma ligeira modificação e tradução para C ++), obrigado pelo código :) Ajudou muito, pois é surpreendentemente difícil acertar todos os casos extremos.
Slauma

Respostas:

134

Última tentativa para sua primeira função:

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

}

A segunda função, GetBusinessDays, pode ser implementada da seguinte forma:

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;
    }
}
Patrick McDonald
fonte
Para o segundo, uma solução é tirar a diferença entre data e data + dias. Isso é bom porque garante que as duas funções serão sincronizadas corretamente e remove a redundância.
Brian
Data atual do feed, executado de 0 a 10 dias úteis, sempre falha na quarta-feira.
Adrian Godong
1
Sim, chegamos lá no final. (Eu digo 'nós' por minha pequena contribuição!) Votação positiva pelo esforço.
Noldorin
Obrigado por sua entrada Noldorin, infelizmente só posso votar positivamente em seus comentários!
Patrick McDonald
3
DateTime.AddDays funciona com números negativos. Isso não segue corretamente o mesmo padrão que usar números negativos com AddBusinessDays permite que dias não úteis sejam selecionados.
Ristogod
63

usando Fluent DateTime :

var now = DateTime.Now;
var dateTime1 = now.AddBusinessDays(3);
var dateTime2 = now.SubtractBusinessDays(5);

o código interno é o seguinte

    /// <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);
    }
Simon
fonte
Esta é a única solução que realmente funcionou para mim quando convertida para VB.Net
Nicholas,
1
OP não solicitou loops, embora este claramente tenha loops. Não há nada de elegante em fazer algo da maneira menos eficiente.
Neolisk,
13

Criei uma extensão que permite adicionar ou subtrair dias úteis. Use um número negativo de businessDays para subtrair. Acho que é uma solução bastante elegante. Parece funcionar em todos os casos.

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

Exemplo:

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
fonte
O resultado é questionável se a data de origem for um sábado ou domingo. Por exemplo: sábado + 1 dia útil resulta na terça, onde prefiro esperar segunda-feira.
Slauma de
3
@Slauma: É assim que a maioria das empresas no Canadá opera. +1 dia útil = "próximo dia útil", que no caso de sábado é terça-feira. Segunda-feira seria "mesmo dia útil".
Neolisk,
3
O Programa @Slauma funciona conforme o esperado. Pense nisso logicamente. Se algo relacionado a negócios começar no sábado e você tiver que permitir 1 dia útil para as pessoas reagirem durante o período desse dia útil, faria sentido dizer a elas que isso deve ser feito na segunda-feira ?!
Riegardt Steyn
8

Para mim, eu precisava ter uma solução que pulasse os fins de semana e fosse negativa ou positiva. Meu critério era que se fosse para a frente e pousasse em um fim de semana, teria que avançar para segunda-feira. Se fosse voltar e pousar em um fim de semana, teria que pular para sexta-feira.

Por exemplo:

  • Quarta-feira - 3 dias úteis = última sexta-feira
  • Quarta-feira + 3 dias úteis = segunda-feira
  • Sexta-feira - 7 dias úteis = última quarta-feira
  • Terça-feira - 5 dias úteis = última terça-feira

Bem, você entendeu;)

Acabei escrevendo esta aula de extensão

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

Ele usa esse método que achei útil em outro lugar ...

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

Se você não quiser se preocupar com isso, você pode simplesmente substituir if (MyClass.IsBusinessDay(date)) seif ((date.DayOfWeek != DayOfWeek.Saturday) && (date.DayOfWeek != DayOfWeek.Sunday))

Então agora você pode fazer

var myDate = DateTime.Now.AddBusinessDays(-3);

ou

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

Aqui estão os resultados de alguns testes:

Resultado Esperado do Teste
Quarta -4 dias úteis Quinta Quinta
Quarta -3 dias úteis Sexta Sexta
Quarta-feira +3 dias úteis segunda-feira
Sexta-feira -7 dias úteis quarta-feira quarta-feira
Terça -5 dias úteis Terça Terça
Sexta-feira + 1 dias úteis segunda-feira
Sábado + 1 dias úteis segunda-feira
Domingo -1 dias úteis sexta sexta
Segunda-feira -1 dias úteis sexta-feira
Segunda-feira + 1 dias úteis terça-feira
Segunda-feira +0 dias úteis segunda-feira
Hugo Yates
fonte
Eu fiz do segundo método um método de extensão também: public static bool IsBusinessDay (esta data DateTime)
Andy B
2
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);
}
LukeH
fonte
1

Estou atrasado para a resposta, mas fiz uma pequena biblioteca com todas as customizações necessárias para fazer operações simples em dias úteis ... Deixo aqui: Working Days Management

Desossado
fonte
2
Infelizmente, este é um GNU licenciado, então é um "veneno legal" para qualquer aplicativo comercial. Alguma chance de você relaxar para "MIT" ou "Apache"?
Tony O'Hagan
Algumas listas estáticas provavelmente devem ser arrays (em vez de listas vinculadas).
Tony O'Hagan
1
Acabei de mudar a licença para MIT (não quero bloquear nada em algo tão simples). Vou analisar sua outra proposta.
Desossado
Bom, seria interessante ver a gestão dos dias úteis por país, pois alguns países podem ter outros dias úteis além de segunda a sexta-feira.
serializador
1

A única solução real é fazer com que essas chamadas acessem uma tabela de banco de dados que define o calendário do seu negócio. Você poderia codificá-lo para uma semana de trabalho de segunda a sexta-feira sem muita dificuldade, mas lidar com os feriados seria um desafio.

Editado para adicionar solução parcial não elegante e não testada:

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

Além disso, violei o requisito de sem loops.

Jamie Ide
fonte
Não acho que o caso do sábado jamais seria atingido.
CoderDennis
@Dennis - seria se a data passada fosse um sábado.
Jamie Ide
Tomei a liberdade de editar seu código para fazê-lo funcionar. Teste o código antes de postá-lo na próxima vez, obrigado.
bytecode77
E eu pensei que o zero voto positivo falava por si. Obrigado!
Jamie Ide
1

Estou ressuscitando este post porque hoje tive que encontrar uma maneira de excluir não só os dias de semana de sábado e domingo, mas também feriados. Mais especificamente, eu precisava lidar com vários conjuntos de feriados possíveis, incluindo:

  • feriados invariantes por país (pelo menos para os países ocidentais - como janeiro, 01).
  • feriados calculados (como a Páscoa e a segunda-feira da Páscoa).
  • feriados específicos de cada país (como o dia da libertação italiana ou o ID4 dos Estados Unidos).
  • feriados específicos da cidade (como o Dia do São Padroeiro em Roma).
  • qualquer outro feriado personalizado (como "amanhã nosso escritório estará fechado").

Eventualmente, eu vim com o seguinte conjunto de classes auxiliares / extensões: embora eles não sejam ostensivamente elegantes, já que fazem um uso massivo de loops ineficientes, eles são decentes o suficiente para resolver meus problemas para sempre. Estou descartando todo o código-fonte aqui neste post, esperando que seja útil para outra pessoa também.

Código fonte

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

Informação de uso

O código é bastante autoexplicativo, no entanto, aqui estão alguns exemplos para explicar como você pode usá-lo.

Adicione 10 dias úteis (ignorando apenas os dias da semana de sábado e domingo)

var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10);

Adicione 10 dias úteis (ignorando sábado, domingo e todos os feriados invariáveis ​​do país para 2019)

var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019));

Adicione 10 dias úteis (pulando sábado, domingo e todos os feriados italianos em 2019)

var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT"));

Adicione 10 dias úteis (pulando sábado, domingo, todos os feriados italianos e feriados específicos de Roma para 2019)

var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT", "Rome"));

As funções acima e os exemplos de código são explicados com mais detalhes nesta postagem do meu blog.

Darkseal
fonte
0
    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;
    }
Alex
fonte
no futuro, adicione um pouco mais de contexto para a resposta e talvez por que você colocou o que tem :)
dax
0

Eu queria um "AddBusinessDays" que suportasse números negativos de dias para adicionar e acabei com o seguinte:

// 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
}

Não é necessário fazer um loop, então deve ser razoavelmente rápido, mesmo para adições "grandes".

Funciona com dias expressos como um número de dias corridos desde a época, desde que isso seja exposto pela nova classe JDK8 LocalDate e eu estava trabalhando em Java. Deve ser simples de se adaptar a outras configurações.

As propriedades fundamentais são que addDayssempre retorna um dia da semana, e que para todos de n,daysBetween(d, addDays(d, n)) == n

Observe que, teoricamente falando, adicionar 0 dias e subtrair 0 dias deve ser uma operação diferente (se sua data for um domingo, adicionar 0 dias deve levar você para segunda-feira e subtrair 0 dias deve levar você para sexta-feira). Como não existe 0 negativo (fora do ponto flutuante!), Optei por interpretar um argumento n = 0 como significando adicionar zero dias.

Max Bolingbroke
fonte
0

Eu acredito que esta poderia ser uma maneira mais simples de 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;
    }
Carlos.Cândido
fonte
0

Aqui está o meu código com a data de partida e a data de entrega no cliente.

            // 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;

Boa codificação.

JanBorup
fonte
0
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
fonte
0
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();
    
            }
Kokul Jose
fonte
-1

Espero que isso ajude alguém.

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;
    }
user2686690
fonte
2
Isso está incorreto. Na maioria dos casos, não funcionará. É improvável que ajude alguém.
Neolisk,