AddBusinessDays y GetBusinessDays

93

Necesito encontrar 2 implementaciones completas elegantes 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) preferible (sin bucles).

EDITAR: Por días hábiles me refiero a días hábiles (lunes, martes, miércoles, jueves, viernes). No festivos, solo fines de semana excluidos.

Ya tengo algunas soluciones desagradables que parecen funcionar, pero me pregunto si hay formas elegantes de hacerlo. Gracias


Esto es lo que he escrito hasta ahora. Funciona en todos los casos y también hace negativos. Todavía se necesita una implementación de 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
fuente
14
¿Existen soluciones elegantes cuando se trata de algo tan ilógico como las fechas?
Wyatt Barnett
¿Estás preocupado por las vacaciones? - James Conigliaro. No
Adrian Zanescu
9
Votar en contra de las personas que intentan ayudar no es una estrategia ganadora.
Jamie Ide
1
Breve nota sobre la AddBusinessDaysimplementación en la pregunta anterior (que en realidad era una respuesta eliminada que propuse recuperar; un mod copió esa respuesta a la pregunta en su lugar): En mi opinión, esta solución es mejor que todas las respuestas hasta ahora porque es la única uno que trate correctamente con valores negativos, sábado y domingo como fuente y no necesite una biblioteca de terceros. (He creado un pequeño programa para probar las diferentes soluciones aquí). Solo agregaría if (businessDays == 0) return startDate;al comienzo del método para obtener el resultado correcto también para este caso de borde.
Slauma
1
@AZ .: La primera eliminación fue bastante antigua. Después de mi solicitud para recuperar su respuesta, un mod había recuperado la respuesta (durante 30 segundos) para copiar el contenido debajo de su pregunta y luego la eliminó nuevamente. Es por eso que su respuesta tiene esta marca de tiempo de eliminación reciente. Escribí el comentario anterior porque para mi propósito, tu AddBusinessDaysfue la solución más general aquí que funcionó en todos los casos que necesito. Lo he copiado en uno de mis proyectos actuales (después de una ligera modificación y traducción a C ++), gracias por el código :) Me ayudó mucho, ya que es sorprendentemente difícil acertar en todos los casos extremos.
Slauma

Respuestas:

134

Último intento de su primera función:

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

}

La segunda función, GetBusinessDays, se puede implementar de la siguiente manera:

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
fuente
Para el segundo, una solución es tomar la diferencia entre fecha y fecha + días. Esto es bueno porque garantiza que las dos funciones se sincronizarán correctamente y elimina la redundancia.
Brian
Alimente la fecha actual, se ejecuta de 0 a 10 días hábiles, siempre falla el miércoles.
Adrian Godong
1
Sí, llegamos allí al final. (¡Digo "nosotros" por mi pequeña contribución!)
Noldorin
Gracias por tu aporte Noldorin, solo puedo votar tus comentarios por desgracia.
Patrick McDonald
3
DateTime.AddDays funciona con números negativos. Esto no sigue correctamente el mismo patrón que el uso de números negativos con AddBusinessDays que permite seleccionar días no hábiles.
Ristogod
63

usando Fluent DateTime :

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

el código interno es el siguiente

    /// <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);
    }
Simón
fuente
Esta es la única solución que realmente funcionó para mí cuando se convirtió a VB.Net
Nicholas
1
OP no solicitó bucles, mientras que este claramente tiene bucles. No hay nada elegante en hacer algo de la manera menos eficiente.
Neolisk
13

Creé una extensión que te permite sumar o restar días laborales. Utilice un número negativo de BusinessDays para restar. Creo que es una solución bastante elegante. Parece funcionar en todos los 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);
        }
    }
}

Ejemplo:

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
fuente
El resultado es cuestionable si la fecha de origen es sábado o domingo. Por ejemplo: sábado + 1 día laborable da como resultado martes, donde prefiero esperar el lunes.
Slauma
3
@Slauma: Así es como operan la mayoría de las empresas en Canadá. +1 día hábil = "siguiente día hábil", que en el caso de sábado es martes. El lunes sería "el mismo día hábil".
Neolisk
3
El programa @Slauma funciona según lo previsto. Piense en ello con lógica. Si algo relacionado con los negocios comienza el sábado y tiene que permitir 1 día hábil para que las personas reaccionen durante el lapso de dicho día hábil, ¿tendría sentido decirles que debe hacerse antes del lunes?
Riegardt Steyn
8

Para mí, tenía que tener una solución que saltara los fines de semana y diera positivo o negativo. Mi criterio era que si avanzaba y aterrizaba en un fin de semana, tendría que avanzar al lunes. Si regresaba y aterrizaba un fin de semana, tendría que saltar al viernes.

Por ejemplo:

  • Miércoles - 3 días laborales = último viernes
  • Miércoles + 3 días laborales = Lunes
  • Viernes - 7 días laborales = último miércoles
  • Martes - 5 días hábiles = último martes

Bueno, ya captas la idea ;)

Terminé escribiendo esta clase de extensión

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

Utiliza este método que pensé que sería útil para usar en otros lugares ...

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

Si no quiere molestarse con eso, simplemente puede reemplazarlo if (MyClass.IsBusinessDay(date))con ifif ((date.DayOfWeek != DayOfWeek.Saturday) && (date.DayOfWeek != DayOfWeek.Sunday))

Entonces ahora puedes hacer

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

o

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

Estos son los resultados de algunas pruebas:

Resultado esperado de la prueba
Miércoles -4 días laborales Jueves Jueves
Miércoles -3 días hábiles Viernes Viernes
Miércoles +3 días laborales Lunes Lunes
Viernes -7 días laborales Miércoles Miércoles
Martes -5 días hábiles Martes Martes
Viernes +1 días hábiles Lunes Lunes
Sábado +1 días hábiles Lunes Lunes
Domingo -1 día laboral Viernes Viernes
Lunes -1 días hábiles Viernes Viernes
Lunes +1 días hábiles Martes Martes
Lunes +0 días laborales Lunes Lunes
Hugo Yates
fuente
Hice el segundo método como un método de extensión también: public static bool IsBusinessDay (esta fecha 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
fuente
1

Llego tarde a la respuesta, pero hice una pequeña biblioteca con toda la personalización necesaria para realizar operaciones sencillas en días laborables ... Lo dejo aquí: Gestión de Días Laborables

Sin espinas
fuente
2
Desafortunadamente, esto tiene licencia GNU, por lo que es un "veneno legal" para cualquier aplicación comercial. ¿Alguna posibilidad de que relajes esto con "MIT" o "Apache"?
Tony O'Hagan
Algunas listas estáticas probablemente deberían ser matrices (en lugar de listas enlazadas).
Tony O'Hagan
1
Acabo de cambiar la licencia a MIT (no quiero bloquear nada en algo tan simple). Examinaré tu otra propuesta.
Boneless
Bien, sería interesante ver la gestión de los días laborables por país, ya que algunos países pueden tener otros días laborables que no sean de lunes a viernes.
serializador
1

La única solución real es que esas llamadas accedan a una tabla de base de datos que define el calendario de su empresa. Puede codificarlo para una semana laboral de lunes a viernes sin demasiada dificultad, pero manejar los días festivos sería un desafío.

Editado para agregar una solución parcial no elegante y no probada:

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

También violé el requisito de no bucles.

Jamie Ide
fuente
No creo que el caso de los sábados se lleve a cabo jamás.
CoderDennis
@Dennis: lo haría si la fecha pasada fuera un sábado.
Jamie Ide
Me tomé la libertad de editar su código para que funcione. Pruebe el código antes de publicarlo la próxima vez, gracias.
bytecode77
Y pensé que los votos positivos cero hablaban por sí mismos. ¡Gracias!
Jamie Ide
1

Estoy resucitando esta publicación porque hoy tuve que encontrar una manera de excluir no solo los sábados y domingos, sino también los feriados. Más específicamente, necesitaba manejar varios conjuntos de posibles vacaciones, que incluyen:

  • días festivos invariables del país (al menos para los países occidentales, como enero, 01).
  • días festivos calculados (como Pascua y Lunes de Pascua).
  • días festivos específicos del país (como el día de la liberación de Italia o el ID4 de Estados Unidos).
  • días festivos específicos de la ciudad (como el Día del Patrón de Roma).
  • cualquier otro día festivo personalizado (como "mañana nuestra oficina estará cerrada").

Finalmente, obtuve el siguiente conjunto de clases de ayuda / extensiones: aunque no son descaradamente elegantes, ya que hacen un uso masivo de bucles ineficientes, son lo suficientemente decentes como para resolver mis problemas para siempre. Dejo todo el código fuente aquí en esta publicación, esperando que también sea útil para otra persona.

Código fuente

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

Información de uso

El código es bastante autoexplicativo, sin embargo, aquí hay un par de ejemplos para explicar cómo se puede usar.

Agregue 10 días hábiles (omitiendo solo los sábados y domingos entre semana)

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

Agregue 10 días hábiles (omitiendo los sábados, domingos y todos los días festivos invariables del país para 2019)

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

Agregue 10 días hábiles (omitiendo los sábados, domingos y todos los días festivos italianos para 2019)

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

Agregue 10 días hábiles (omitiendo los sábados, domingos, todos los feriados italianos y los feriados específicos de Roma para 2019)

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

Las funciones anteriores y los ejemplos de código se explican con más detalle en esta publicación de mi blog.

Darkseal
fuente
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
fuente
en un futuro añadir un poco más de contexto para la respuesta y tal vez por eso que has puesto lo que tiene :)
Dax
0

Quería un "AddBusinessDays" que admitiera números negativos de días para agregar, y terminé con esto:

// 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 se requiere bucle, por lo que debería ser razonablemente rápido incluso para adiciones "grandes".

Funciona con días expresados ​​como un número de días calendario desde la época, ya que está expuesto por la nueva clase JDK8 LocalDate y yo estaba trabajando en Java. Sin embargo, debería ser fácil de adaptar a otros entornos.

Las propiedades fundamentales son que addDayssiempre devuelve un día de la semana, y que para todos dy n,daysBetween(d, addDays(d, n)) == n

Tenga en cuenta que, en teoría, sumar 0 días y restar 0 días deberían ser operaciones diferentes (si su fecha es un domingo, la suma de 0 días debería llevarlo al lunes y la resta de 0 días debería llevarlo al viernes). Dado que no existe el 0 negativo (¡fuera del punto flotante!), He optado por interpretar un argumento n = 0 en el sentido de agregar cero días.

Max Bolingbroke
fuente
0

Creo que esta podría ser una forma más sencilla 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
fuente
0

Aquí está mi código con la fecha de salida y la fecha de entrega al 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;

Codificación feliz.

JanBorup
fuente
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
fuente
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 José
fuente
-1

Espero que esto ayude a alguien.

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;
    }
usuario2686690
fuente
2
Esto es incorrecto. En la mayoría de los casos, no funcionará. Es poco probable que ayude a nadie.
Neolisk