;-- TIME_STAMP   2015-08-26 11:51:04   v 0.2

#include-once
#include <Math.au3>
#include <Date.au3>
#include <Array.au3>

;===============================================================================
; Function Name:   _GetWorkingDaysInRange
; Description::    Ermittelt die Anzahl an Arbeitstagen in einem übergebenen Datumsbereich, auch spezifisch nach Bundesland,
;                  auch Jahresübergreifend;
;                  Heiligabend und Silvester werden mit 0,5 Arbeitstagen berechnet;
;                  Anzahl der Wochenarbeitstage variabel (5-7)
; Parameter(s):    $sFromDate         Startdatum
;                  $sToDate           Enddatum
;                  $vFederalState     Index oder Kürzel des BundesLandes
;                                     0  BG  nur Bundesweit gesetzlich
;                                     1  SH  Schleswig-Holstein
;                                     2  HH  Freie und Hansestadt Hamburg
;                                     3  NI  Niedersachsen
;                                     4  HB  Freie Hansestadt Bremen
;                                     5  NW  Nordrhein-Westfalen
;                                     6  HE  Hessen
;                                     7  RP  Rheinland-Pfalz
;                                     8  BW  Baden-Württemberg
;                                     9  BY  Bayern
;                                    10  SL  Saarland
;                                    11  BE  Berlin
;                                    12  BB  Brandenburg
;                                    13  MV  Mecklenburg-Vorpommern
;                                    14  SN  Freistaat Sachsen|Sachsen
;                                    15  ST  Sachsen-Anhalt
;                                    16  TH  Thüringen
;                  $iCountWorkDays   Anzahl Arbeitstage pro Woche (5=Mo-Fr Standard; 6=Mo-Sa; 7=Mo-So)
; Requirement(s):  Funktionen: _Feiertage(), __IsWorkingDay()
; Return Value(s): Anzahl der Arbeitstage
; Author(s):       BugFix
;===============================================================================
Func _GetWorkingDaysInRange($sFromDate, $sToDate, $vFederalState=0, $iCountWorkDays=5)
	Local $aSplitFrom = StringSplit($sFromDate, '.', 2)
	Local $aSplitTo = StringSplit($sToDate, '.', 2)
	Local $aYears[1] = [$aSplitFrom[2]] ; für jahresübergreifende Daten
	If $aSplitTo[2] <> $aSplitFrom[2] Then
		ReDim $aYears[$aSplitTo[2]-$aSplitFrom[2]+1]
		For $i = 1 To UBound($aYears) -1
			$aYears[$i] = $aYears[$i-1] +1
		Next
	EndIf
	Local $aHolidays, $iWorkingDayCounter = 0, $n = 0, $sTmpDate
	Switch UBound($aYears)
		Case 1    ; Datumswerte in einem Jahr
			$aHolidays = _Feiertage($aYears[0],$vFederalState, 0, 0, 1)
			Do
				$sTmpDate = _DateAdd('D', $n, $aSplitFrom[2] & '/' & $aSplitFrom[1] & '/' & $aSplitFrom[0])
				$iWorkingDayCounter += __IsWorkingDay($aHolidays, $sTmpDate, $iCountWorkDays)
				$n += 1
			Until $aSplitTo[2] & '/' & $aSplitTo[1] & '/' & $aSplitTo[0] = $sTmpDate
		Case 2    ; Datumswerte in zwei Jahren
			$aHolidays = _Feiertage($aYears[0],$vFederalState, 0, 0, 1)
			Do
				$sTmpDate = _DateAdd('D', $n, $aSplitFrom[2] & '/' & $aSplitFrom[1] & '/' & $aSplitFrom[0])
				$iWorkingDayCounter += __IsWorkingDay($aHolidays, $sTmpDate, $iCountWorkDays)
				$n += 1
				If $aSplitFrom[2] & '/12/31' = $sTmpDate Then $aHolidays = _Feiertage($aYears[1],$vFederalState, 0, 0, 1)
			Until $aSplitTo[2] & '/' & $aSplitTo[1] & '/' & $aSplitTo[0] = $sTmpDate
		Case Else ; Datumswerte über mehr als 2 Jahre
			Local $tmpYear, $index = 0, $sDateEnd = $aSplitTo[2] & '/' & $aSplitTo[1] & '/' & $aSplitTo[0]
			$aHolidays = _Feiertage($aYears[$index],$vFederalState, 0, 0, 1)
			Do
				$sTmpDate = _DateAdd('D', $n, $aSplitFrom[2] & '/' & $aSplitFrom[1] & '/' & $aSplitFrom[0])
				$iWorkingDayCounter += __IsWorkingDay($aHolidays, $sTmpDate, $iCountWorkDays)
				$n += 1
				$tmpYear = StringLeft($sTmpDate, 4)
				If $tmpYear & '/12/31' = $sTmpDate And $tmpYear & '/12/31' <> $sDateEnd Then
					$index += 1
					$aHolidays = _Feiertage($aYears[$index],$vFederalState, 0, 0, 1)
				EndIf
			Until $sDateEnd = $sTmpDate
	EndSwitch
	Return $iWorkingDayCounter
EndFunc  ;==>_GetWorkingDaysInRange

Func __IsWorkingDay(ByRef $aHolidays, $sDate, $iCountWorkDays)
	; 5 Werktage, Sa/So frei; 6 Werktage, So frei; 7 Werktage, ./. frei
	Local $sFreeDay = $iCountWorkDays < 6 ? '6 7' : ($iCountWorkDays = 6 ? '7' : 'N')
	Local $aSplitDate = StringSplit($sDate, '/', 2)
	Local $iWeekday = _DateToDayOfWeekISO($aSplitDate[0], $aSplitDate[1], $aSplitDate[2])
	If @error Then Return 0
	If StringInStr($sFreeDay, $iWeekday) Then Return 0 ; ist Sa./So. = kein WorkingDay
	Local $index = _ArraySearch($aHolidays, $sDate, 0, 0, 1)
	If $index = -1 Then Return 1 ; nicht in Feiertagen (u. kein Sa./So.) = WorkingDay
	Local $sDay = StringRight($sDate, 5)
	If $sDay = '12/24' Or $sDay = '12/31' Then Return 0.5 ; Halber Feiertag (24./31. Dez.)
	Return 0 ; in Feiertagen = kein WorkingDay
EndFunc  ;==>__IsWorkingDay

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Funktion _Feiertage($Jahr)
;
;	gibt die Feiertage des übergebenen Jahres
;	als sortiertes Array[TT.MM.JJJJ od. JJJJ/MM/TT][Feiertag][Geltungsbereich][opt. Wochentag] zurück
;   Geltungsbereich  Leerstring - kein offizieller Feiertag
;                    BG - Bundesweit Gesetzlich
;                    Regionalkennzeichen: BB-Brandenburg  BE-Berlin  BW-Baden-Württemberg  BY-Bayern
;                                         HB-Bremen  HE-Hessen  HH-Hamburg  MV-Mecklenburg-Vorpommern
;                                         NI-Niedersachsen  NW-Nordrhein-Westfalen  RP-Rheinland-Pfalz
;                                         SH-Schleswig-Holstein  SL-Saarland  SN-Sachsen
;                                         ST-Sachsen-Anhalt  TH-Thüringen
;   $vBundesland -1  alle Feiertage mit Geltungsbereich (Bundesländer), (Standard)
;                 0  BG  nur Bundesweit gesetzlich
;
;                Bundesland (inkl. BG)
;                 1  SH  Schleswig-Holstein
;                 2  HH  Freie und Hansestadt Hamburg
;                 3  NI  Niedersachsen
;                 4  HB  Freie Hansestadt Bremen
;                 5  NW  Nordrhein-Westfalen
;                 6  HE  Hessen
;                 7  RP  Rheinland-Pfalz
;                 8  BW  Baden-Württemberg
;                 9  BY  Bayern
;                10  SL  Saarland
;                11  BE  Berlin
;                12  BB  Brandenburg
;                13  MV  Mecklenburg-Vorpommern
;                14  SN  Freistaat Sachsen|Sachsen
;                15  ST  Sachsen-Anhalt
;                16  TH  Thüringen
;	$DateTyp	 1 - Datum als TT.MM.JJJJ (Standard)
;                0 - Datum als JJJJ/MM/TT
;   $wDay        0 - Rückabe Wochentag numerisch  1-7 / Sonntag-Samstag
;                1 - Rückgabe Wochentag ($array[n][3])
;   $sort        0 - gegliedert fix/variabel (Standard), innerhalb sortiert
;                1 - komplett sortiert
;	Autor	BugFix (bugfix@autoit.de)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Func _Feiertage($year, $vBundesland=-1, $DateTyp=1, $wDay=0, $sort=0)
	Local $4AdvDat, $3AdvDat, $2AdvDat, $1AdvDat, $TotSoDat, $BuBDat, $MutterDat, $ErnteDat, $tmp
	Local $HDays[33][4], $a, $b, $c, $d, $e, $H1, $H2, $N, $M, $Tempyear, $RestJahr
	Local $aWDays[8] = [7,'Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Sonnabend']
	Local $aBuLand[17] = ['BG','SH','HH','NI','HB','NW','HE','RP','BW','BY','SL','BE','BB','MV','SN','ST','TH']
	If IsString($vBundesland) Then $vBundesland = _ArraySearch($aBuLand, $vBundesland)
;fixe Feiertage
	$HDays[0][0] = $year & "/01/01\Neujahr\BG"
	$HDays[1][0] = $year & "/01/06\Heilige Drei Könige\BW BY ST"
	$HDays[2][0] = $year & "/02/14\Valentinstag\"
	$HDays[3][0] = $year & "/05/01\Maifeiertag\BG"
	$HDays[4][0] = $year & "/08/15\Mariä Himmelfahrt\BY SL"
	$HDays[5][0] = $year & "/10/03\Tag der Deutschen Einheit\BG"
	$HDays[6][0] = $year & "/10/31\Reformationstag\BB MV SN ST TH"
	$HDays[7][0] = $year & "/11/01\Allerheiligen\BW BY NW RP SL"
	$HDays[8][0] = $year & "/12/24\Heiligabend\BG"
	$HDays[9][0] = $year & "/12/25\1. Weihnachtsfeiertag\BG"
	$HDays[10][0] = $year & "/12/26\2. Weihnachtsfeiertag\BG"
	$HDays[11][0] = $year & "/12/31\Silvester\BG"
;variable Feiertage
	$a = Mod($year, 19)
	$b = Mod($year, 4)
	$c = Mod($year, 7)
	$H1 = Int($year / 100)
	$H2 = Int($year / 400)
	$N = 4 + $H1 - $H2
	$M = 15 + $H1 - $H2 - Floor (Int((8 * $H1 + 13) / 25))
	$d = Mod((19 * $a + $M), 30)
	$e = Mod((2 * $b + 4 * $c + 6 * $d + $N), 7)
	$EasterDay = ($d + $e = 35) ? 50 : (($d = 28 And $e = 6 And $a > 10) ? 49 : 22 + $d + $e)
	$EasterMonth = '03'
	If $EasterDay > 31 Then
		$EasterDay -= 31
		$EasterMonth = '04'
	EndIf
	$EasterDay = StringRight("0" & $EasterDay, 2)
	If $year < 1900 Then ; Datumsoperationen nur mgl. wenn > 1900 , Jahr wird konvertiert
		$RestJahr = StringRight("0" & Mod($year, 100), 2)
		$Tempyear = _DateIsLeapYear($year) ? 20 & $RestJahr : 19 & $RestJahr
		$EasterDate = $Tempyear & "/" & $EasterMonth & "/" & $EasterDay
	Else
		$EasterDate = $year & "/" & $EasterMonth & "/" & $EasterDay
	EndIf
	$WFastDate = _DateAdd( 'd', -52, $EasterDate)
	$RosDat = _DateAdd( 'd', -48, $EasterDate)
	$FastDat = _DateAdd( 'd', -47, $EasterDate)
	$AschDat = _DateAdd( 'd', -46, $EasterDate)
	$GrDoDat = _DateAdd( 'd', -3, $EasterDate)
	$KarDat = _DateAdd( 'd', -2, $EasterDate)
	$OSaDat = _DateAdd( 'd', -1, $EasterDate)
	$OSoDat = $EasterDate
	$OMoDat = _DateAdd( 'd', 1, $EasterDate)
	$HiFaDat = _DateAdd( 'd', 39, $EasterDate)
	$PfSoDat = _DateAdd( 'd', 49, $EasterDate)
	$PfMoDat = _DateAdd( 'd', 50, $EasterDate)
	$FroDat = _DateAdd( 'd', 60, $EasterDate)
	; Ermitteln nicht von Ostern abhängiger, veränderlicher Feiertage
	; Muttertag = 2. Sonntag im Mai ABER wenn Pfingsten = 2.Sonntag im Mai dann ist Muttertag am 1. Sonntag
	; Der 2. Sonntag kann nur zw. dem 8. u. 14.5. liegen
	For $maitag = 8 To 14
		If _DateToDayOfWeek($year, 5, $maitag) = 1 Then
			$maitag = StringRight("0" & $maitag, 2)
			$MutterDat = $year & "/05/" & $maitag
			If $MutterDat = $PfSoDat Then
				$MutterDat = _DateAdd( 'd', -7, $year & "/05/" & $maitag)
			EndIf
			ExitLoop
		EndIf
	Next
	; Erntedankfest 1. Sonntag im Oktober (zw. 1. u. 7.10.)
	For $oktobertag = 1 To 7
		If _DateToDayOfWeek($year, 10, $oktobertag) = 1 Then
			$ErnteDat = $year & "/10/" & StringRight("0" & $oktobertag, 2)
			ExitLoop
		EndIf
	Next
	; 4.Advent = Sonntag vor 25.12. (zw. 18. u. 24.12.)
	For $deztag = 18 To 24
		If _DateToDayOfWeek($year, 12, $deztag) = 1 Then
			$4AdvDat = $year & "/12/" & $deztag
			$3AdvDat = _DateAdd( 'd', -7, $4AdvDat)
			$2AdvDat = _DateAdd( 'd', -14, $4AdvDat)
			$1AdvDat = _DateAdd( 'd', -21, $4AdvDat)
			$TotSoDat = _DateAdd( 'd', -28, $4AdvDat)
			$BuBDat = _DateAdd( 'd', -32, $4AdvDat)
			ExitLoop
		EndIf
	Next
	$HDays[12][0] = $WFastDate & "\Weiberfastnacht\"
	$HDays[13][0] = $RosDat & "\Rosenmontag\"
	$HDays[14][0] = $FastDat & "\Fastnacht\"
	$HDays[15][0] = $AschDat & "\Aschermittwoch\"
	$HDays[16][0] = $GrDoDat & "\Gründonnerstag\"
	$HDays[17][0] = $KarDat & "\Karfreitag\BG"
	$HDays[18][0] = $OSaDat & "\Ostersamstag\"
	$HDays[19][0] = $OSoDat & "\Ostersonntag\"
	$HDays[20][0] = $OMoDat & "\Ostermontag\BG"
	$HDays[21][0] = $HiFaDat & "\Christi Himmelfahrt\BG"
	$HDays[22][0] = $PfSoDat & "\Pfingstsonntag\"
	$HDays[23][0] = $PfMoDat & "\Pfingstmontag\BG"
	$HDays[24][0] = $MutterDat & "\Muttertag\"
	$HDays[25][0] = $FroDat & "\Fronleichnam\BW BY HE NW RP SL SN TH"
	$HDays[26][0] = $ErnteDat & "\Erntedankfest\"
	$HDays[27][0] = $BuBDat & "\Buß- und Bettag\SN"
	$HDays[28][0] = $TotSoDat & "\Totensonntag\"
	$HDays[29][0] = $1AdvDat & "\1. Advent\"
	$HDays[30][0] = $2AdvDat & "\2. Advent\"
	$HDays[31][0] = $3AdvDat & "\3. Advent\"
	$HDays[32][0] = $4AdvDat & "\4. Advent\"
	If $sort Then
		_ArraySort($HDays)
	Else
		_ArraySort($HDays, 0, 0, 11)
		_ArraySort($HDays, 0, 12)
	EndIf
	For $i = 0 To 32
		$tmp = StringSplit($HDays[$i][0], "\", 2)
		If $DateTyp Then ; Datum konvertieren zu TT.MM.JJJJ
			$HDays[$i][0] = StringRight($tmp[0],2) & "." & StringMid($tmp[0],6,2) & "." & StringLeft($tmp[0],4)
		Else
			$HDays[$i][0] = $tmp[0]
		EndIf
		$HDays[$i][1] = $tmp[1]
		$HDays[$i][2] = $tmp[2]
		If $wDay = 1 Then
			$HDays[$i][3] = $aWDays[_DateToDayOfWeek(StringLeft($tmp[0],4), StringMid($tmp[0],6,2), StringRight($tmp[0],2))]
		Else
			$HDays[$i][3] = _DateToDayOfWeek(StringLeft($tmp[0],4), StringMid($tmp[0],6,2), StringRight($tmp[0],2))
		EndIf
	Next
	If $vBundesland > -1 Then
		Local $aTmp[1][4]
		For $i = 0 To 32
			If StringInStr($HDays[$i][2], $aBuLand[$vBundesland], 1) Or StringInStr($HDays[$i][2], 'BG', 1) Then
				If $aTmp[UBound($aTmp)-1][0] <> '' Then ReDim $aTmp[UBound($aTmp)+1][4]
				$aTmp[UBound($aTmp)-1][0] = $HDays[$i][0]
				$aTmp[UBound($aTmp)-1][1] = $HDays[$i][1]
				$aTmp[UBound($aTmp)-1][2] = $HDays[$i][2]
				$aTmp[UBound($aTmp)-1][3] = $HDays[$i][3]
			EndIf
		Next
		Return $aTmp
	EndIf
	Return $HDays
EndFunc   ;==>_Feiertage

