web-dev-qa-db-fra.com

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 (atteinte avec DateTime.AddDays) doit ajouter ou soustraire x jours ouvrables, c'est-à-dire ignorer les week-ends et les jours fériés. Comment puis-je le faire faire cela? Dois-je implémenter ma propre version et la joindre à un calendrier ou quelque chose?

45
Aks

Je suggérerais que vous deviez l'implémenter par vous-même, et le feriez 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;
    }
}
56
Alexander Schmidt

Basé sur Taz'slink :

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

Je l'ai fait récemment avec un peu de LINQ:

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

Il m'a fallu un peu de temps pour le résoudre ... J'ai créé une table DB que je tire dans un tableau avec mes jours de vacances . All Credit to Kev (on this post) .. J'ai dû modifier son travail pour qu'il soit comme moi.

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

Le code de Kev pourrait revenir un dimanche ... le code ci-dessous le ramènera à la journée de travail précédente (généralement un vendredi - à moins que le vendredi soit un jour férié, quand il repasserait 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 mon cours 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;
    }
2
Dan B

Je cherchais à ajouter et à soustraire des dates en évitant les week-ends et c'est la première entrée dans Google. Je n'ai aimé aucune des réponses ici, donc j'ajouterai celle que j'ai faite moi-même au cas où quelqu'un finirait ici comme moi:

public static DateTime AddExcludingWeekends(this DateTime dateTime, int nDays)
{
  var wholeWeeks = nDays / 5; //since nDays does not include weekdays every week is considered as 5 days
  var absDays = Math.Abs(nDays);
  var remaining = absDays % 5; //results in the number remaining days to add or substract excluding the whole weeks
  var direction = nDays / absDays;//results in 1 if nDays is posisive or -1 if it's negative
  while (dateTime.DayOfWeek == DayOfWeek.Saturday || dateTime.DayOfWeek == DayOfWeek.Sunday)
    dateTime = dateTime.AddDays(direction); //If we are already in a weekend, get out of it
  while (remaining-- > 0)
  {//add remaining days...
    dateTime = dateTime.AddDays(direction);
    if (dateTime.DayOfWeek == DayOfWeek.Saturday)
      dateTime = dateTime.AddDays(direction * 2);//...skipping weekends
  }
  return dateTime.AddDays(wholeWeeks * 7); //Finally add the whole weeks as 7 days, thus skipping the weekends without checking for DayOfWeek
}

J'espère que cela aide quelqu'un.

1
Naburi

J'ai modifié les réponses précédentes à des approches plus fonctionnelles. J'ai fourni deux solutions ci-dessous, l'une utilisant IEnumerable et l'autre utilisant les extensions IObservable et Reactive

Utilisation d'IObservable

public static class DateTimeExtensions
{
    public static DateTime AddWorkDays(this DateTime date, int workingDays)
    {
        return Observable
            .Generate
                (date, arg => true, arg => arg.AddDays(workingDays < 0 ? -1 : 1), arg => arg)
            .Where(newDate =>
                (newDate.DayOfWeek != DayOfWeek.Saturday &&
                 newDate.DayOfWeek != DayOfWeek.Sunday &&
                 !newDate.IsHoliday()))
            .Take(Math.Abs(workingDays) + 1)
            .LastAsync()
            .Wait();
    }

    public static bool IsHoliday(this DateTime date)
    {
        return false;
    }
}

Utiliser IEnumerable

public static class DateTimeExtensions
{
    public static DateTime AddWorkDays(this DateTime date, int workingDays)
    {
        return date.GetDates(workingDays < 0)
            .Where(newDate =>
                (newDate.DayOfWeek != DayOfWeek.Saturday &&
                 newDate.DayOfWeek != DayOfWeek.Sunday &&
                 !newDate.IsHoliday()))
            .Take(Math.Abs(workingDays))
            .Last();
    }

    private static IEnumerable<DateTime> GetDates(this DateTime date, bool isForward)
    {
        while (true)
        {
            date = date.AddDays(isForward ? -1 : 1);
            yield return date;
        }
    } 

    public static bool IsHoliday(this DateTime date)
    {
        return false;
    }
}

Vous devriez vérifier si la journée fonctionne par vous-même, car la classe DateTime ne peut pas savoir quels jours seront des vacances cette année :)

0
Nickolay Olshevsky

Vous devriez probablement avoir une base de données de jours fériés à comparer et si la valeur de jour plus/moins x est égale à une valeur dans la base de données, ajoutez/soustrayez-en une autre, car tout le monde n'a pas les mêmes jours fériés.

0
Brett McCann

une solution simple est là Essayez-le et profitez du codage

public class Program
{
    public static void Main(string[] args)
    {
        Double days= 7;
        string s=DateTime.Now.AddDays(7).ToString("dd/MM/yyyy");  
        DateTime dt= OrderDeliveryDate(days);
        Console.WriteLine("dt"+dt.ToString("dd/MM/yyyy"));


    }

    public static DateTime OrderDeliveryDate(Double days)
    {

        Double count=0;
        for(int i=0;i<days;i++)
        {
            if(DateTime.Now.AddDays(i).DayOfWeek.ToString() == "Saturday")
            {
               count= count+1;
            }
            else if(DateTime.Now.AddDays(i).DayOfWeek.ToString() == "Sunday")
            {
                count=count+1;
            }

        }
        days=days+count;
        return DateTime.Now.AddDays(days);
    }
}
0
Hardik Dudhaiya