En C#, comment puis-je calculer le nombre de entreprise (ou jours de semaine) entre deux dates ?
Assurez-vous également que les jours fériés sont pris en compte comme suit : if (firstDay <= bh && bh <= lastDay && bh.IsWorkingDay())
En C#, comment puis-je calculer le nombre de entreprise (ou jours de semaine) entre deux dates ?
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 :
Assurez-vous également que les jours fériés sont pris en compte comme suit : if (firstDay <= bh && bh <= lastDay && bh.IsWorkingDay())
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.
J'ai modifié la dernière boucle pour une instruction Linq : businessDays -= bankHolidays.Select(bankHoliday => bankHoliday.Date).Count(bh => firstDay <= bh && bh <= lastDay) ;
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/
Bon travail, mais peut-être utiliser les enums DayOfWeek eux-mêmes plutôt que de les convertir en ints ?
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;
}
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.
Pour être inclusif, le code devrait être : return Enumerable .Range(0, dayDifference + 1) ...
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() ;
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 ?
La distance entre les deux dates est le nombre de jours qui les séparent, donc la fonction Count() est suffisante.
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.
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");
}
}
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.
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 ?