[.NET C#] Werkdagen

Pagina: 1
Acties:
  • 722 views sinds 30-01-2008
  • Reageer

  • vinnux
  • Registratie: Maart 2001
  • Niet online
Een van de "rampen" die ik tegenkom is het uitrekenen van werkdagen.
Hieronder volgt een definitie van werkdagen die wij hanteren.
Artikel 3 van de Algemene Termijnenwet
• Algemeen erkende feestdagen in de zin van deze wet zijn: de Nieuwjaarsdag, de Christelijke tweede Paas- en
Pinksterdag, de beide Kerstdagen, de Hemelvaartsdag, de dag waarop de verjaardag van de Koning wordt gevierd
en de vijfde mei.
• Voor de toepassing van deze wet wordt de Goede Vrijdag met de in het vorige lid genoemde dagen gelijkgesteld
• Wij (red: de Koning) kunnen bepaalde dagen voor de toepassing van deze wet met de in het eerste lid genoemde
gelijkstellen. Ons besluit wordt in de Nederlandse Staatscourant openbaar gemaakt.
Als extra regel wordt er gesteld dat een werkdag van 00:00-24:00 loopt. Zonder deze regeling wordt het allemaal weer wat ingewikkelder.

Onderstaanden code rekent werkdagen uit:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
rotected static double calculateWaitTime(DateTime startDate, DateTime stopDate){
            double workDays = 0.0d;
      Console.WriteLine("START_DATE:"+startDate.ToLongDateString()+" "+startDate.ToLongTimeString());   
            Console.WriteLine("START_DATE:"+stopDate.ToLongDateString()+" "+stopDate.ToLongTimeString());   
            //Move startDate to Monday when it is a Saterday or Sunday 
            if(startDate.DayOfWeek == DayOfWeek.Sunday) 
                startDate = startDate.Add(new TimeSpan(1,-1*startDate.Hour,-1*startDate.Minute,-1*startDate.Second, -1*startDate.Millisecond));
            else if (startDate.DayOfWeek == DayOfWeek.Saturday) 
                startDate = startDate.Add(new TimeSpan(2,-1*startDate.Hour,-1*startDate.Minute,-1*startDate.Second, -1*startDate.Millisecond));
            //Move stopDate to Monday when it is a Saterday or Sunday
            if(stopDate.DayOfWeek == DayOfWeek.Sunday) 
                stopDate = stopDate.Add(new TimeSpan(1,-1*startDate.Hour,-1*startDate.Minute,-1*startDate.Second, -1*startDate.Millisecond));
            else if (stopDate.DayOfWeek == DayOfWeek.Saturday) 
                stopDate = stopDate.Add(new TimeSpan(2,-1*startDate.Hour,-1*startDate.Minute,-1*startDate.Second, -1*startDate.Millisecond));
            Console.WriteLine("START_DATE:"+startDate.ToLongDateString()+" "+startDate.ToLongTimeString()); 
            Console.WriteLine("START_DATE:"+stopDate.ToLongDateString()+" "+stopDate.ToLongTimeString());   


            //When startDate < stopDate switch dates for calculation
            DateTime tmp = stopDate;
            bool reverse = false;
            if(stopDate < startDate){
                stopDate = startDate;
                startDate = tmp;
                reverse = true;
            }

            double days = (double)(stopDate.Ticks - startDate.Ticks)/TimeSpan.TicksPerDay;
            double wholeWeeks = Math.Floor(days / 7.0d);            
            
            workDays += wholeWeeks * 5;
            workDays += days % 7; 
            
            //When the stopDate weekday < startDate weekday distract an extre weekend
            if(((int)(stopDate.DayOfWeek+1)%7) < ((int)(startDate.DayOfWeek+1)%7))
                workDays -= 2.0d;
            
            //When startDate < stopDate workdays is negative
            if(reverse)
                workDays *= -1;

            return workDays;
        }

Zoals je ziet houd ik geen rekening met feestdagen etc, dit wordt echt te veel.
Of hebben jullie een leuke oplossing?

Nu vraag ik mij af of er geen betere manier is om werkdagen te berekenen.
Is er bijvoorbeeld niet een bepaald Calender object wat dit kan doen?
Wat is jullie gedachten over het berekenen van werkdagen?

[ Voor 8% gewijzigd door vinnux op 22-06-2005 13:21 ]


  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 20-10-2025

D4Skunk

Kind of Blue

Dit probleem kan je volgens mij beter Set-based aanpakken, oftewel in sql.

Maak een (virtuele) array enumeratie van alle geldige werkdagen, en gebruik deze om je vooruitgang te tellen.

In .net 2.0 kan je voor een virtuele array enumeratie trouwens het yield-statement gebruiken.


zoiets ( itereren over dagen)

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    bool IsWerkDag(DateTime dt) {
        return (!IsFeestDag(dt) ||
                !IsWeekendDag(dt));
    }
    
    void WerkTermijn(ref DateTime startDatum,ref DateTime eindDatum) {
      int termijn = eindDatum-startDatum;
      while (!IsWerkDag(startDatum)) startDatum++;
      eindDatum=startdatum;
      while (termijn>0)
      {  while (!IsWerkDag(eindDatum)) eindDatum++;
         termijn--;
      }
    }

[ Voor 48% gewijzigd door D4Skunk op 21-06-2005 15:13 ]


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 06-10-2025

pjvandesande

GC.Collect(head);

Tip:

C#:
1
2
3
4
5
           //When startDate < stopDate workdays is negative
            if(reverse)
                workDays *= -1;

            return workDays;


Mag worden:

C#:
1
2
3
4
5
6
7
8
9
           //When startDate < stopDate workdays is negative
            if(reverse)
            {
                        return -workDays;
            }
            else
            {
                        return workDays;
            }

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
questa schreef op dinsdag 21 juni 2005 @ 15:46:
Tip:

C#:
1
2
3
4
5
           //When startDate < stopDate workdays is negative
            if(reverse)
                workDays *= -1;

            return workDays;


Mag worden:

C#:
1
2
3
4
5
6
7
8
9
           //When startDate < stopDate workdays is negative
            if(reverse)
            {
                        return -workDays;
            }
            else
            {
                        return workDays;
            }
C# is nou niet echt mijn kaasje, maar kan dit niet (of zoiets dergelijks)?
C#:
1
return (reverse) ? -workDays : workDays

:?
Verder, als ik het aantal werkdagen onder VB wil hebben doe ik dat als volgt (pseudo):
code:
1
2
werkdagen = datediff(d,dateA,dateB) - (datediff(ww,dateA,dateB) * 2)
werkdagen = werkdagen - aantFeestdagen

Reken het aantal dagen verschil uit tussen A en B en trek daar het (aantal weken * 2) van af (weekends). Vervolgens hoef je alleen nog maar te weten HOEVEEL feestdagen tussen A en B liggen (tabelletje ofzo) en die er ook nog van af te trekken.
Let op met het aantal dagen dat je voor de weekends af trekt (het kan zijn dat je een weekend teveel of te weinig aftrekt, afhankelijk van hoe ver je in de week zit en hoever je terug moet).

[ Voor 36% gewijzigd door RobIII op 21-06-2005 15:59 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 13:03

mulder

ik spuug op het trottoir

C#:
1
return (reverse) ? -workDays : workDays
Dat is voor linux nerds en/of mensen die gebrek hebben aan lege regels ;)

oogjes open, snaveltjes dicht


  • sig69
  • Registratie: Mei 2002
  • Laatst online: 14:01
Als ik je vakantie dagen optel zijn dat er welgeteld 8. Hoe moelijk is het om te controleren of die dagen voorkomen binnen een bepaalde periode en er eventueel vanaf te trekken?

Roomba E5 te koop


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Effe wat VB Code (Getest met 1000+ random date's):
Visual Basic:
1
2
3
4
5
6
Private Function WorkDays(ByVal dtA As Date, ByVal dtB As Date) As Long
    Dim lWeekends As Long
    lWeekends = DateDiff("ww", dtA, dtB) * 2
    lWeekends = lWeekends + IIf(DateAdd("d", lWeekends, dtA) > dtB, 1, 0)
    WorkDays = DateDiff("d", dtA, dtB) - lWeekends + 1
End Function

Zorg wel dat:
• dtA >= dtB (en anders effe swappen)
• Beide data niet in een weekend liggen (en anders daarop corrigeren)
• Vervolgens even het aantal "vakantiedagen" tussen die twee data aftrekken et voila.

Scheelt een hoop ranzige lussen etc. Helaas is dit VB code, maar dat moet niet al te moeilijk om te zetten zijn naar C# lijkt me zo.

[ Voor 32% gewijzigd door RobIII op 21-06-2005 16:54 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 06-10-2025

pjvandesande

GC.Collect(head);

RobIII schreef op dinsdag 21 juni 2005 @ 15:50:
[...]

C# is nou niet echt mijn kaasje, maar kan dit niet (of zoiets dergelijks)?
C#:
1
return (reverse) ? -workDays : workDays
Dat kan inderdaad ook. :)

  • Orphix
  • Registratie: Februari 2000
  • Niet online
Ik zou het zo aanpakken (voor zover ik je vraagstelling goed heb begrepen):
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private int NrOfWorkingDays(DateTime startDate, DateTime endDate)
    {
        // in het geval dat de einddatum voor de startdatum ligt zijn het aantal werkdagen negatief
        if (startDate > endDate)
            return -NrOfWorkingDays(endDate, startDate);

        TimeSpan interval = endDate - startDate;
        int nrOfDays = interval.Days;
        int nrOfWorkingDays = 0;
        
        // operator <= want ook de laatste dag telt mee
        for (int i = 0; i <= nrOfDays; i++)
        {
            // bereken de nieuwe datum om te controleren op geldigheid als werkdag
            DateTime dateToCheck = startDate.Add(new TimeSpan(i, 0, 0, 0, 0));

            if(IsWeekendDay(dateToCheck) == false && IsHoliday(dateToCheck) == false)
                nrOfWorkingDays++;
        }

        return nrOfWorkingDays;
    }

    private bool IsWeekendDay(DateTime date)
    {
        return date.DayOfWeek == DayOfWeek.Sunday || date.DayOfWeek == DayOfWeek.Saturday;
    }

    private bool IsHoliday(DateTime date)
    {
        //TODO: voeg meer cases toe        
        return ((date.Month == 5 && date.Day == 5)      // 5 mei
            || (date.Month == 12 && date.Day == 25)     // eerste kerstdag
            || (date.Month == 12 && date.Day == 26)     // tweede kerstdag
            || (date.Month == 12 && date.Day == 31)     // oudjaar
            || (date.Month == 1 && date.Day == 1));     // nieuwjaar
    }

Deze code is imho overzichtelijker en makkelijker leesbaar. Misschien dat ik het probleem te simpel opvat maar ik itereer gewoon over de dagen en tel alleen die dagen die geen weekenden of feestdagen zijn.

  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 20-10-2025

D4Skunk

Kind of Blue

of zo :p

C#:
1
return abs(startdate-stopdate)

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Ik snap niet waarom iedereen gaat zitten itereren. Kent C# geen DateDiff / DateAdd enzo? :?
Waarom zou je zo knullig gaan zitten "tellen"? Dat is toch een vette performance kill als je 10.000+ keer (voor een recordset ofzo bijvoorbeeld) het aantal werkdagen moet uittellen? En zeker als het verschil nogal groot is tussen datum A en datum B.
Of redeneren we tegenwoordig allemaal maar gewoon "ach, de hardware is snel genoeg"?

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 20-10-2025

D4Skunk

Kind of Blue

RobIII schreef op dinsdag 21 juni 2005 @ 17:17:
Ik snap niet waarom iedereen gaat zitten itereren. Kent C# geen DateDiff / DateAdd enzo? :?
Waarom zou je zo knullig gaan zitten "tellen"? Dat is toch een vette performance kill als je 10.000+ keer (voor een recordset ofzo bijvoorbeeld) het aantal werkdagen moet uittellen? En zeker als het verschil nogal groot is tussen datum A en datum B.
Of redeneren we tegenwoordig allemaal maar gewoon "ach, de hardware is snel genoeg"?
Wanner het bv op de client maar een beperkt aantal keer zou moeten uitgerekend worden, zou ik kiezen voor iteratie, gezien die meestal eenvoudiger te volgen is, en dus ook minder gevoelig aan bugs etc...
Wanneer het gaat over een grote hoeveelheid berekeningen, zou ik sowiezo voor SQL gaan, dan heb je het minste performantieverlies.

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Als je uberhaupt met SQL werkt wel ja. En wat als ik gewoon een flat-file heb ofzo? Eerst SQL installeren, vervolgens de gegevens daar in gooien en dan SQL alles maar laten uitrekenen :?
Ik bedoel dus: Als je dit soort dingen in je app uitrekent vind ik het persoonlijk geen excuus om te gaan zitten itereren (tenzij je het maar 3x gebruikt in je app om het verschil tussen 2 dichtbijelkaar liggende datums uit te kienen). Gebruik je het echter in een veelgebruikte routine of liggen de datums regelmatig wél ver uit elkaar dan vind ik dat je 't dient uit te kienen zonder itereren.

Ik heb even een poging ondernomen, maar hij's nog niet helemaal goed:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void Main(string[] args)
{
    DateTime dtA = new DateTime(2005,06,1);
    DateTime dtB = new DateTime(2005,06,20);
    TimeSpan tsR = dtB - dtA;

    Int32 lWeekends = ((int)tsR.TotalDays / 7);
    lWeekends += (dtA.AddDays(lWeekends*7)  < dtB) && (tsR.TotalDays >= 7) ? 1 : 0;
    lWeekends *= 2;

    lWeekends += (dtA.AddDays(lWeekends)>dtB) ? 1 : 0;
    Console.WriteLine ((int)tsR.TotalDays - lWeekends + 1);
    Console.ReadLine();
}

Ten eerste heb ik nog nooit iets gedaan met C#, en ten tweede ben ik niet bekend met alle functies die in .Net zitten. Dus wellicht doe ik iets stoms, wellicht doe ik het compleet verkeerd maar dit komt toch al aardig in de buurt en scheelt dus een boel iteraties... Vermits je de puntjes van mijn vorige post in de gaten houdt. En ja, het gaat nog fout in een aantal gevallen...
Met een beetje denkwerk moet deze functie prima te maken zijn lijkt me (hoewel ik inderdaad geen DateDiff voor C# kan vinden).

/edit:
Hmmmm, bij nader inzien gaat het in de meeste gevallen fout :P Hoe dan ook moet dit anders op te lossen zijn dan itereren.

[ Voor 28% gewijzigd door RobIII op 21-06-2005 18:02 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Orphix
  • Registratie: Februari 2000
  • Niet online
RobIII schreef op dinsdag 21 juni 2005 @ 17:17:
Ik snap niet waarom iedereen gaat zitten itereren. Kent C# geen DateDiff / DateAdd enzo? :?
Waarom zou je zo knullig gaan zitten "tellen"? Dat is toch een vette performance kill als je 10.000+ keer (voor een recordset ofzo bijvoorbeeld) het aantal werkdagen moet uittellen? En zeker als het verschil nogal groot is tussen datum A en datum B.
Of redeneren we tegenwoordig allemaal maar gewoon "ach, de hardware is snel genoeg"?
De oplossing die jij hebt gegeven is nog steeds geen oplossing, omdat het bijvoorbeeld niet rekening houdt met feestdagen. Dit is juist de reden waarom je vrij snel moet gaan itereren. Het is niet in een enkele simpele formule te berekenen. Een ander voordeel is dat je erg flexibel bent. Zo kan je vrij gemakkelijk koopzondagen, of vakanties inplannen.
Ik vermoed zelf niet dat deze methode duizenden keren wordt aangesproken. Maar zelfs al zou dat wel zo zijn, de regel blijft om niet vooraf te optimaliseren. Kijk of het echt een probleem is, en begin danpas methodes efficienter te maken.

  • gorgi_19
  • Registratie: Mei 2002
  • Laatst online: 09:25

gorgi_19

Kruimeltjes zijn weer op :9

Orphix schreef op dinsdag 21 juni 2005 @ 19:05:
[...]

De oplossing die jij hebt gegeven is nog steeds geen oplossing, omdat het bijvoorbeeld niet rekening houdt met feestdagen. Dit is juist de reden waarom je vrij snel moet gaan itereren. Het is niet in een enkele simpele formule te berekenen. Een ander voordeel is dat je erg flexibel bent. Zo kan je vrij gemakkelijk koopzondagen, of vakanties inplannen.
Ik vermoed zelf niet dat deze methode duizenden keren wordt aangesproken.
Rekening houden met feestdagen lijkt me geen probleem. Controleer of een feestdag een werkdag is of niet en pas evt. het aantal dagen aan. :) Zo hoef je maar een klein rijtje te doorlopen :)
Maar zelfs al zou dat wel zo zijn, de regel blijft om niet vooraf te optimaliseren. Kijk of het echt een probleem is, en begin danpas methodes efficienter te maken
Neemt niet weg dat als er een efficient algoritme is, zonder veel extra werk, die geimplementeerd kan worden, deze de voorkeur verdiend, imho :)


Voor de topicstarter: zie ook http://www.codeproject.com/csharp/DateTimeLib.asp :)

[ Voor 24% gewijzigd door gorgi_19 op 21-06-2005 19:14 ]

Digitaal onderwijsmateriaal, leermateriaal voor hbo


  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Orphix schreef op dinsdag 21 juni 2005 @ 17:05:
Ik zou het zo aanpakken (voor zover ik je vraagstelling goed heb begrepen):
C#:
1
2
3
4
5
6
7
8
9
    private bool IsHoliday(DateTime date)
    {
        //TODO: voeg meer cases toe        
        return ((date.Month == 5 && date.Day == 5)      // 5 mei
            || (date.Month == 12 && date.Day == 25)     // eerste kerstdag
            || (date.Month == 12 && date.Day == 26)     // tweede kerstdag
            || (date.Month == 12 && date.Day == 31)     // oudjaar
            || (date.Month == 1 && date.Day == 1));     // nieuwjaar
    }

Deze code is imho overzichtelijker en makkelijker leesbaar. Misschien dat ik het probleem te simpel opvat maar ik itereer gewoon over de dagen en tel alleen die dagen die geen weekenden of feestdagen zijn.
Nadeel van dit soort code is dat sommige feestdagen niet op vaste datums vallen (goede vrijdag, pinksteren en pasen, e.a.). Overigens wordt oudjaar niet altijd als feestdag gezien ;)

  • Orphix
  • Registratie: Februari 2000
  • Niet online
gorgi_19 schreef op dinsdag 21 juni 2005 @ 19:12:
Neemt niet weg dat als er een efficient algoritme is, zonder veel extra werk, die geimplementeerd kan worden, deze de voorkeur verdiend, imho :)
Absoluut. Mits het de leesbaarheid en onderhoudbaarheid niet ondergraaft. Natuurlijk kan bovenstaande code worden geoptimaliseerd. Bijvoorbeeld door gebruik van een lookup table, of weet ik veel. Ik denk alleen wel als ik kijk naar de code van de TS, dat hij het voor zichzelf zo complex heeft gemaakt dat het niet meer is te overzien.
Maar ook hier geldt:
If you notice the algorithm, I have talked about holidays too, but I have not implemented this in any of the above code but that can be taken care of by a simple query. So I am leaving that up to you.
Mja zo kan ik het ook ;)

  • Orphix
  • Registratie: Februari 2000
  • Niet online
ACM schreef op dinsdag 21 juni 2005 @ 19:19:
[...]

Nadeel van dit soort code is dat sommige feestdagen niet op vaste datums vallen (goede vrijdag, pinksteren en pasen, e.a.). Overigens wordt oudjaar niet altijd als feestdag gezien ;)
Klopt, je zou deze functie dus een stuk kunnen uitbreiden / complexer kunnen maken. Ik weet de regels niet precies waarop ze die data baseren maar er zal ongetwijfeld voor elke feestdag een 'regel' zijn (1e maandag v/d maand, 3e dinsdag in september, etc). Je kan dit gewoon kwijt in deze functie. Nadeel zou kunnen zijn dat die (misschien) veels te traag wordt hierdoor. Maar dan nog geldt: meet het, en ga dan pas kijken naar lookup-tables, partitionering, of andere optimalisaties.
Die complexiteit zal er linksom of rechtsom toch in moeten komen (even van uitgaande dat je geen library gebruikt die dit voor je afhandelt), en dan heb ik het liever gecentraliseerd op een enkele plek.

Oh en oudjaar.. tja ben nog student he :9

[ Voor 4% gewijzigd door Orphix op 21-06-2005 19:29 ]


  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Hier worden die regels een beetje toegelicht. Ze hangen vrij sterk met elkaar samen.
[rml][ SQL] Tellen van aantal werkdagen tussen 2 datums.[/rml]

Als je veel lookups, maar vooral van een klein aantal jaren doet kan je natuurlijk alle feestdagen van dat jaar even voorcalculeren en met snelle hashtable/indexed/whatever-lookups de boel uitwerken.

  • __fred__
  • Registratie: November 2001
  • Laatst online: 09:39
Volgens mij is deze redelijk volledig:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
        private int bereken_werkdagen (DateTime van, DateTime tot) 
        {
            // van moet kleiner zijn dan of gelijk zijn aan tot
            Debug.Assert(van <= tot);

            // eventuele weekenddagen aan het begin worden eraf getrokken
            if (van.DayOfWeek == DayOfWeek.Saturday) { van = van.AddDays(2); };
            if (van.DayOfWeek == DayOfWeek.Sunday) { van = van.AddDays(1); };
            if (tot.DayOfWeek == DayOfWeek.Saturday) { tot = tot.AddDays(-1); };
            if (tot.DayOfWeek == DayOfWeek.Sunday) { tot = tot.AddDays(-2); };

            // Reken gewoon het aantal dagen uit tussen datum één en datum twee
            // de totdag wordt meegeteld!
            int werkdagen = (int)((tot.Ticks - van.Ticks) / TimeSpan.TicksPerDay) + 1;

            // haal de weekenden eraf      
            werkdagen -= (werkdagen / 7) * 2;
            
            // weekendje extra eraf als de van verder in de week is dan de tot
            if (van.DayOfWeek > tot.DayOfWeek)
            {
            werkdagen -= 2;
            }

            // nu een lus die per jaartal wordt uitgevoerd voor de feestdagen etc.

            for (int i = van.Year; i <= tot.Year; i++) 
            {
                // afhankelijk van in welk jaar we zitten is de ondergrens OF de van OF 1-1-<i>
                // respectievelijk is de bovengrens OF de tot OF 31-12-<i>
                DateTime ondergrens = (van.Year == i) ? van : new DateTime(i,1,1);  
                DateTime bovengrens = (tot.Year == i) ? tot : new DateTime(i,12,31);

                // nieuwjaar
                if (    ondergrens.Day == 1 && ondergrens.Month == 1 && 
                    ondergrens.DayOfWeek != DayOfWeek.Saturday &&       // 1e moet niet in een weekend vallen
                    ondergrens.DayOfWeek != DayOfWeek.Sunday
                    )
                {
                    werkdagen -= 1;
                }

                // doe de paasberekening
                DateTime paaszondag = Outin(i);

                // goede vrijdag, hemelvaart en pinksteren zijn van pasen afhankelijk
                DateTime goedevrijdag = paaszondag.AddDays(-2);
                DateTime hemelvaart = paaszondag.AddDays(39);
                DateTime pinksterzondag = paaszondag.AddDays(49);

                // paasmaandag
                if (    paaszondag.AddDays(1) >= ondergrens &&
                    paaszondag.AddDays(1) <= bovengrens
                    )
                {
                    werkdagen -= 1;
                }

                // goedevrijdag
                if (    goedevrijdag >= ondergrens &&
                    goedevrijdag <= bovengrens
                    )
                {
                    werkdagen -= 1;
                }

                // hemelvaartdonderdag
                if (    hemelvaart >= ondergrens &&
                    hemelvaart <= bovengrens
                    )
                {
                    werkdagen -= 1;
                }

                // pinkstermaandag
                if (    pinksterzondag.AddDays(1) >= ondergrens &&
                    pinksterzondag.AddDays(1) <= bovengrens
                    )
                {
                    werkdagen -= 1;
                }

                // verjaardag van de koningin
                DateTime koninginnedag = new DateTime(i,4,30);
                if (    koninginnedag >= ondergrens &&
                    koninginnedag <= bovengrens &&
                    koninginnedag.DayOfWeek != DayOfWeek.Saturday &&
                    koninginnedag.DayOfWeek != DayOfWeek.Sunday &&
                    // ff voorkomen dat we overlappende dagen dubbel tellen
                    koninginnedag != paaszondag.AddDays(1) &&           
                    koninginnedag != goedevrijdag &&
                    koninginnedag != hemelvaart &&
                    koninginnedag != pinksterzondag.AddDays(1)
                    )
                {
                    werkdagen -= 1;
                }

                // bevrijdingsdag
                DateTime bevrijdingsdag = new DateTime(i,5,5);
                if (    bevrijdingsdag >= ondergrens &&
                    bevrijdingsdag <= bovengrens &&
                    bevrijdingsdag.DayOfWeek != DayOfWeek.Saturday &&
                    bevrijdingsdag.DayOfWeek != DayOfWeek.Sunday &&
                    bevrijdingsdag != paaszondag.AddDays(1) &&          
                    bevrijdingsdag != goedevrijdag &&
                    bevrijdingsdag != hemelvaart &&
                    bevrijdingsdag != pinksterzondag.AddDays(1)
                    )
                {
                    werkdagen -= 1;
                }

                // kerst
                for (int j = 0; j <= 1; j++) // lusje voor eerste en tweede kerstdag
                {
                    DateTime kerstdag = new DateTime(i,12,25+j);
                    if (    kerstdag >= ondergrens &&
                        kerstdag <= bovengrens &&
                        kerstdag.DayOfWeek != DayOfWeek.Saturday &&
                        kerstdag.DayOfWeek != DayOfWeek.Sunday
                        )
                    {
                        werkdagen -= 1;
                    }
                }
            }

            return werkdagen;
        }

        private DateTime Outin (int jaar) 
        {
            // paasberekening van Outin, werkt voor elk jaar volgens de gregoriaanse kalender,
            // in tegenstelling tot Gauss die slechts werkt van 1900 tot 2099
            // http://www.ortelius.de/kalender/east_en.php

            int c = jaar / 100;
            int n = jaar - 19 * (jaar / 19);
            int k = (c - 17) / 25;
            int i1 = c - (c / 4) - (( c - k) / 3) + 19 * n + 15;
            int i2 = i1 - 30 * (i1 / 30);
            int i3 = i2 - (i2 / 28) * (1 - (i2 / 28) * (29 / (i2 + 1)) * ((21 - n) / 11));
            int a1 = jaar + (jaar / 4) + i3 + 2 - c + (c/4);
            int a2 = a1 - 7 * (a1 / 7);
            int l = i3 - a2;
            int m = 3 + ((l + 40) / 44);
            int d = l + 28 - 31 * ( m / 4);

            return new DateTime(jaar,m,d);
        }


Voor mensen met herhalingsallergie (elke programmeur in zekere mate ;-) kun je nog een functie IsWeekend() en ControleerGrenzen() maken om wat dubbele code weg te halen :)
Eventueel is een en ander natuurlijk wel om te bouwen naar een itererende functie, maar dan zou ik toch echt gebruik maken van een lookuptable om de berekening maar één keer uit te voeren.

[ Voor 21% gewijzigd door __fred__ op 22-06-2005 09:51 ]

Pagina: 1