49 votes

c# DateTime pour ajouter/soustraire des jours ouvrables

J'ai un scénario où, étant donné une date( DateTime ), cette date plus/moins x jours (obtenus avec DateTime.AddDays ) doit ajouter ou soustraire x jours ouvrables, c'est-à-dire qu'il ne tient pas compte des week-ends et des jours fériés. Comment puis-je faire en sorte qu'il le fasse ? Dois-je mettre en place ma propre version et l'attacher à un calendrier ou autre ?

3 votes

Comment savoir quels jours sont des jours fériés ?

0 votes

Vous pouvez étendre la méthode indiquée par @Taz pour inclure les vacances.

71voto

Kev Points 60744

Sur la base de Taz's lien :

public static class DateTimeExtensions
{
  public static DateTime AddWorkDays(this DateTime date, int workingDays)
  {
    int direction = workingDays < 0 ? -1 : 1;
    DateTime newDate = date;
    while (workingDays != 0)
    {
      newDate = newDate.AddDays(direction);
      if (newDate.DayOfWeek != DayOfWeek.Saturday && 
          newDate.DayOfWeek != DayOfWeek.Sunday && 
          !newDate.IsHoliday())
      {
        workingDays -= direction;
      }
    }
    return newDate;
  }

  public static bool IsHoliday(this DateTime date)
  {
    // You'd load/cache from a DB or file somewhere rather than hardcode
    DateTime[] holidays = 
    new DateTime[] { 
      new DateTime(2010,12,27),
      new DateTime(2010,12,28),
      new DateTime(2011,01,03),
      new DateTime(2011,01,12),
      new DateTime(2011,01,13)
    };

    return holidays.Contains(date.Date);
  }
}

69voto

sprinter252 Points 1215

Je vous suggère de l'implémenter vous-même, et de le faire dans une méthode d'extension comme celle-ci :

public static class DateTimeExtensions
{
    public static DateTime AddWorkdays(this DateTime originalDate, int workDays)
    {
        DateTime tmpDate = originalDate;
        while (workDays > 0)
        {
            tmpDate = tmpDate.AddDays(1);
            if (tmpDate.DayOfWeek < DayOfWeek.Saturday && 
                tmpDate.DayOfWeek > DayOfWeek.Sunday &&
                !tmpDate.IsHoliday())
                workDays--;
        }
        return tmpDate;
    }

    public static bool IsHoliday(this DateTime originalDate)
    {
        // INSERT YOUR HOlIDAY-CODE HERE!
        return false;
    }
}

7 votes

Ce code lance un ArgumentOutOfRangeException exception. Ne gère pas non plus les jours négatifs.

4 votes

Oui, je le sais. Ce code ne montre que le concept grossier et doit être implémenté de manière plus sophistiquée. Je pense qu'il y a des librairies prêtes à l'emploi sur codeplex.

10voto

Rikalous Points 2996

Je l'ai fait récemment :

private DateTime CalculateFutureDate(DateTime fromDate, int numberofWorkDays, ICollection<DateTime> holidays)
{
    var futureDate = fromDate;
    var daterange = Enumerable.Range(1, numberofWorkDays * 2);
    var dateSet = daterange.Select (d => futureDate.AddDays(d));
    var dateSetElim = dateSet.Except(holidays).Except(dateSet.Where( s =>s.DayOfWeek == DayOfWeek.Sunday).Except(dateSet.Where  (s=>s.DayOfWeek==DayOfWeek.Saturday) ));

    //zero-based array
    futureDate = dateSetElim.ElementAt(numberofWorkDays-1);
    return futureDate;
}

Vous devrez vérifier le comportement des jours négatifs, je ne les ajoute jamais !

2voto

Dan B Points 402

J'ai mis du temps à trouver une solution... J'ai créé une table DB que je tire dans un tableau avec mes jours de vacances. Tous les crédits à Kev (sur ce post) .. J'ai dû modifier le sien pour qu'il fonctionne tel quel pour moi.

Dans mon cas, si le premier jour était un samedi, et que mon workingDayCount = -1, je veux repasser le jeudi (car ma date ne peut pas tomber un week-end ou un jour férié... il faut que ce soit un jour ouvrable... dans ce cas le vendredi).

Le code de Kev pourrait renvoyer un dimanche ... le code ci-dessous le ramènera au jour ouvrable précédent (généralement un vendredi - sauf si le vendredi est un jour férié, auquel cas il renverrait au jeudi).

public static DateTime AddWorkDays(this DateTime date, int workingDays, params Holidays[] bankHolidays)
    {
        int direction = workingDays < 0 ? -1 : 1;
        DateTime newDate = date;
        // If a working day count of Zero is passed, return the date passed
        if (workingDays == 0)
        {

            newDate = date;
        }
        else
        {
            while (workingDays != -direction)
            {
                if (newDate.DayOfWeek != DayOfWeek.Saturday &&
                    newDate.DayOfWeek != DayOfWeek.Sunday &&
                    Array.IndexOf(bankHolidays, newDate) < 0)
                {
                    workingDays -= direction;
                }
                // if the original return date falls on a weekend or holiday, this will take it to the previous / next workday, but the "if" statement keeps it from going a day too far.

                if (workingDays != -direction)
                { newDate = newDate.AddDays(direction); }
            }
        }
        return newDate;
    }

Voici ma classe de vacances simple :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Clarity.Utilities
{
    public class Holidays 
    {
        public int Id { get; set; }
        public DateTime dtHoliday  { get; set; }
        public string Desc { get; set; }
        public bool Active { get; set; }

    }
}

Voici comment remplir le tableau :

private Holidays[] PopulateArrayWithDates()
    {
        SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["DBConn"].ConnectionString);
        DateTime[] dtHolidays = new DateTime[] { };
        string sql = @"SELECT HolDate, HolName FROM [Server].DBName.dbo.tblHolidays";
        SqlCommand ADDCmd = new SqlCommand(sql, con);
        DataTable table = new DataTable();
        DataTable tbl = new DataTable();
        Utilities.Holidays[] allRecords = null;

        using (var command = new SqlCommand(sql, con))
        {
            con.Open();
            using (var reader = command.ExecuteReader())
            {
                var list = new List<Holidays>();
                while (reader.Read())
                    list.Add(new Holidays { dtHoliday = reader.GetDateTime(0), Desc = reader.GetString(1) });
                allRecords = list.ToArray();
            }
        }
        return allRecords;
    }

0voto

Nickolay Olshevsky Points 8155

Vous devez vérifier vous-même si le jour en question fonctionne, car la classe DateTime ne peut pas savoir quels jours seront fériés cette année :)

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