134 votes

Calculez le nombre de jours ouvrables entre deux dates ?

En C#, comment puis-je calculer le nombre de entreprise (ou jours de semaine) entre deux dates ?

1 votes

Voici un exemple en C# qui s'étend également à l'obtention d'heures. codeproject.com/KB/datetime/CalculatingBusinessHours.aspx

1 votes

Jetez un coup d'œil à infopathdev.com/forums/t/7156.aspx

4 votes

Vous allez donc exclure les vacances ?

143voto

Alexander Points 650

J'ai déjà été confronté à une telle tâche et j'ai la solution. J'éviterais d'énumérer tous les jours intermédiaires lorsque c'est évitable, ce qui est le cas ici. Je ne parle même pas de créer un tas d'instances de DateTime, comme je l'ai vu dans une des réponses ci-dessus. C'est vraiment un gaspillage de puissance de traitement. Surtout dans la situation réelle, lorsque vous devez examiner des intervalles de temps de plusieurs mois. Voir mon code, avec des commentaires, ci-dessous.

    /// <summary>
    /// Calculates number of business days, taking into account:
    ///  - weekends (Saturdays and Sundays)
    ///  - bank holidays in the middle of the week
    /// </summary>
    /// <param name="firstDay">First day in the time interval</param>
    /// <param name="lastDay">Last day in the time interval</param>
    /// <param name="bankHolidays">List of bank holidays excluding weekends</param>
    /// <returns>Number of business days during the 'span'</returns>
    public static int BusinessDaysUntil(this DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays)
    {
        firstDay = firstDay.Date;
        lastDay = lastDay.Date;
        if (firstDay > lastDay)
            throw new ArgumentException("Incorrect last day " + lastDay);

        TimeSpan span = lastDay - firstDay;
        int businessDays = span.Days + 1;
        int fullWeekCount = businessDays / 7;
        // find out if there are weekends during the time exceedng the full weeks
        if (businessDays > fullWeekCount*7)
        {
            // we are here to find out if there is a 1-day or 2-days weekend
            // in the time interval remaining after subtracting the complete weeks
            int firstDayOfWeek = (int) firstDay.DayOfWeek;
            int lastDayOfWeek = (int) lastDay.DayOfWeek;
            if (lastDayOfWeek < firstDayOfWeek)
                lastDayOfWeek += 7;
            if (firstDayOfWeek <= 6)
            {
                if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval
                    businessDays -= 2;
                else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval
                    businessDays -= 1;
            }
            else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval
                businessDays -= 1;
        }

        // subtract the weekends during the full weeks in the interval
        businessDays -= fullWeekCount + fullWeekCount;

        // subtract the number of bank holidays during the time interval
        foreach (DateTime bankHoliday in bankHolidays)
        {
            DateTime bh = bankHoliday.Date;
            if (firstDay <= bh && bh <= lastDay)
                --businessDays;
        }

        return businessDays;
    }

Édité par Slauma, août 2011

Excellente réponse ! Il y a cependant un petit problème. Je prends la liberté d'éditer cette réponse puisque le répondeur est absent depuis 2009.

Le code ci-dessus suppose que DayOfWeek.Sunday a la valeur 7 ce qui n'est pas le cas. La valeur est en fait 0 . Cela conduit à un calcul erroné si par exemple firstDay y lastDay sont tous deux le même dimanche. La méthode renvoie 1 dans ce cas, mais il devrait être 0 .

La correction la plus simple pour ce bug : Remplacer dans le code ci-dessus les lignes où firstDayOfWeek y lastDayOfWeek sont déclarés par ce qui suit :

int firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday 
    ? 7 : (int)firstDay.DayOfWeek;
int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday
    ? 7 : (int)lastDay.DayOfWeek;

Maintenant le résultat est :

  • Du vendredi au vendredi -> 1
  • Du samedi au samedi -> 0
  • Du dimanche au dimanche -> 0
  • Du vendredi au samedi -> 1
  • Du vendredi au dimanche -> 1
  • Du vendredi au lundi -> 2
  • Du samedi au lundi -> 1
  • Dimanche à lundi -> 1
  • Lundi à lundi -> 1

2 votes

Assurez-vous également que les jours fériés sont pris en compte comme suit : if (firstDay <= bh && bh <= lastDay && bh.IsWorkingDay())

8 votes

Merci pour la méthode. Cependant, j'ai dû ajouter ce qui suit à l'énoncé if de la soustraction/itération des jours fériés : && !(bh.DayOfWeek == DayOfWeek.Sunday || bh.DayOfWeek == DayOfWeek.Saturday) sinon il soustrairait deux fois le même jour, si un jour férié tombe sur un week-end.

0 votes

J'ai modifié la dernière boucle pour une instruction Linq : businessDays -= bankHolidays.Select(bankHoliday => bankHoliday.Date).Count(bh => firstDay <= bh && bh <= lastDay) ;

134voto

Alec Pojidaev Points 147

Ok. Je pense qu'il est temps de poster la bonne réponse :

public static double GetBusinessDays(DateTime startD, DateTime endD)
{
    double calcBusinessDays =
        1 + ((endD - startD).TotalDays * 5 -
        (startD.DayOfWeek - endD.DayOfWeek) * 2) / 7;

    if (endD.DayOfWeek == DayOfWeek.Saturday) calcBusinessDays--;
    if (startD.DayOfWeek == DayOfWeek.Sunday) calcBusinessDays--;

    return calcBusinessDays;
}

Source originale :

http://alecpojidaev.wordpress.com/2009/10/29/work-days-calculation-with-c/

11 votes

Bon travail, mais peut-être utiliser les enums DayOfWeek eux-mêmes plutôt que de les convertir en ints ?

3 votes

Sérieusement, c'est la meilleure solution qui existe. Merci Alec

0 votes

@xanadont il suffit d'enlever l'initiale 1+

58voto

Alpha Points 2146

Je sais que cette question est déjà résolue, mais j'ai pensé que je pouvais fournir une réponse plus directe qui pourrait aider d'autres visiteurs à l'avenir.

Voici mon point de vue :

public int GetWorkingDays(DateTime from, DateTime to)
{
    var dayDifference = (int)to.Subtract(from).TotalDays;
    return Enumerable
        .Range(1, dayDifference)
        .Select(x => from.AddDays(x))
        .Count(x => x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday);
}

C'était ma soumission originale :

public int GetWorkingDays(DateTime from, DateTime to)
{
    var totalDays = 0;
    for (var date = from; date < to; date = date.AddDays(1))
    {
        if (date.DayOfWeek != DayOfWeek.Saturday
            && date.DayOfWeek != DayOfWeek.Sunday)
            totalDays++;
    }

    return totalDays;
}

0 votes

"où" pourrait être "compte" pour le raccourcir

1 votes

Beaucoup plus clair, et les solutions énumérées se prêtent à l'élimination des jours fériés. Elles sont cependant beaucoup plus lentes en masse ; dans LINQPad, le calcul des jours ouvrables pour des écarts de 90 jours dans une boucle d'un million d'itérations prend 10s avec cette solution, et seulement 0,2s environ avec la réponse acceptée ou celle, beaucoup plus agréable, d'Alec Pojidaev.

1 votes

Pour être inclusif, le code devrait être : return Enumerable .Range(0, dayDifference + 1) ...

23voto

Qwerty Points 244

Définissez une méthode d'extension sur DateTime comme suit :

public static class DateTimeExtensions
{
    public static bool IsWorkingDay(this DateTime date)
    {
        return date.DayOfWeek != DayOfWeek.Saturday
            && date.DayOfWeek != DayOfWeek.Sunday;
    }
}

Ensuite, utilisez is dans une clause Where pour filtrer une liste plus large de dates :

var allDates = GetDates(); // method which returns a list of dates

// filter dates by working day's  
var countOfWorkDays = allDates
     .Where(day => day.IsWorkingDay())
     .Count() ;

0 votes

Ne serait-il pas préférable d'étendre également l'intervalle de temps afin de pouvoir l'utiliser - puisqu'il a dit qu'il voulait utiliser la distance entre deux dates et non une liste de dates ?

0 votes

La distance entre les deux dates est le nombre de jours qui les séparent, donc la fonction Count() est suffisante.

3 votes

Je ne suis pas sûr que cette réponse soit appropriée... il n'a pas une liste de jours individuels, il a deux dates et il veut trouver le nombre de jours ouvrables entre elles. Pour utiliser cette solution, vous devez fournir les éléments suivants un autre qui produit une liste de toutes les dates entre les deux dates.

13voto

paulslater19 Points 3125

J'ai utilisé le code suivant pour prendre également en compte les jours fériés :

public class WorkingDays
{
    public List<DateTime> GetHolidays()
    {
        var client = new WebClient();
        var json = client.DownloadString("https://www.gov.uk/bank-holidays.json");
        var js = new JavaScriptSerializer();
        var holidays = js.Deserialize <Dictionary<string, Holidays>>(json);
        return holidays["england-and-wales"].events.Select(d => d.date).ToList();
    }

    public int GetWorkingDays(DateTime from, DateTime to)
    {
        var totalDays = 0;
        var holidays = GetHolidays();
        for (var date = from.AddDays(1); date <= to; date = date.AddDays(1))
        {
            if (date.DayOfWeek != DayOfWeek.Saturday
                && date.DayOfWeek != DayOfWeek.Sunday
                && !holidays.Contains(date))
                totalDays++;
        }

        return totalDays;
    }
}

public class Holidays
{
    public string division { get; set; }
    public List<Event> events { get; set; }
}

public class Event
{
    public DateTime date { get; set; }
    public string notes { get; set; }
    public string title { get; set; }
}

Et des tests unitaires :

[TestClass]
public class WorkingDays
{
    [TestMethod]
    public void SameDayIsZero()
    {
        var service = new WorkingDays();

        var from = new DateTime(2013, 8, 12);

        Assert.AreEqual(0, service.GetWorkingDays(from, from));

    }

    [TestMethod]
    public void CalculateDaysInWorkingWeek()
    {
        var service = new WorkingDays();

        var from = new DateTime(2013, 8, 12);
        var to = new DateTime(2013, 8, 16);

        Assert.AreEqual(4, service.GetWorkingDays(from, to), "Mon - Fri = 4");

        Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Mon - Tues = 1");
    }

    [TestMethod]
    public void NotIncludeWeekends()
    {
        var service = new WorkingDays();

        var from = new DateTime(2013, 8, 9);
        var to = new DateTime(2013, 8, 16);

        Assert.AreEqual(5, service.GetWorkingDays(from, to), "Fri - Fri = 5");

        Assert.AreEqual(2, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Fri - Tues = 2");
        Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 12)), "Fri - Mon = 1");
    }

    [TestMethod]
    public void AccountForHolidays()
    {
        var service = new WorkingDays();

        var from = new DateTime(2013, 8, 23);

        Assert.AreEqual(0, service.GetWorkingDays(from, new DateTime(2013, 8, 26)), "Fri - Mon = 0");

        Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 27)), "Fri - Tues = 1");
    }
}

0 votes

Pourquoi commence-t-on à compter en ajoutant 1 jour à "from" ? @ for (var date = from.AddDays(1) ; date <= to ; date = date.AddDays(1)) ?

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