99 votes

AddBusinessDays et GetBusinessDays

Je dois trouver 2 implémentations complètes et élégantes 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) préférable (pas de boucles).

EDIT : Par jours ouvrables, j'entends les jours de travail (lundi, mardi, mercredi, jeudi, vendredi). Pas de jours fériés, sauf les week-ends.

J'ai déjà des solutions peu élégantes qui semblent fonctionner, mais je me demande s'il existe des moyens élégants de le faire. Merci


Voici ce que j'ai écrit jusqu'à présent. Il fonctionne dans tous les cas et fait aussi les négatifs. J'ai toujours besoin d'une implémentation 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);
}

14 votes

Existe-t-il des solutions élégantes lorsqu'il s'agit de quelque chose d'aussi illogique que les dates ?

0 votes

Êtes-vous préoccupé par les vacances ? - James Conigliaro . Non

0 votes

Avec l'internationalisation, c'est difficile. Comme mentionné dans d'autres fils de discussion ici sur le FOS, les jours fériés diffèrent certainement d'un pays à l'autre et même d'une province à l'autre. La plupart des gouvernements ne programment pas leurs vacances plus de cinq ans environ.

138voto

Patrick McDonald Points 20645

Dernière tentative pour votre première fonction :

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 deuxième fonction, GetBusinessDays, peut être mise en œuvre comme suit :

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

0 votes

Pour la seconde, une solution consiste à prendre la différence entre la date et la date+jours. Cette solution est intéressante dans la mesure où elle garantit que les deux fonctions se synchroniseront correctement et où elle supprime la redondance.

0 votes

Alimenter la date actuelle, fonctionner pendant 0 à 10 jours ouvrables, il échoue toujours le mercredi.

1 votes

Oui, on y est arrivé à la fin. (Je dis "nous" pour ma minuscule contribution !) Votez pour l'effort.

66voto

Simon Points 11945

En utilisant DateTime fluide :

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

Le code interne est le suivant

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

0 votes

C'est la seule solution qui a fonctionné pour moi lors de la conversion en VB.Net.

1 votes

L'OP a demandé qu'il n'y ait pas de boucles, alors que celui-ci en a clairement. Il n'y a rien d'élégant à faire quelque chose de la manière la moins efficace possible.

16voto

Arjen Points 96

J'ai créé une extension qui vous permet d'ajouter ou de soustraire des jours ouvrables. Utilisez un nombre négatif de businessDays pour soustraire. Je pense que c'est une solution assez élégante. Elle semble fonctionner dans tous les cas.

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

Exemple :

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

0 votes

Le résultat est douteux si la date source est un samedi ou un dimanche. Par exemple : Samedi + 1 jour ouvrable donne comme résultat mardi, alors que je m'attendrais plutôt à lundi.

3 votes

@Slauma : C'est ainsi que fonctionnent la plupart des entreprises au Canada. +1 jour ouvrable = "jour ouvrable suivant", qui dans le cas du samedi est le mardi. Le lundi serait le "même jour ouvrable".

4 votes

Le programme @Slauma fonctionne comme prévu. Pensez-y logiquement. Si quelque chose de professionnel démarre le samedi et que vous devez laisser un jour ouvrable aux gens pour qu'ils réagissent pendant ce jour ouvrable, serait-il logique de leur dire que cela doit être fait avant le lundi ?

2voto

Ash Machine Points 2491

Avec l'internationalisation, c'est difficile. Comme mentionné dans d'autres fils de discussion ici sur SOF, les jours fériés diffèrent certainement d'un pays à l'autre et même d'une province à l'autre. La plupart des gouvernements ne programment pas leurs vacances plus de cinq ans ou plus.

2voto

LukeH Points 110965
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);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X