Ich hatte schon einmal eine solche Aufgabe und habe die Lösung. Ich würde es vermeiden, alle Tage dazwischen aufzuzählen, wenn dies vermeidbar ist, was hier der Fall ist. Ich erwähne nicht einmal das Erstellen einer Reihe von DateTime-Instanzen, wie ich in einer der obigen Antworten gesehen habe. Das ist wirklich Verschwendung von Rechenleistung. Besonders in der realen Welt, wenn Sie Zeitintervalle von mehreren Monaten untersuchen müssen. Siehe meinen Code mit Kommentaren unten.
/// <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>publicstaticintBusinessDaysUntil(thisDateTime firstDay,DateTime lastDay,paramsDateTime[] bankHolidays){
firstDay = firstDay.Date;
lastDay = lastDay.Date;if(firstDay > lastDay)thrownewArgumentException("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 weeksif(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 weeksint 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;elseif(lastDayOfWeek >=6)// Only Saturday is in the remaining time interval
businessDays -=1;}elseif(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 intervalforeach(DateTime bankHoliday in bankHolidays){DateTime bh = bankHoliday.Date;if(firstDay <= bh && bh <= lastDay)--businessDays;}return businessDays;}
Bearbeiten von Slauma, August 2011
Gute Antwort! Es gibt jedoch einen kleinen Fehler. Ich erlaube mir, diese Antwort zu bearbeiten, da der Antwortende seit 2009 abwesend ist.
Der obige Code geht davon aus, dass DayOfWeek.Sundayder Wert 7nicht der Fall ist. Der Wert ist tatsächlich 0. Es führt zum Beispiel zu einer falschen Berechnung firstDayund lastDaybeide sind am selben Sonntag. Die Methode gibt 1in diesem Fall zurück, sollte es aber sein 0.
Einfachste Lösung für diesen Fehler: Ersetzen Sie den Code über den Zeilen, in denen firstDayOfWeekund lastDayOfWeekwie folgt deklariert sind:
int firstDayOfWeek = firstDay.DayOfWeek==DayOfWeek.Sunday?7:(int)firstDay.DayOfWeek;int lastDayOfWeek = lastDay.DayOfWeek==DayOfWeek.Sunday?7:(int)lastDay.DayOfWeek;
+1 Das ist wahrscheinlich der einfachste und effizienteste Weg, dies zu tun (meine Lösung aus C ++ verwendet nicht die Unterstützung von TimeSpan, C # macht einige Aufgaben so viel einfacher). Die bankHolidays sind auch eine nette Geste!
RedGlyph
2
Stellen Sie außerdem sicher, dass die Feiertage wie folgt sind: if (firstDay <= bh && bh <= lastDay && bh.IsWorkingDay ())
Tawani
5
Danke für die Methode. Obwohl ich der if-Anweisung für die Subtraktion / Iteration von Feiertagen Folgendes hinzufügen musste: && !(bh.DayOfWeek == DayOfWeek.Sunday || bh.DayOfWeek == DayOfWeek.Saturday)Andernfalls würde derselbe Tag zweimal abgezogen, wenn ein Feiertag auf ein Wochenende fällt.
KristianB
Ich habe die letzte Schleife für eine Linq-Anweisung geändert: businessDays - = bankHolidays.Select (bankHoliday => bankHoliday.Date) .Count (bh => firstDay <= bh && bh <= lastDay);
Gute Arbeit, aber vielleicht die DayOfWeek-Enums selbst verwenden, anstatt sie in Ints umzuwandeln?
Neo
3
Im Ernst, die beste Lösung da draußen. Prost Alec
Mizmor
6
Beachten Sie, dass diese Funktion zwar ein Double zurückgibt, jedoch nur ganze Werktage angegeben werden sollte. Es wird nicht die richtige Antwort für gebrochene Tage zurückgegeben, wenn es um Zeiten geht.
Pakman
4
Nur um zu erwähnen, dass bei der '1+' der Beginn des ersten Tages bis zum Ende des letzten Tages angenommen wird, bei der '1+' das Ende des ersten Tages bis zum Ende des letzten Tages angenommen wird. Ich habe eine Weile gebraucht, um das herauszufinden, da ich vom Beginn des ersten bis zum Beginn des letzten Tages ausgegangen bin , was für mich sinnvoller war.
Jeffry van de Vuurst
11
Dies ist NICHT die richtige Antwort. Die Tage können bis zu 4 aus sein. Fast richtig, berücksichtigt nicht, wann der Start- und Endtag um das Wochenende herum endet, was das schwierigste Stück ist. Das Start-Ende sollte auch nicht in Klammern stehen. Es hat nichts mit dem Problem zu tun. In 60% der Fälle ist diese Lösung FALSCH .
Eule
47
Ich weiß, dass diese Frage bereits gelöst ist, aber ich dachte, ich könnte eine einfachere Antwort geben, die anderen Besuchern in Zukunft helfen könnte.
publicintGetWorkingDays(DateTimefrom,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;}
Viel klarer, und die aufgezählten Lösungen eignen sich zur Beseitigung von Feiertagen. Sie sind jedoch viel langsamer in der Masse; In LINQPad dauert die Berechnung der Arbeitstage für 90-Tage-Lücken in einer 1-Millionen-Iterationsschleife mit dieser Lösung 10 Sekunden und mit der akzeptierten Antwort nur etwa 0,2 Sekunden, oder Alec Pojidaevs viel schönere.
Whelkaholism
Um inklusiv zu sein, sollte der Code lauten: Enumerable .Range (0, dayDifference + 1) zurückgeben ...
Edza
gibt keine Tage in der Vergangenheit zurück. Wie -18 Arbeitstage.
iwtu
@iwtu Dies setzt das voraus to > from. Vielleicht ist das das Problem?
Alpha
22
Definieren Sie eine Erweiterungsmethode für DateTime wie folgt:
Die Verwendung befindet sich dann in einer Where-Klausel, um eine breitere Liste von Daten zu filtern:
var allDates =GetDates();// method which returns a list of dates// filter dates by working day's var countOfWorkDays = allDates
.Where(day => day.IsWorkingDay()).Count();
Würden Sie nicht auch die Zeitspanne verlängern, damit Sie diese nutzen können - da er gesagt hat, er möchte den Abstand zwischen zwei Daten und nicht eine Liste von Daten verwenden?
WesleyJohnson
Der Abstand zwischen den beiden Daten ist die Anzahl der Tage zwischen den beiden Daten, daher ist Count () ausreichend.
Carles Company
3
Ich bin mir nicht sicher, warum dies eine geeignete Antwort ist ... er hat keine Liste einzelner Tage, er hat zwei Daten und er möchte die Anzahl der Werktage zwischen ihnen ermitteln. Um diese Lösung verwenden zu können, müssten Sie eine andere Funktion bereitstellen , die eine Liste aller Daten zwischen den beiden erstellt.
Adam Robinson
1
Adam, dies ist ein einfaches Beispiel mit der minimalen Menge an Code, die benötigt wird, um ein Konzept zu demonstrieren. In meiner ursprünglichen Antwort habe ich auch eine Schleife eingefügt, die die allDates-Liste aufgefüllt hat, die ich seitdem in die Funktion "GetDates" abstrahiert habe. Der IsWorkingDay-Test kann leicht aus der LINQ-Anweisung in diese Schleife verschoben werden. Ich persönlich mag es, wie es jetzt ist, weil es sehr menschlich lesbar ist, was passiert.
Qwerty
10
Könnte kurzgeschlossen werden, indem Sie den Zählort ändern und die Zählung eliminieren
rekursiv
12
Ich habe den folgenden Code verwendet, um auch Feiertage zu berücksichtigen:
publicclassWorkingDays{publicList<DateTime>GetHolidays(){var client =newWebClient();var json = client.DownloadString("https://www.gov.uk/bank-holidays.json");var js =newJavaScriptSerializer();var holidays = js.Deserialize<Dictionary<string,Holidays>>(json);return holidays["england-and-wales"].events.Select(d => d.date).ToList();}publicintGetWorkingDays(DateTimefrom,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;}}publicclassHolidays{publicstring division {get;set;}publicList<Event> events {get;set;}}publicclassEvent{publicDateTime date {get;set;}publicstring notes {get;set;}publicstring title {get;set;}}
Und Unit Tests:
[TestClass]publicclassWorkingDays{[TestMethod]publicvoidSameDayIsZero(){var service =newWorkingDays();varfrom=newDateTime(2013,8,12);Assert.AreEqual(0, service.GetWorkingDays(from,from));}[TestMethod]publicvoidCalculateDaysInWorkingWeek(){var service =newWorkingDays();varfrom=newDateTime(2013,8,12);var to =newDateTime(2013,8,16);Assert.AreEqual(4, service.GetWorkingDays(from, to),"Mon - Fri = 4");Assert.AreEqual(1, service.GetWorkingDays(from,newDateTime(2013,8,13)),"Mon - Tues = 1");}[TestMethod]publicvoidNotIncludeWeekends(){var service =newWorkingDays();varfrom=newDateTime(2013,8,9);var to =newDateTime(2013,8,16);Assert.AreEqual(5, service.GetWorkingDays(from, to),"Fri - Fri = 5");Assert.AreEqual(2, service.GetWorkingDays(from,newDateTime(2013,8,13)),"Fri - Tues = 2");Assert.AreEqual(1, service.GetWorkingDays(from,newDateTime(2013,8,12)),"Fri - Mon = 1");}[TestMethod]publicvoidAccountForHolidays(){var service =newWorkingDays();varfrom=newDateTime(2013,8,23);Assert.AreEqual(0, service.GetWorkingDays(from,newDateTime(2013,8,26)),"Fri - Mon = 0");Assert.AreEqual(1, service.GetWorkingDays(from,newDateTime(2013,8,27)),"Fri - Tues = 1");}}
Warum fängst du an zu zählen, indem du 1 Tage zu "von" @ für (var date = from.AddDays (1); date <= to; date = date.AddDays (1)) hinzufügst?
Oncel Umut TURER
6
Nun, das wurde zu Tode geschlagen. :) Allerdings werde ich noch eine andere Antwort geben, weil ich etwas anderes brauchte. Diese Lösung unterscheidet sich darin, dass zwischen Start und Ende eine Geschäftszeitspanne zurückgegeben wird. Sie können die Geschäftszeiten des Tages festlegen und Feiertage hinzufügen. Sie können damit berechnen, ob es innerhalb eines Tages, über Tage, über Wochenenden und sogar Feiertage geschieht. Und Sie können nur die Werktage abrufen oder nicht, indem Sie einfach das abrufen, was Sie vom zurückgegebenen TimeSpan-Objekt benötigen. Und wie es Listen von Tagen verwendet, können Sie sehen, wie einfach es wäre, die Liste der arbeitsfreien Tage hinzuzufügen, wenn es nicht der typische Sa und So ist. Und ich habe ein Jahr lang getestet, und es scheint super schnell zu sein.
Ich hoffe nur, dass das Einfügen des Codes korrekt ist. Aber ich weiß, dass es funktioniert.
publicstaticTimeSpanGetBusinessTimespanBetween(DateTime start,DateTime end,TimeSpan workdayStartTime,TimeSpan workdayEndTime,List<DateTime> holidays =null){if(end < start)thrownewArgumentException("start datetime must be before end datetime.");// Just create an empty list for easier coding.if(holidays ==null) holidays =newList<DateTime>();if(holidays.Where(x => x.TimeOfDay.Ticks>0).Any())thrownewArgumentException("holidays can not have a TimeOfDay, only the Date.");var nonWorkDays =newList<DayOfWeek>(){DayOfWeek.Saturday,DayOfWeek.Sunday};var startTime = start.TimeOfDay;// If the start time is before the starting hours, set it to the starting hour.if(startTime < workdayStartTime) startTime = workdayStartTime;var timeBeforeEndOfWorkDay = workdayEndTime - startTime;// If it's after the end of the day, then this time lapse doesn't count.if(timeBeforeEndOfWorkDay.TotalSeconds<0) timeBeforeEndOfWorkDay =newTimeSpan();// If start is during a non work day, it doesn't count.if(nonWorkDays.Contains(start.DayOfWeek)) timeBeforeEndOfWorkDay =newTimeSpan();elseif(holidays.Contains(start.Date)) timeBeforeEndOfWorkDay =newTimeSpan();var endTime = end.TimeOfDay;// If the end time is after the ending hours, set it to the ending hour.if(endTime > workdayEndTime) endTime = workdayEndTime;var timeAfterStartOfWorkDay = endTime - workdayStartTime;// If it's before the start of the day, then this time lapse doesn't count.if(timeAfterStartOfWorkDay.TotalSeconds<0) timeAfterStartOfWorkDay =newTimeSpan();// If end is during a non work day, it doesn't count.if(nonWorkDays.Contains(end.DayOfWeek)) timeAfterStartOfWorkDay =newTimeSpan();elseif(holidays.Contains(end.Date)) timeAfterStartOfWorkDay =newTimeSpan();// Easy scenario if the times are during the day day.if(start.Date.CompareTo(end.Date)==0){if(nonWorkDays.Contains(start.DayOfWeek))returnnewTimeSpan();elseif(holidays.Contains(start.Date))returnnewTimeSpan();return endTime - startTime;}else{var timeBetween = end - start;var daysBetween =(int)Math.Floor(timeBetween.TotalDays);var dailyWorkSeconds =(int)Math.Floor((workdayEndTime - workdayStartTime).TotalSeconds);var businessDaysBetween =0;// Now the fun begins with calculating the actual Business days.if(daysBetween >0){var nextStartDay = start.AddDays(1).Date;var dayBeforeEnd = end.AddDays(-1).Date;for(DateTime d = nextStartDay; d <= dayBeforeEnd; d = d.AddDays(1)){if(nonWorkDays.Contains(d.DayOfWeek))continue;elseif(holidays.Contains(d.Date))continue;
businessDaysBetween++;}}var dailyWorkSecondsToAdd = dailyWorkSeconds * businessDaysBetween;var output = timeBeforeEndOfWorkDay + timeAfterStartOfWorkDay;
output = output +newTimeSpan(0,0, dailyWorkSecondsToAdd);return output;}}
Und hier ist Testcode : Beachten Sie, dass Sie diese Funktion nur in eine Klasse namens DateHelper einfügen müssen, damit der Testcode funktioniert.
[TestMethod]publicvoidTestGetBusinessTimespanBetween(){var workdayStart =newTimeSpan(8,0,0);var workdayEnd =newTimeSpan(17,0,0);var holidays =newList<DateTime>(){newDateTime(2018,1,15),// a MondaynewDateTime(2018,2,15)// a Thursday};var testdata =new[]{new{
expectedMinutes =0,
start =newDateTime(2016,10,19,9,50,0),
end =newDateTime(2016,10,19,9,50,0)},new{
expectedMinutes =10,
start =newDateTime(2016,10,19,9,50,0),
end =newDateTime(2016,10,19,10,0,0)},new{
expectedMinutes =5,
start =newDateTime(2016,10,19,7,50,0),
end =newDateTime(2016,10,19,8,5,0)},new{
expectedMinutes =5,
start =newDateTime(2016,10,19,16,55,0),
end =newDateTime(2016,10,19,17,5,0)},new{
expectedMinutes =15,
start =newDateTime(2016,10,19,16,50,0),
end =newDateTime(2016,10,20,8,5,0)},new{
expectedMinutes =10,
start =newDateTime(2016,10,19,16,50,0),
end =newDateTime(2016,10,20,7,55,0)},new{
expectedMinutes =5,
start =newDateTime(2016,10,19,17,10,0),
end =newDateTime(2016,10,20,8,5,0)},new{
expectedMinutes =0,
start =newDateTime(2016,10,19,17,10,0),
end =newDateTime(2016,10,20,7,5,0)},new{
expectedMinutes =545,
start =newDateTime(2016,10,19,12,10,0),
end =newDateTime(2016,10,20,12,15,0)},// Spanning multiple weekdaysnew{
expectedMinutes =835,
start =newDateTime(2016,10,19,12,10,0),
end =newDateTime(2016,10,21,8,5,0)},// Spanning multiple weekdaysnew{
expectedMinutes =1375,
start =newDateTime(2016,10,18,12,10,0),
end =newDateTime(2016,10,21,8,5,0)},// Spanning from a Thursday to a Tuesday, 5 mins short of complete day.new{
expectedMinutes =1615,
start =newDateTime(2016,10,20,12,10,0),
end =newDateTime(2016,10,25,12,5,0)},// Spanning from a Thursday to a Tuesday, 5 mins beyond complete day.new{
expectedMinutes =1625,
start =newDateTime(2016,10,20,12,10,0),
end =newDateTime(2016,10,25,12,15,0)},// Spanning from a Friday to a Monday, 5 mins beyond complete day.new{
expectedMinutes =545,
start =newDateTime(2016,10,21,12,10,0),
end =newDateTime(2016,10,24,12,15,0)},// Spanning from a Friday to a Monday, 5 mins short complete day.new{
expectedMinutes =535,
start =newDateTime(2016,10,21,12,10,0),
end =newDateTime(2016,10,24,12,5,0)},// Spanning from a Saturday to a Monday, 5 mins short complete day.new{
expectedMinutes =245,
start =newDateTime(2016,10,22,12,10,0),
end =newDateTime(2016,10,24,12,5,0)},// Spanning from a Saturday to a Sunday, 5 mins beyond complete day.new{
expectedMinutes =0,
start =newDateTime(2016,10,22,12,10,0),
end =newDateTime(2016,10,23,12,15,0)},// Times within the same Saturday.new{
expectedMinutes =0,
start =newDateTime(2016,10,22,12,10,0),
end =newDateTime(2016,10,23,12,15,0)},// Spanning from a Saturday to the Sunday next week.new{
expectedMinutes =2700,
start =newDateTime(2016,10,22,12,10,0),
end =newDateTime(2016,10,30,12,15,0)},// Spanning a year.new{
expectedMinutes =143355,
start =newDateTime(2016,10,22,12,10,0),
end =newDateTime(2017,10,30,12,15,0)},// Spanning a year with 2 holidays.new{
expectedMinutes =142815,
start =newDateTime(2017,10,22,12,10,0),
end =newDateTime(2018,10,30,12,15,0)},};foreach(var item in testdata){Assert.AreEqual(item.expectedMinutes,DateHelper.GetBusinessTimespanBetween(
item.start, item.end,
workdayStart, workdayEnd,
holidays).TotalMinutes);}}
Diese Lösung vermeidet Iterationen, funktioniert sowohl für + ve als auch für -ve Wochentagsunterschiede und enthält eine Unit-Test-Suite zur Regression gegen die langsamere Methode zum Zählen von Wochentagen. Ich habe auch eine prägnante Methode zum Hinzufügen von Wochentagen hinzugefügt, die ebenfalls auf dieselbe nicht iterative Weise funktioniert.
Unit-Tests decken einige tausend Datumskombinationen ab, um alle Start- / End-Wochentagskombinationen mit kleinen und großen Datumsbereichen ausführlich zu testen.
Wichtig : Wir gehen davon aus, dass wir Tage zählen, indem wir das Startdatum und das Enddatum ausschließen. Dies ist wichtig, wenn Sie Wochentage zählen, da die spezifischen Start- / Endtage, die Sie einschließen / ausschließen, das Ergebnis beeinflussen. Dies stellt auch sicher, dass die Differenz zwischen zwei gleichen Tagen immer Null ist und dass wir nur volle Arbeitstage einbeziehen, da Sie normalerweise möchten, dass die Antwort zu jedem Zeitpunkt am aktuellen Startdatum (häufig heute) korrekt ist und das vollständige Enddatum enthält (z ein Fälligkeitsdatum).
HINWEIS: Dieser Code muss für Feiertage zusätzlich angepasst werden. In Übereinstimmung mit der obigen Annahme muss dieser Code jedoch Feiertage am Startdatum ausschließen.
Wochentage hinzufügen:
privatestaticreadonlyint[,] _addOffset ={// 0 1 2 3 4{0,1,2,3,4},// Su 0{0,1,2,3,4},// M 1{0,1,2,3,6},// Tu 2{0,1,4,5,6},// W 3{0,1,4,5,6},// Th 4{0,3,4,5,6},// F 5{0,2,3,4,5},// Sa 6};publicstaticDateTimeAddWeekdays(thisDateTime date,int weekdays){int extraDays = weekdays %5;int addDays = weekdays >=0?(weekdays /5)*7+ _addOffset[(int)date.DayOfWeek, extraDays]:(weekdays /5)*7- _addOffset[6-(int)date.DayOfWeek,-extraDays];return date.AddDays(addDays);}
Wochentagsdifferenz berechnen:
staticreadonlyint[,] _diffOffset ={// Su M Tu W Th F Sa{0,1,2,3,4,5,5},// Su{4,0,1,2,3,4,4},// M {3,4,0,1,2,3,3},// Tu{2,3,4,0,1,2,2},// W {1,2,3,4,0,1,1},// Th{0,1,2,3,4,0,0},// F {0,1,2,3,4,5,0},// Sa};publicstaticintGetWeekdaysDiff(thisDateTime dtStart,DateTime dtEnd){int daysDiff =(int)(dtEnd - dtStart).TotalDays;return daysDiff >=0?5*(daysDiff /7)+ _diffOffset[(int) dtStart.DayOfWeek,(int) dtEnd.DayOfWeek]:5*(daysDiff /7)- _diffOffset[6-(int) dtStart.DayOfWeek,6-(int) dtEnd.DayOfWeek];}
Ich fand heraus, dass die meisten anderen Lösungen zum Stapelüberlauf entweder langsam (iterativ) oder zu komplex waren und viele einfach falsch waren. Die Moral der Geschichte ist ... Vertraue ihr nicht, es sei denn, du hast sie gründlich getestet !!
Die Idee dazu kam von einer SQL-Lösung, die ich beim Stapelüberlauf gefunden habe. Ihre Idee war solide, aber leider hatte auch sie einen Fehler. Es funktionierte für + ve Werte, aber die Zuordnung der Nachschlagetabelle war für -ve Werte falsch.
Tony O'Hagan
4
Hier ist ein Code für diesen Zweck mit schwedischen Feiertagen, aber Sie können anpassen, welche Feiertage gezählt werden sollen. Beachten Sie, dass ich ein Limit hinzugefügt habe, das Sie möglicherweise entfernen möchten, aber es war für ein webbasiertes System und ich wollte nicht, dass jemand ein großes Datum eingibt, um den Prozess zu blockieren
publicstaticintGetWorkdays(DateTimefrom,DateTime to){int limit =9999;int counter =0;DateTime current =from;int result =0;if(from> to){DateTime temp =from;from= to;
to = temp;}if(from>= to){return0;}while(current <= to && counter < limit){if(IsSwedishWorkday(current)){
result++;}
current = current.AddDays(1);
counter++;}return result;}publicstaticboolIsSwedishWorkday(DateTime date){return(!IsSwedishHoliday(date)&& date.DayOfWeek!=DayOfWeek.Saturday&& date.DayOfWeek!=DayOfWeek.Sunday);}publicstaticboolIsSwedishHoliday(DateTime date){return(IsSameDay(GetEpiphanyDay(date.Year), date)||IsSameDay(GetMayDay(date.Year), date)||IsSameDay(GetSwedishNationalDay(date.Year), date)||IsSameDay(GetChristmasDay(date.Year), date)||IsSameDay(GetBoxingDay(date.Year), date)||IsSameDay(GetGoodFriday(date.Year), date)||IsSameDay(GetAscensionDay(date.Year), date)||IsSameDay(GetAllSaintsDay(date.Year), date)||IsSameDay(GetMidsummersDay(date.Year), date)||IsSameDay(GetPentecostDay(date.Year), date)||IsSameDay(GetEasterMonday(date.Year), date)||IsSameDay(GetNewYearsDay(date.Year), date)||IsSameDay(GetEasterDay(date.Year), date));}// TrettondagenpublicstaticDateTimeGetEpiphanyDay(int year){returnnewDateTime(year,1,6);}// Första majpublicstaticDateTimeGetMayDay(int year){returnnewDateTime(year,5,1);}// JuldagenpublicstaticDateTimeGetSwedishNationalDay(int year){returnnewDateTime(year,6,6);}// JuldagenpublicstaticDateTimeGetNewYearsDay(int year){returnnewDateTime(year,1,1);}// JuldagenpublicstaticDateTimeGetChristmasDay(int year){returnnewDateTime(year,12,25);}// Annandag julpublicstaticDateTimeGetBoxingDay(int year){returnnewDateTime(year,12,26);}// LångfredagenpublicstaticDateTimeGetGoodFriday(int year){returnGetEasterDay(year).AddDays(-3);}// Kristi himmelsfärdsdagpublicstaticDateTimeGetAscensionDay(int year){returnGetEasterDay(year).AddDays(5*7+4);}// MidsommarpublicstaticDateTimeGetAllSaintsDay(int year){DateTime result =newDateTime(year,10,31);while(result.DayOfWeek!=DayOfWeek.Saturday){
result = result.AddDays(1);}return result;}// MidsommarpublicstaticDateTimeGetMidsummersDay(int year){DateTime result =newDateTime(year,6,20);while(result.DayOfWeek!=DayOfWeek.Saturday){
result = result.AddDays(1);}return result;}// PingstdagenpublicstaticDateTimeGetPentecostDay(int year){returnGetEasterDay(year).AddDays(7*7);}// Annandag påskpublicstaticDateTimeGetEasterMonday(int year){returnGetEasterDay(year).AddDays(1);}publicstaticDateTimeGetEasterDay(int y){double c;double n;double k;double i;double j;double l;double m;double d;
c =System.Math.Floor(y /100.0);
n = y -19*System.Math.Floor(y /19.0);
k =System.Math.Floor((c -17)/25.0);
i = c -System.Math.Floor(c /4)-System.Math.Floor((c - k)/3)+19* n +15;
i = i -30*System.Math.Floor(i /30);
i = i -System.Math.Floor(i /28)*(1-System.Math.Floor(i /28)*System.Math.Floor(29/(i +1))*System.Math.Floor((21- n)/11));
j = y +System.Math.Floor(y /4.0)+ i +2- c +System.Math.Floor(c /4);
j = j -7*System.Math.Floor(j /7);
l = i - j;
m =3+System.Math.Floor((l +40)/44);// month
d = l +28-31*System.Math.Floor(m /4);// daydouble days =((m ==3)? d : d +31);DateTime result =newDateTime(y,3,1).AddDays(days-1);return result;}
Funktion issamedate fehlt, ist aber einfach öffentlich statisch bool IsSameDay (DateTime date1, DateTime date2) {return date1.Date == date2.Date; }
Choco Smith
Sie können eine Int-Array-Nachschlagetabelle verwenden, anstatt neue Datumsobjekte zu instanziieren.
TheRealChx101
3
Hier ist ein kurzer Beispielcode. Es ist eine Klassenmethode und funktioniert daher nur innerhalb Ihrer Klasse. Wenn Sie möchten static, ändern Sie die Signatur in private static(oder public static).
privateIEnumerable<DateTime>GetWorkingDays(DateTime sd,DateTime ed){for(var d = sd; d <= ed; d = d.AddDays(1))if(d.DayOfWeek!=DayOfWeek.Saturday&& d.DayOfWeek!=DayOfWeek.Sunday)yieldreturn d;}
Diese Methode erstellt eine Schleifenvariable d, initialisiert sie auf den Starttag und sderhöht sie dann bei jeder Iteration um einen Tag ( d = d.AddDays(1)).
Es gibt die gewünschten Werte mit zurück yield, wodurch ein erstellt wird iterator. Das Coole an Iteratoren ist, dass sie nicht alle Werte des IEnumerableIn-Speichers enthalten, sondern nur nacheinander aufrufen. Dies bedeutet, dass Sie diese Methode von Anfang an aufrufen können, ohne sich Sorgen machen zu müssen, dass Ihnen der Speicher ausgeht.
Diese Methode gibt nicht die Anzahl der Geschäftstage zwischen zwei Daten zurück, sondern die Geschäftsdaten zwischen zwei Daten. Der Code, den Sie vorschlagen, ist sehr sauber und ich mag die Verwendung von Yield, aber er beantwortet die Frage nicht.
Martin
3
Ich habe viel nach einem leicht verdaulichen Algorithmus gesucht, um die Arbeitstage zwischen zwei Daten zu berechnen und auch die Nationalfeiertage auszuschließen, und schließlich habe ich mich für diesen Ansatz entschieden:
publicstaticintNumberOfWorkingDaysBetween2Dates(DateTime start,DateTime due,IEnumerable<DateTime> holidays){var dic =newDictionary<DateTime,DayOfWeek>();var totalDays =(due - start).Days;for(int i =0; i < totalDays +1; i++){if(!holidays.Any(x => x == start.AddDays(i)))
dic.Add(start.AddDays(i), start.AddDays(i).DayOfWeek);}return dic.Where(x => x.Value!=DayOfWeek.Saturday&& x.Value!=DayOfWeek.Sunday).Count();}
Grundsätzlich wollte ich mit jedem Datum meine Bedingungen bewerten:
Ist nicht Samstag
Ist nicht Sonntag
Ist kein Nationalfeiertag
aber ich wollte auch vermeiden, dass Daten wiederholt werden.
Durch Ausführen und Messen der Zeit, die für die Bewertung eines vollen Jahres benötigt wird, erhalte ich das folgende Ergebnis:
staticvoidMain(string[] args){var start =newDateTime(2017,1,1);var due =newDateTime(2017,12,31);var sw =Stopwatch.StartNew();var days =NumberOfWorkingDaysBetween2Dates(start, due,NationalHolidays());
sw.Stop();Console.WriteLine($"Total working days = {days} --- time: {sw.Elapsed}");Console.ReadLine();// result is:// Total working days = 249-- - time: 00:00:00.0269087}
Ich denke, keine der obigen Antworten ist tatsächlich richtig. Keiner von ihnen löst alle Sonderfälle, z. B. wenn das Datum in der Mitte eines Wochenendes beginnt und endet, wenn das Datum an einem Freitag beginnt und am nächsten Montag endet usw. Darüber hinaus runden sie die Berechnungen insgesamt ab Tage. Wenn das Startdatum beispielsweise in der Mitte eines Samstags liegt, wird ein ganzer Tag von den Arbeitstagen abgezogen, was zu falschen Ergebnissen führt ...
Wie auch immer, hier ist meine Lösung, die sehr effizient und einfach ist und für alle Fälle funktioniert. Der Trick besteht darin, nur den vorherigen Montag für Start- und Enddaten zu finden und dann eine kleine Entschädigung zu leisten, wenn Start und Ende am Wochenende stattfinden:
publicdoubleWorkDays(DateTime startDate,DateTime endDate){double weekendDays;double days = endDate.Subtract(startDate).TotalDays;if(days<0)return0;DateTime startMonday = startDate.AddDays(DayOfWeek.Monday- startDate.DayOfWeek).Date;DateTime endMonday = endDate.AddDays(DayOfWeek.Monday- endDate.DayOfWeek).Date;
weekendDays =((endMonday.Subtract(startMonday).TotalDays)/7)*2;// compute fractionary part of weekend daysdouble diffStart = startDate.Subtract(startMonday).TotalDays-5;double diffEnd = endDate.Subtract(endMonday).TotalDays-5;// compensate weekenddaysif(diffStart>0) weekendDays -= diffStart;if(diffEnd>0) weekendDays += diffEnd;return days - weekendDays;}
Dies gibt -1 zurück, wenn mit einem Samstag und Sonntag aufgerufen wird.
Whelkaholism
1
Funktioniert und ohne Schleifen
Diese Methode verwendet keine Schleifen und ist eigentlich recht einfach. Es erweitert den Datumsbereich auf volle Wochen, da wir wissen, dass jede Woche 5 Werktage hat. Anschließend wird anhand einer Nachschlagetabelle die Anzahl der Werktage ermittelt, die vom Anfang und Ende abgezogen werden müssen, um das richtige Ergebnis zu erzielen. Ich habe die Berechnung erweitert, um zu zeigen, was los ist, aber das Ganze könnte bei Bedarf in einer einzigen Zeile zusammengefasst werden.
Wie auch immer, das funktioniert bei mir und deshalb dachte ich, ich würde es hier posten, falls es anderen helfen könnte. Viel Spaß beim Codieren.
Berechnung
t: Gesamtzahl der Tage zwischen den Daten (1, wenn min = max)
a + b: Zusätzliche Tage erforderlich, um die Gesamtzahl auf volle Wochen auszudehnen
k: 1,4 ist die Anzahl der Wochentage pro Woche, dh (t / 7) * 5
c: Anzahl der Wochentage, die von der Gesamtsumme abgezogen werden sollen
m: Eine Nachschlagetabelle, mit der der Wert von "c" für jeden Wochentag ermittelt wird
Kultur
Der Code geht von einer Arbeitswoche von Montag bis Freitag aus. Für andere Kulturen, wie z. B. Sonntag bis Donnerstag, müssen Sie die Daten vor der Berechnung ausgleichen.
Methode
publicintWeekdays(DateTime min,DateTime max){if(min.Date> max.Date)thrownewException("Invalid date span");var t =(max.AddDays(1).Date- min.Date).TotalDays;var a =(int) min.DayOfWeek;var b =6-(int) max.DayOfWeek;var k =1.4;var m =newint[]{0,0,1,2,3,4,5};var c = m[a]+ m[b];return(int)((t + a + b)/ k)- c;}
Ich werde nur meine Lösung teilen. Es hat bei mir funktioniert, vielleicht merke ich einfach nicht, dass es einen Fehler gibt. Ich begann damit, die erste unvollständige Woche zu bekommen, wenn es welche gibt. Eine vollständige Woche war von Sonntag bis Samstag. Wenn also (int) _now.DayOfWeek nicht 0 (Sonntag) war, war die erste Woche unvollständig.
Ich subtrahiere nur 1 bis die Anzahl der ersten Wochen für den Samstag der ersten Woche und addiere sie dann zur neuen Anzahl.
Dann bekomme ich die letzte unvollständige Woche, dann subtrahiere ich 1 für den Sonntag und addiere dann zu der neuen Zählung.
Dann wurde schließlich die Anzahl der vollständigen Wochen multipliziert mit 5 (Wochentage) zur neuen Zählung hinzugefügt.
Ich hatte Probleme, eine solide TSQL-Version dieses Codes zu finden. Im Folgenden finden Sie im Wesentlichen eine Konvertierung des C # -Codes hier mit der Feiertagstabelle, die zur Vorberechnung von Feiertagen verwendet werden sollte.
CREATE TABLE dbo.Holiday(HolidayDt DATE NOT NULL,Name NVARCHAR(50) NOT NULL,IsWeekday BIT NOT NULL,
CONSTRAINT PK_Holiday PRIMARY KEY (HolidayDt))
GO
CREATE INDEX IDX_Holiday ON Holiday(HolidayDt,IsWeekday)
GO
CREATE function dbo.GetBusinessDays(@FirstDay datetime,@LastDay datetime
)
RETURNS INT
AS
BEGIN
DECLARE @BusinessDays INT,@FullWeekCount INT
SELECT @FirstDay= CONVERT(DATETIME,CONVERT(DATE,@FirstDay)),@LastDay= CONVERT(DATETIME,CONVERT(DATE,@LastDay))
IF @FirstDay>@LastDay
RETURN NULL;
SELECT @BusinessDays= DATEDIFF(DAY,@FirstDay,@LastDay)+1
SELECT @FullWeekCount=@BusinessDays/7;-- find outif there are weekends during the time exceedng the full weeks
IF @BusinessDays>(@FullWeekCount*7)
BEGIN
-- we are here to find outif there is a 1-day or 2-days weekend
--in the time interval remaining after subtracting the complete weeks
DECLARE @firstDayOfWeek INT,@lastDayOfWeek INT;
SELECT @firstDayOfWeek= DATEPART(DW,@FirstDay),@lastDayOfWeek= DATEPART(DW,@LastDay);
IF @lastDayOfWeek<@firstDayOfWeek
SELECT @lastDayOfWeek=@lastDayOfWeek+7;
IF @firstDayOfWeek<=6
BEGIN
IF (@lastDayOfWeek>=7)--BothSaturday and Sunday are in the remaining time interval
BEGIN
SELECT @BusinessDays=@BusinessDays-2
END
ELSE IF @lastDayOfWeek>=6--OnlySaturdayisin the remaining time interval
BEGIN
SELECT @BusinessDays=@BusinessDays-1
END
END
ELSE IF @firstDayOfWeek<=7 AND @lastDayOfWeek>=7--OnlySundayisin the remaining time interval
BEGIN
SELECT @BusinessDays=@BusinessDays-1
END
END
-- subtract the weekends during the full weeks in the interval
DECLARE @Holidays INT;
SELECT @Holidays= COUNT(*)
FROM Holiday
WHERE HolidayDt BETWEEN @FirstDay AND @LastDay
AND IsWeekday= CAST(1 AS BIT)
SELECT @BusinessDays=@BusinessDays-(@FullWeekCount+@FullWeekCount)---@Holidays
RETURN @BusinessDays
END
Hier ist eine sehr einfache Lösung für dieses Problem. Wir haben Startdatum, Enddatum und "for-Schleife", um den Tag zu vergrößern und zu berechnen, ob es sich um einen Arbeitstag oder ein Wochenende handelt, indem wir in den String DayOfWeek konvertieren.
classProgram{staticvoidMain(string[] args){DateTime day =newDateTime();Console.Write("Inser your end date (example: 01/30/2015): ");DateTime endDate =DateTime.Parse(Console.ReadLine());int numberOfDays =0;for(day =DateTime.Now.Date; day.Date< endDate.Date; day = day.Date.AddDays(1)){string dayToString =Convert.ToString(day.DayOfWeek);if(dayToString !="Saturday"&& dayToString !="Sunday") numberOfDays++;}Console.WriteLine("Number of working days (not including local holidays) between two dates is "+numberOfDays);}}
Basierend auf dem Kommentar, der als empfohlene Antwort und der empfohlene Patch markiert ist, sowie -> Diese Version möchte die Tage in Geschäftszeiten konvertieren ... Berücksichtigt auch die gleichen Tagesstunden.
/// <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 hours during the 'span'</returns>publicstaticintBusinessHoursUntil(DateTime firstDay,DateTime lastDay,paramsDateTime[] bankHolidays){var original_firstDay = firstDay;var original_lastDay = lastDay;
firstDay = firstDay.Date;
lastDay = lastDay.Date;if(firstDay > lastDay)return-1;//// 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 weeksif(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 weeksint firstDayOfWeek = firstDay.DayOfWeek==DayOfWeek.Sunday?7:(int)firstDay.DayOfWeek;int lastDayOfWeek = lastDay.DayOfWeek==DayOfWeek.Sunday?7:(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;elseif(lastDayOfWeek >=6)// Only Saturday is in the remaining time interval
businessDays -=1;}elseif(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;if(bankHolidays !=null&& bankHolidays.Any()){// subtract the number of bank holidays during the time intervalforeach(DateTime bankHoliday in bankHolidays){DateTime bh = bankHoliday.Date;if(firstDay <= bh && bh <= lastDay)--businessDays;}}int total_business_hours =0;if(firstDay.Date== lastDay.Date){//If on the same day, go granular with Hours from the Orginial_*Day values
total_business_hours =(int)(original_lastDay - original_firstDay).TotalHours;}else{//Convert Business-Days to TotalHours
total_business_hours =(int)(firstDay.AddDays(businessDays).AddHours(firstDay.Hour)- firstDay).TotalHours;}return total_business_hours;}
Ich habe gerade die Antwort von @Alexander und @Slauma verbessert, um eine Geschäftswoche als Parameter zu unterstützen, für Fälle, in denen Samstag ein Geschäftstag ist, oder sogar für Fälle, in denen nur einige Wochentage als Geschäftstage gelten:
/// <summary>/// Calculate the number of business days between two dates, considering:/// - Days of the week that are not considered business days./// - Holidays between these two dates./// </summary>/// <param name="fDay">First day of the desired 'span'.</param>/// <param name="lDay">Last day of the desired 'span'.</param>/// <param name="BusinessDaysOfWeek">Days of the week that are considered to be business days, if NULL considers monday, tuesday, wednesday, thursday and friday as business days of the week.</param>/// <param name="Holidays">Holidays, if NULL, considers no holiday.</param>/// <returns>Number of business days during the 'span'</returns>publicstaticintBusinessDaysUntil(thisDateTime fDay,DateTime lDay,DayOfWeek[]BusinessDaysOfWeek=null,DateTime[]Holidays=null){if(BusinessDaysOfWeek==null)BusinessDaysOfWeek=newDayOfWeek[]{DayOfWeek.Monday,DayOfWeek.Tuesday,DayOfWeek.Wednesday,DayOfWeek.Thursday,DayOfWeek.Friday};if(Holidays==null)Holidays=newDateTime[]{};
fDay = fDay.Date;
lDay = lDay.Date;if(fDay > lDay)thrownewArgumentException("Incorrect last day "+ lDay);int bDays =(lDay - fDay).Days+1;int fullWeekCount = bDays /7;int fullWeekCountMult =7-WeekDays.Length;// Find out if there are weekends during the time exceedng the full weeksif(bDays >(fullWeekCount *7)){int fDayOfWeek =(int)fDay.DayOfWeek;int lDayOfWeek =(int)lDay.DayOfWeek;if(fDayOfWeek > lDayOfWeek)
lDayOfWeek +=7;// If they are the same, we already covered it right before the Holiday subtractionif(lDayOfWeek != fDayOfWeek){// Here we need to see if any of the days between are considered business daysfor(int i = fDayOfWeek; i <= lDayOfWeek; i++)if(!WeekDays.Contains((DayOfWeek)(i >6? i -7: i)))
bDays -=1;}}// Subtract the days that are not in WeekDays[] during the full weeks in the interval
bDays -=(fullWeekCount * fullWeekCountMult);// Subtract the number of bank holidays during the time interval
bDays = bDays -Holidays.Select(x => x.Date).Count(x => fDay <= x && x <= lDay);return bDays;}
Hier ist die Funktion, mit der wir Geschäftstage zwischen zwei Daten berechnen können. Ich verwende keine Urlaubsliste, da diese je nach Land / Region variieren kann.
Wenn wir es trotzdem verwenden möchten, können wir das dritte Argument als Liste der Feiertage verwenden. Bevor wir die Anzahl erhöhen, sollten wir überprüfen, ob die Liste nicht d enthält
publicstaticintGetBussinessDaysBetweenTwoDates(DateTimeStartDate,DateTimeEndDate){if(StartDate>EndDate)return-1;int bd =0;for(DateTime d =StartDate; d <EndDate; d = d.AddDays(1)){if(d.DayOfWeek!=DayOfWeek.Saturday&& d.DayOfWeek!=DayOfWeek.Sunday)
bd++;}return bd;}
Hier ist noch eine weitere Idee: Mit dieser Methode können Sie jede Arbeitswoche und jeden Feiertag angeben.
Die Idee hier ist, dass wir den Kern des Datumsbereichs vom ersten ersten Arbeitstag der Woche bis zum letzten Wochenendtag der Woche finden. Dies ermöglicht es uns, die gesamten Wochen einfach zu berechnen ( ohne alle Daten zu durchlaufen). Alles, was wir dann tun müssen, ist, die Arbeitstage hinzuzufügen, die vor dem Beginn und dem Ende dieses Kernbereichs liegen.
publicstaticintCalculateWorkingDays(DateTime startDate,DateTime endDate,IList<DateTime> holidays,DayOfWeek firstDayOfWeek,DayOfWeek lastDayOfWeek){// Make sure the defined working days run contiguouslyif(lastDayOfWeek < firstDayOfWeek){thrownewException("Last day of week cannot fall before first day of week!");}// Create a list of the days of the week that make-up the weekend by working back// from the firstDayOfWeek and forward from lastDayOfWeek to get the start and end// the weekendvar weekendStart = lastDayOfWeek ==DayOfWeek.Saturday?DayOfWeek.Sunday: lastDayOfWeek +1;var weekendEnd = firstDayOfWeek ==DayOfWeek.Sunday?DayOfWeek.Saturday: firstDayOfWeek -1;var weekendDays =newList<DayOfWeek>();var w = weekendStart;do{
weekendDays.Add(w);if(w == weekendEnd)break;
w =(w ==DayOfWeek.Saturday)?DayOfWeek.Sunday: w +1;}while(true);// Force simple dates - no time
startDate = startDate.Date;
endDate = endDate.Date;// Ensure a progessive date rangeif(endDate < startDate){var t = startDate;
startDate = endDate;
endDate = t;}// setup some working variables and constantsconstint daysInWeek =7;// yeah - really!var actualStartDate = startDate;// this will end up on startOfWeek boundaryvar actualEndDate = endDate;// this will end up on weekendEnd boundaryint workingDaysInWeek = daysInWeek - weekendDays.Count;int workingDays =0;// the result we are trying to findint leadingDays =0;// the number of working days leading up to the firstDayOfWeek boundaryint trailingDays =0;// the number of working days counting back to the weekendEnd boundary// Calculate leading working days// if we aren't on the firstDayOfWeek we need to step forward to the nearestif(startDate.DayOfWeek!= firstDayOfWeek){var d = startDate;do{if(d.DayOfWeek== firstDayOfWeek || d >= endDate){
actualStartDate = d;break;}if(!weekendDays.Contains(d.DayOfWeek)){
leadingDays++;}
d = d.AddDays(1);}while(true);}// Calculate trailing working days// if we aren't on the weekendEnd we step back to the nearestif(endDate >= actualStartDate && endDate.DayOfWeek!= weekendEnd){var d = endDate;do{if(d.DayOfWeek== weekendEnd || d < actualStartDate){
actualEndDate = d;break;}if(!weekendDays.Contains(d.DayOfWeek)){
trailingDays++;}
d = d.AddDays(-1);}while(true);}// Calculate the inclusive number of days between the actualStartDate and the actualEndDatevar coreDays =(actualEndDate - actualStartDate).Days+1;var noWeeks = coreDays / daysInWeek;// add together leading, core and trailing days
workingDays += noWeeks * workingDaysInWeek;
workingDays += leadingDays;
workingDays += trailingDays;// Finally remove any holidays that fall within the range.if(holidays !=null){
workingDays -= holidays.Count(h => h >= startDate &&(h <= endDate));}return workingDays;}
Da kann ich nicht kommentieren. Es gibt noch ein Problem mit der akzeptierten Lösung, bei der Feiertage abgezogen werden, selbst wenn sie am Wochenende liegen. Wenn man sieht, wie andere Eingaben überprüft werden, ist es nur passend, dass dies auch so ist.
Der foreach sollte daher sein:
// subtract the number of bank holidays during the time intervalforeach(DateTime bankHoliday in bankHolidays){DateTime bh = bankHoliday.Date;// Do not subtract bank holidays when they fall in the weekend to avoid double subtractionif(bh.DayOfWeek==DayOfWeek.Saturday|| bh.DayOfWeek==DayOfWeek.Sunday)continue;if(firstDay <= bh && bh <= lastDay)--businessDays;}
Hier ist ein Ansatz, wenn Sie MVC verwenden. Ich habe auch Nationalfeiertage oder Festtage berechnet, die ausgeschlossen werden sollen, indem ich sie aus dem Feiertagskalender abrufe, den Sie benötigen, um einen zu erstellen.
foreach(DateTime day inEachDay(model)){bool key =false;foreach(LeaveModel ln in holidaycalendar){if(day.Date== ln.Date&& day.DayOfWeek!=DayOfWeek.Saturday&& day.DayOfWeek!=DayOfWeek.Sunday){
key =true;break;}}if(day.DayOfWeek==DayOfWeek.Saturday|| day.DayOfWeek==DayOfWeek.Sunday){
key =true;}if(key !=true){
leavecount++;}}
Hier ist eine Hilfsfunktion, die ich für diese Aufgabe geschrieben habe. Über den outParameter wird auch die Anzahl der Wochenenden zurückgegeben . Wenn Sie möchten, können Sie die "Wochenend" -Tage zur Laufzeit für Länder anpassen, die unterschiedliche Wochenendtage verwenden, oder Feiertage über den weekendDays[]optionalen Parameter einschließen :
publicstaticintGetNetworkDays(DateTime startDate,DateTime endDate,outint totalWeekenDays,DayOfWeek[] weekendDays =null){if(startDate >= endDate){thrownewException("start date can not be greater then or equel to end date");}DayOfWeek[] weekends =newDayOfWeek[]{DayOfWeek.Sunday,DayOfWeek.Saturday};if(weekendDays !=null){
weekends = weekendDays;}var totaldays =(endDate - startDate).TotalDays+1;// add one to include the first day tovar counter =0;var workdaysCounter =0;var weekendsCounter =0;for(int i =0; i < totaldays; i++){if(weekends.Contains(startDate.AddDays(counter).DayOfWeek)){
weekendsCounter++;}else{
workdaysCounter++;}
counter++;}
totalWeekenDays = weekendsCounter;return workdaysCounter;}
publicstaticintCalculateBusinessDaysInRange(thisDateTime startDate,DateTime endDate,paramsDateTime[] holidayDates){
endDate = endDate.Date;if(startDate > endDate)thrownewArgumentException("The end date can not be before the start date!", nameof(endDate));int accumulator =0;DateTime itterator = startDate.Date;do{if(itterator.DayOfWeek!=DayOfWeek.Saturday&& itterator.DayOfWeek!=DayOfWeek.Sunday&&!holidayDates.Any(hol => hol.Date== itterator)){ accumulator++;}}while((itterator = itterator.AddDays(1)).Date<= endDate);return accumulator
}
Ich poste dies nur, weil trotz all der hervorragenden Antworten, die gegeben wurden, keine der Berechnungen für mich Sinn machte. Dies ist definitiv eine KISS-Methode, die funktionieren und ziemlich wartbar sein sollte. Zugegeben, wenn Sie Bereiche berechnen, die länger als 2-3 Monate sind, ist dies nicht der effektivste Weg. Wir bestimmen einfach, ob es ein Samstag oder Sonntag ist oder ob das Datum ein bestimmtes Feiertagsdatum ist. Wenn nicht, fügen wir einen Werktag hinzu. Wenn ja, ist alles in Ordnung.
Ich bin sicher, dass dies mit LINQ noch einfacher sein könnte, aber dieser Weg ist viel einfacher zu verstehen.
Ein weiterer Ansatz zur Berechnung von Geschäftstagen, bei dem keine Feiertage berücksichtigt werden, sondern die Tageszeit berücksichtigt wird, die einen Bruchteil der Tage zurückgibt:
Antworten:
Ich hatte schon einmal eine solche Aufgabe und habe die Lösung. Ich würde es vermeiden, alle Tage dazwischen aufzuzählen, wenn dies vermeidbar ist, was hier der Fall ist. Ich erwähne nicht einmal das Erstellen einer Reihe von DateTime-Instanzen, wie ich in einer der obigen Antworten gesehen habe. Das ist wirklich Verschwendung von Rechenleistung. Besonders in der realen Welt, wenn Sie Zeitintervalle von mehreren Monaten untersuchen müssen. Siehe meinen Code mit Kommentaren unten.
Bearbeiten von Slauma, August 2011
Gute Antwort! Es gibt jedoch einen kleinen Fehler. Ich erlaube mir, diese Antwort zu bearbeiten, da der Antwortende seit 2009 abwesend ist.
Der obige Code geht davon aus, dass
DayOfWeek.Sunday
der Wert7
nicht der Fall ist. Der Wert ist tatsächlich0
. Es führt zum Beispiel zu einer falschen BerechnungfirstDay
undlastDay
beide sind am selben Sonntag. Die Methode gibt1
in diesem Fall zurück, sollte es aber sein0
.Einfachste Lösung für diesen Fehler: Ersetzen Sie den Code über den Zeilen, in denen
firstDayOfWeek
undlastDayOfWeek
wie folgt deklariert sind:Jetzt ist das Ergebnis:
quelle
&& !(bh.DayOfWeek == DayOfWeek.Sunday || bh.DayOfWeek == DayOfWeek.Saturday)
Andernfalls würde derselbe Tag zweimal abgezogen, wenn ein Feiertag auf ein Wochenende fällt.OK. Ich denke, es ist Zeit, die richtige Antwort zu posten:
Originalquelle:
http://alecpojidaev.wordpress.com/2009/10/29/work-days-calculation-with-c/
PS-Lösungen, die oben veröffentlicht wurden, machen mich aus irgendeinem Grund sic.
quelle
Ich weiß, dass diese Frage bereits gelöst ist, aber ich dachte, ich könnte eine einfachere Antwort geben, die anderen Besuchern in Zukunft helfen könnte.
Hier ist meine Meinung dazu:
Dies war meine ursprüngliche Einreichung:
quelle
to > from
. Vielleicht ist das das Problem?Definieren Sie eine Erweiterungsmethode für DateTime wie folgt:
Die Verwendung befindet sich dann in einer Where-Klausel, um eine breitere Liste von Daten zu filtern:
quelle
Ich habe den folgenden Code verwendet, um auch Feiertage zu berücksichtigen:
Und Unit Tests:
quelle
Nun, das wurde zu Tode geschlagen. :) Allerdings werde ich noch eine andere Antwort geben, weil ich etwas anderes brauchte. Diese Lösung unterscheidet sich darin, dass zwischen Start und Ende eine Geschäftszeitspanne zurückgegeben wird. Sie können die Geschäftszeiten des Tages festlegen und Feiertage hinzufügen. Sie können damit berechnen, ob es innerhalb eines Tages, über Tage, über Wochenenden und sogar Feiertage geschieht. Und Sie können nur die Werktage abrufen oder nicht, indem Sie einfach das abrufen, was Sie vom zurückgegebenen TimeSpan-Objekt benötigen. Und wie es Listen von Tagen verwendet, können Sie sehen, wie einfach es wäre, die Liste der arbeitsfreien Tage hinzuzufügen, wenn es nicht der typische Sa und So ist. Und ich habe ein Jahr lang getestet, und es scheint super schnell zu sein.
Ich hoffe nur, dass das Einfügen des Codes korrekt ist. Aber ich weiß, dass es funktioniert.
Und hier ist Testcode : Beachten Sie, dass Sie diese Funktion nur in eine Klasse namens DateHelper einfügen müssen, damit der Testcode funktioniert.
quelle
Diese Lösung vermeidet Iterationen, funktioniert sowohl für + ve als auch für -ve Wochentagsunterschiede und enthält eine Unit-Test-Suite zur Regression gegen die langsamere Methode zum Zählen von Wochentagen. Ich habe auch eine prägnante Methode zum Hinzufügen von Wochentagen hinzugefügt, die ebenfalls auf dieselbe nicht iterative Weise funktioniert.
Unit-Tests decken einige tausend Datumskombinationen ab, um alle Start- / End-Wochentagskombinationen mit kleinen und großen Datumsbereichen ausführlich zu testen.
Wichtig : Wir gehen davon aus, dass wir Tage zählen, indem wir das Startdatum und das Enddatum ausschließen. Dies ist wichtig, wenn Sie Wochentage zählen, da die spezifischen Start- / Endtage, die Sie einschließen / ausschließen, das Ergebnis beeinflussen. Dies stellt auch sicher, dass die Differenz zwischen zwei gleichen Tagen immer Null ist und dass wir nur volle Arbeitstage einbeziehen, da Sie normalerweise möchten, dass die Antwort zu jedem Zeitpunkt am aktuellen Startdatum (häufig heute) korrekt ist und das vollständige Enddatum enthält (z ein Fälligkeitsdatum).
HINWEIS: Dieser Code muss für Feiertage zusätzlich angepasst werden. In Übereinstimmung mit der obigen Annahme muss dieser Code jedoch Feiertage am Startdatum ausschließen.
Wochentage hinzufügen:
Wochentagsdifferenz berechnen:
Ich fand heraus, dass die meisten anderen Lösungen zum Stapelüberlauf entweder langsam (iterativ) oder zu komplex waren und viele einfach falsch waren. Die Moral der Geschichte ist ... Vertraue ihr nicht, es sei denn, du hast sie gründlich getestet !!
Unit-Tests basierend auf kombinatorischen NUnit-Tests und der NUnit-Erweiterung von ShouldBe .
quelle
Hier ist ein Code für diesen Zweck mit schwedischen Feiertagen, aber Sie können anpassen, welche Feiertage gezählt werden sollen. Beachten Sie, dass ich ein Limit hinzugefügt habe, das Sie möglicherweise entfernen möchten, aber es war für ein webbasiertes System und ich wollte nicht, dass jemand ein großes Datum eingibt, um den Prozess zu blockieren
quelle
Hier ist ein kurzer Beispielcode. Es ist eine Klassenmethode und funktioniert daher nur innerhalb Ihrer Klasse. Wenn Sie möchten
static
, ändern Sie die Signatur inprivate static
(oderpublic static
).Diese Methode erstellt eine Schleifenvariable
d
, initialisiert sie auf den Starttag undsd
erhöht sie dann bei jeder Iteration um einen Tag (d = d.AddDays(1)
).Es gibt die gewünschten Werte mit zurück
yield
, wodurch ein erstellt wirditerator
. Das Coole an Iteratoren ist, dass sie nicht alle Werte desIEnumerable
In-Speichers enthalten, sondern nur nacheinander aufrufen. Dies bedeutet, dass Sie diese Methode von Anfang an aufrufen können, ohne sich Sorgen machen zu müssen, dass Ihnen der Speicher ausgeht.quelle
Ich habe viel nach einem leicht verdaulichen Algorithmus gesucht, um die Arbeitstage zwischen zwei Daten zu berechnen und auch die Nationalfeiertage auszuschließen, und schließlich habe ich mich für diesen Ansatz entschieden:
Grundsätzlich wollte ich mit jedem Datum meine Bedingungen bewerten:
aber ich wollte auch vermeiden, dass Daten wiederholt werden.
Durch Ausführen und Messen der Zeit, die für die Bewertung eines vollen Jahres benötigt wird, erhalte ich das folgende Ergebnis:
Bearbeiten: eine neue Methode einfacher:
quelle
Ich denke, keine der obigen Antworten ist tatsächlich richtig. Keiner von ihnen löst alle Sonderfälle, z. B. wenn das Datum in der Mitte eines Wochenendes beginnt und endet, wenn das Datum an einem Freitag beginnt und am nächsten Montag endet usw. Darüber hinaus runden sie die Berechnungen insgesamt ab Tage. Wenn das Startdatum beispielsweise in der Mitte eines Samstags liegt, wird ein ganzer Tag von den Arbeitstagen abgezogen, was zu falschen Ergebnissen führt ...
Wie auch immer, hier ist meine Lösung, die sehr effizient und einfach ist und für alle Fälle funktioniert. Der Trick besteht darin, nur den vorherigen Montag für Start- und Enddaten zu finden und dann eine kleine Entschädigung zu leisten, wenn Start und Ende am Wochenende stattfinden:
quelle
Funktioniert und ohne Schleifen
Diese Methode verwendet keine Schleifen und ist eigentlich recht einfach. Es erweitert den Datumsbereich auf volle Wochen, da wir wissen, dass jede Woche 5 Werktage hat. Anschließend wird anhand einer Nachschlagetabelle die Anzahl der Werktage ermittelt, die vom Anfang und Ende abgezogen werden müssen, um das richtige Ergebnis zu erzielen. Ich habe die Berechnung erweitert, um zu zeigen, was los ist, aber das Ganze könnte bei Bedarf in einer einzigen Zeile zusammengefasst werden.
Wie auch immer, das funktioniert bei mir und deshalb dachte ich, ich würde es hier posten, falls es anderen helfen könnte. Viel Spaß beim Codieren.
Berechnung
Kultur
Der Code geht von einer Arbeitswoche von Montag bis Freitag aus. Für andere Kulturen, wie z. B. Sonntag bis Donnerstag, müssen Sie die Daten vor der Berechnung ausgleichen.
Methode
quelle
Ich werde nur meine Lösung teilen. Es hat bei mir funktioniert, vielleicht merke ich einfach nicht, dass es einen Fehler gibt. Ich begann damit, die erste unvollständige Woche zu bekommen, wenn es welche gibt. Eine vollständige Woche war von Sonntag bis Samstag. Wenn also (int) _now.DayOfWeek nicht 0 (Sonntag) war, war die erste Woche unvollständig.
Ich subtrahiere nur 1 bis die Anzahl der ersten Wochen für den Samstag der ersten Woche und addiere sie dann zur neuen Anzahl.
Dann bekomme ich die letzte unvollständige Woche, dann subtrahiere ich 1 für den Sonntag und addiere dann zu der neuen Zählung.
Dann wurde schließlich die Anzahl der vollständigen Wochen multipliziert mit 5 (Wochentage) zur neuen Zählung hinzugefügt.
quelle
Ich hatte Probleme, eine solide TSQL-Version dieses Codes zu finden. Im Folgenden finden Sie im Wesentlichen eine Konvertierung des C # -Codes hier mit der Feiertagstabelle, die zur Vorberechnung von Feiertagen verwendet werden sollte.
quelle
quelle
Hier ist eine sehr einfache Lösung für dieses Problem. Wir haben Startdatum, Enddatum und "for-Schleife", um den Tag zu vergrößern und zu berechnen, ob es sich um einen Arbeitstag oder ein Wochenende handelt, indem wir in den String DayOfWeek konvertieren.
quelle
Basierend auf dem Kommentar, der als empfohlene Antwort und der empfohlene Patch markiert ist, sowie -> Diese Version möchte die Tage in Geschäftszeiten konvertieren ... Berücksichtigt auch die gleichen Tagesstunden.
quelle
quelle
Ich habe gerade die Antwort von @Alexander und @Slauma verbessert, um eine Geschäftswoche als Parameter zu unterstützen, für Fälle, in denen Samstag ein Geschäftstag ist, oder sogar für Fälle, in denen nur einige Wochentage als Geschäftstage gelten:
quelle
Hier ist die Funktion, mit der wir Geschäftstage zwischen zwei Daten berechnen können. Ich verwende keine Urlaubsliste, da diese je nach Land / Region variieren kann.
Wenn wir es trotzdem verwenden möchten, können wir das dritte Argument als Liste der Feiertage verwenden. Bevor wir die Anzahl erhöhen, sollten wir überprüfen, ob die Liste nicht d enthält
quelle
Ich glaube, das könnte einfacher sein:
quelle
Hier ist noch eine weitere Idee: Mit dieser Methode können Sie jede Arbeitswoche und jeden Feiertag angeben.
Die Idee hier ist, dass wir den Kern des Datumsbereichs vom ersten ersten Arbeitstag der Woche bis zum letzten Wochenendtag der Woche finden. Dies ermöglicht es uns, die gesamten Wochen einfach zu berechnen ( ohne alle Daten zu durchlaufen). Alles, was wir dann tun müssen, ist, die Arbeitstage hinzuzufügen, die vor dem Beginn und dem Ende dieses Kernbereichs liegen.
quelle
Da kann ich nicht kommentieren. Es gibt noch ein Problem mit der akzeptierten Lösung, bei der Feiertage abgezogen werden, selbst wenn sie am Wochenende liegen. Wenn man sieht, wie andere Eingaben überprüft werden, ist es nur passend, dass dies auch so ist.
Der foreach sollte daher sein:
quelle
Hier ist ein Ansatz, wenn Sie MVC verwenden. Ich habe auch Nationalfeiertage oder Festtage berechnet, die ausgeschlossen werden sollen, indem ich sie aus dem Feiertagskalender abrufe, den Sie benötigen, um einen zu erstellen.
Leavemodel ist eine Liste hier
quelle
Hier ist eine Hilfsfunktion, die ich für diese Aufgabe geschrieben habe.
Über den
out
Parameter wird auch die Anzahl der Wochenenden zurückgegeben .Wenn Sie möchten, können Sie die "Wochenend" -Tage zur Laufzeit für Länder anpassen, die unterschiedliche Wochenendtage verwenden, oder Feiertage über den
weekendDays[]
optionalen Parameter einschließen :quelle
Ich habe die folgende Lösung gefunden
quelle
Sie müssen nur jeden Tag im Zeitbereich durchlaufen und einen Tag vom Zähler abziehen, wenn es sich um einen Samstag oder einen Sonntag handelt.
quelle
Ich poste dies nur, weil trotz all der hervorragenden Antworten, die gegeben wurden, keine der Berechnungen für mich Sinn machte. Dies ist definitiv eine KISS-Methode, die funktionieren und ziemlich wartbar sein sollte. Zugegeben, wenn Sie Bereiche berechnen, die länger als 2-3 Monate sind, ist dies nicht der effektivste Weg. Wir bestimmen einfach, ob es ein Samstag oder Sonntag ist oder ob das Datum ein bestimmtes Feiertagsdatum ist. Wenn nicht, fügen wir einen Werktag hinzu. Wenn ja, ist alles in Ordnung.
Ich bin sicher, dass dies mit LINQ noch einfacher sein könnte, aber dieser Weg ist viel einfacher zu verstehen.
quelle
Ein weiterer Ansatz zur Berechnung von Geschäftstagen, bei dem keine Feiertage berücksichtigt werden, sondern die Tageszeit berücksichtigt wird, die einen Bruchteil der Tage zurückgibt:
Hier können Sie herumspielen: https://rextester.com/ASHRS53997
quelle
Dies ist eine generische Lösung.
startdayvalue ist die Tagesnummer des Startdatums.
Wochenendtag_1 ist die Tagesnummer des Wochenendes.
Tagesnummer - MON - 1, DI - 2, ... SA - 6, SONN -7.
Unterschied ist Unterschied zwischen zwei Daten ..
Beispiel: Startdatum: 4. April 2013, Enddatum: 14. April 2013
Differenz: 10, Starttagwert: 4, Wochenendtag_1: 7 (wenn SONNTAG ein Wochenende für Sie ist.)
Dies gibt Ihnen die Anzahl der Feiertage.
Anzahl der Werktage = (Differenz + 1) - Feiertag1
quelle