Werk- en feestdagen

Access kent geen functie om het aantal werkdagen tussen twee datums te berekenen. Daar moeten we dus zelf iets voor in elkaar draaien. Een bijkomend probleem is dat we niet alleen rekening moeten houden met zater- en zondagen, maar ook met diverse feestdagen. Een aantal van die feestdagen kent bovendien geen vaste datum.

We gaan eerst de kwestie van de variabele (christelijke) feestdagen (Goede Vrijdag, Pasen, Hemelvaartdag, Pinksteren) oplossen. Een gelukkige bijkomstigheid is dat al deze dagen een bepaald aantal dagen voor of na Pasen vallen. Weten we wanneer het Pasen is, dan weten we de rest ook. Er bestaat een berekening die op basis van het jaartal de datum waar eerste paasdag op valt uitrekent. Vraag me niet die berekening uit leggen, maar als we hem in de vorm van een functie gieten ziet hij er zo uit:

Function Pasen(Jaar As Integer) As Date
Dim A As Byte
Dim B As Byte
Dim C As Byte
Dim D As Byte
Dim E As Byte

A = Jaar Mod 19
B = Int(Jaar / 100)
C = Int((3 * B - 5) / 4)
D = (Int(12 + 11 * A + (8 * B + 13) / 25 - C) Mod 30 + 30) Mod 30

If 11 * D < A + 1 Then E = 56 - D Else E = 57 - D End If

Pasen = DateValue("01/03/" & Str(Jaar)) + E - 1 - Int(E + (5 * Jaar) / 4 - C) Mod 7
End Function

Met deze functie als basis kunnen we eenvoudig uitrekenen wanneer het Goede Vrijdag is (twee dagen voor eerste paasdag), wanneer Hemelvaartsdag (39 dagen na eerste paasdag) en wanneer Pinksteren (7 weken na Pasen).

De overige feestdagen (Nieuwjaar, Koninginnedag, Bevrijdingsdag en Kerst) vallen op vaste dagen. Gegeven een bepaalde datum is dus eenvoudig vast te stellen of dit een feestdag is door naar de dag en de maand te kijken.

Ook zaterdagen en zondagen zijn simpel te detecteren. Met behulp van de functie DatePart is vast te stellen op welke weekdag een bepaalde datum valt.

We kunnen nu een functie maken die gegeven een bepaalde datum aangeeft of die datum al dan niet een ZZF-dag (zaterdag, zondag of feestdag) is:

Function IsZZFdag(Datum As Date) As Boolean
Dim Jaar As Integer
Dim TDag As String
Dim Pa1 As Date

IsZZFdag = False

'Controleren of de datum een zaterdag of een zondag is
If DatePart("w", Datum) = 1 Or _
  DatePart("w", Datum) = 7 Then
  IsZZFdag = True
  GoTo Einde
End If

'Controleren op feestdagen met een vaste datum
TDag = Format(Datum, "dd-mm")

If TDag = "01-01" Then
  IsZZFdag = True
  GoTo Einde
End If

If TDag = "30-04" Then
  IsZZFdag = True
  GoTo Einde
End If

If TDag = "05-05" Then
  IsZZFdag = True
  GoTo Einde
End If

If TDag = "25-12" Then
  IsZZFdag = True
  GoTo Einde
End If

If TDag = "26-12" Then
  IsZZFdag = True
  GoTo Einde
End If

'Bepalen eerste paasdag
Jaar = DatePart("yyyy", Datum)
Pa1 = Pasen(Jaar)

'Controle goede vrijdag
If (Datum = Pa1 - 2) Then
  IsZZFdag = True
  GoTo Einde
End If

'Controle tweede paasdag
If (Datum = Pa1 + 1) Then
  IsZZFdag = True
  GoTo Einde
End If

'Controle hemelvaartdag
If (Datum = Pa1 + 39) Then
  IsZZFdag = True
  GoTo Einde
End If

'Controle tweede pinksterdag
If (Datum = Pa1 + 50) Then
  IsZZFdag = True
  GoTo Einde
End If

Einde:
End Function

Zoals je ziet stoppen we de functie zodra vastgesteld is dat een dag een ZZF-dag is. Omdat we in het begin al op zondagen controleren, hoeven we eerste paasdag en eerste pinksterdag niet na te gaan; dat zijn immers zondagen en die zijn eerder al als ZZF-dag aangemerkt.
In de functie kan je desgewenst stukjes code weghalen als een bepaalde dag in jouw geval niet als feestdag aangemerkt mag worden (bijvoorbeeld Goede Vrijdag of 5 mei). Je verwijdert dan simpleweg het betreffende IF-statement.

Nu we een functie hebben om te bepalen of een dag al dan niet een ZZF-dag is, kunnen we die gebruiken om een functie te maken die uitrekent hoeveel werkdagen (niet-ZZF-dagen) er in een bepaalde periode vallen:

Function AantalWerkdagen(Van As Date, Tot As Date) As Integer
Dim Tel As Integer
Dim Datum As Date

Tel = 0
Datum = Van
Do While Datum <= Tot
  If Not IsZZFdag(Datum) Then
    Tel = Tel + 1
  End If

  Datum = DateAdd("d", 1, Datum)
Loop

AantalWerkdagen = Tel
End Function

De functie loopt alle dagen in de opgegeven periode af en bepaalt met behulp van de hiervoor beschreven functie IsZZFdag per dag of het al dan niet een ZZF-dag is. In een teller wordt het aantal werkdagen bijgehouden en aan het einde als waarde van de functie geretourneerd.

Op een vergelijkbare manier kunnen we een functie maken die bij een datum een aantal werkdagen optelt en ons vertelt welke datum het dan is:

Function PlusWerkdagen(Van As Date, Werkdagen As Integer) As Date
Dim Tel As Integer
Dim Datum As Date

Datum = Van
Tel = Werkdagen

Do While Tel <> 0
  Datum = DateAdd("d", 1, Datum)
  If Not IsZZFdag(Datum) Then
    Tel = Tel - 1
  End If
Loop

PlusWerkdagen = Datum
End Function

Een voorbeelddatabase waarin dit alles is verwerkt kun je hier downloaden.

 

Uitbreiding met vakantieperiodes
Ik heb een uitbreiding gemaakt op het bovenstaande. In de uitgebreidere versie kan je ook vakantieperiodes opgeven (door middel van een begin- en einddatum). Die worden in een tabel opgeslagen. Bij het bepalen of een dag al dan niet een werkdag is wordt ook naar de vakantieperiodes gekeken.

De functie IsZZFdag heb ik daartoe uitgebreid met het volgende stukje code (en hernoemd tot IsZZFVdag):

'Controleren op vakantie dagen
hDat = Format(Datum, "mm-dd-yyyy")
If Not IsNull(DLookup("VAN", "Vakantie", "VAN<=#" & hDat & "# AND TEM>=#" & hDat & "#")) Then
  IsZZFVdag = True
  GoTo Einde
End If

De uitgebreide voorbeelddatabase kun je hier downloaden.