====== Bash: Feiertage ====== Der folgende Code ist entstanden, als es galt, eine Feiertagsprüfung in Bash (wahlweise AWK) umzusetzen. Also habe ich mal die Feiertage für Sachsen und Sachsen-Anhalt berechnen lassen... Ist nicht schnell, aber funktioniert. Historisch gesehen, habe ich das zum ersten Mal Ende der 80er Jahre in Pascal geschrieben, später in VBA für Excel 5.0 (deutsch), Excel 97 (englisch), PHP, Perl, ... und nun eben zum ersten Mal in Bash :-) #!/bin/bash # Funktionssammlung fuer Kalenderzwecke # checkJahr $JAHR # checkMonat $MONAT # checkTag $TAG $MONAT $JAHR # is_Leapyear $JAHR # cal_days_in_month $MONAT $JAHR # getKarfreitag $JAHR # getOstersonntag $JAHR # getOstermontag $JAHR # getHimmelfahrt $JAHR # getAdvent1 $JAHR # getAdvent2 $JAHR # getAdvent3 $JAHR # getAdvent4 $JAHR # getWochentag $TAG $MONAT $JAHR # istFeiertag $TAG $MONAT $JAHR EWIGERKALENDER='ewkal.sh' [ -f "$EWIGERKALENDER" ] && . "$EWIGERKALENDER" # testet, ob das angegebene Jahr eine vierstellige Zahl und mindestens 1683 ist # Param1: Jahreszahl # Rückgabe: 1 bei Fehler, 0 wenn OK # außerdem wird $? gesetzt checkJahr() { # integer, > 1683 TESTJAHR=${1:-0} VERSJ=$(echo "$TESTJAHR" | tr -d '[0-9]') [ -z "$VERSJ" ] || return 1 if [[ $TESTJAHR =~ ^[0-9]+$ ]] && [ $TESTJAHR -gt 1683 ]; then echo 0 return 0 else echo 1 return 1 fi } # checkJahr # testet, ob der angegebene Jahr Monat im Bereich 1 bis 12 liegt # Param1: Monatsnummer # Rückgabe: 1 bei Fehler, 0 wenn OK # außerdem wird $? gesetzt checkMonat() { # integer, Bereich 1..12 TESTMONAT=${1:-0} if [[ $TESTMONAT =~ ^[0-9]+$ ]] && [ $TESTMONAT -ge 1 -a $TESTMONAT -le 12 ]; then echo 0 return 0 else echo 1 return 1 fi } # checkMonat # teste, ob es sich um ein Schaltjahr handelt # Param1: Jahreszahl # Rückgabe: Text 0 bei Fehler, Text 1 wenn OK # außerdem wird $? gesetzt is_Leapyear() { RETVAL=9 TESTJAHR=${1:-0} JAHR_OK=$(checkJahr $TESTJAHR) if [ "x$JAHR_OK" != 'x0' ]; then echo $RETVAL return $RETVAL fi if [ $(($TESTJAHR % 100)) -ne 0 -a $(($TESTJAHR % 4)) -eq 0 ]; then RETVAL=1 elif [ $(($TESTJAHR % 400)) == 0 ]; then RETVAL=1 else RETVAL=0 fi echo $RETVAL return $RETVAL } # is_Leapyear # Anzahl Tage im Kalendermonat # Param1: Monatszahl, siehe checkMonat # Param2: Jahreszahl, siehe checkJahr # Rückgabe: eine Zahl im Bereich 28..31, je nachdem :-) # im Fehlerfall eine 1 (Errorlevel wird gesetzt) cal_days_in_month() { TESTMONAT=${1:-0} TESTJAHR=${2:-0} JAHR_OK=$(checkJahr $TESTJAHR) MONAT_OK=$(checkMonat $TESTMONAT) if [ "x$JAHR_OK" != 'x0' -o "x$MONAT_OK" != 'x0' ]; then echo 1 return 1 fi case $TESTMONAT in 1|3|5|7|8|10|12) RETVAL=31 ;; 4|6|9|11) RETVAL=30 ;; 2) if [ is_Leapyear $TESTJAHR ]; then RETVAL=29 else RETVAL=28 fi ;; esac echo $RETVAL } # cal_days_in_month # testet, ob ein Tag im erlaubten Bereich (Monatslänge) ist # Param1: Tageszahl # Param2: Monatszahl, siehe checkMonat # Param3: Jahreszahl, siehe checkJahr # Rückgabe: Tageszahl (Param1), wenn Tag möglich ist # sonst letzter Tag des Monats # $? = 0, wenn Tag möglich ist, 1 sonst checkTag() { TESTTAG=${1:-0} TESTMONAT=${2:-0} TESTJAHR=${3:-0} [ "x${TESTTAG:0:1}" = 'x0' ] && TESTTAG="${TESTTAG:1:1}" [ "x${TESTMONAT:0:1}" = 'x0' ] && TESTMONAT="${TESTMONAT:1:1}" # echo "testjahr=$TESTJAHR, testmonat=$TESTMONAT (x${TESTMONAT:0:2}), testtag=$TESTTAG" >> /tmp/jahr4 TAG_OK=$(echo $TESTTAG | tr -d [0-9]) # if [[ $TESTTAG =~ ^[0-9]+$ ]] && [[ $TESTTAG -lt 1 ]]; then let "TESTTAG *= 1" if [ -z "$TAG_OK" -a $TESTTAG -ge 1 ]; then : else echo 1 return 1 fi JAHR_OK=$(checkJahr $TESTJAHR) if [ "x$JAHR_OK" != "x0" ]; then echo 2 return 1 fi MON_OK=$(checkMonat $TESTMONAT) if [ "x$MON_OK" != 'x0' ]; then echo 3 return 1 fi MAXTAGE=$(cal_days_in_month $TESTMONAT $TESTJAHR) if [ $MAXTAGE -ge $TESTTAG ]; then echo $TESTTAG return 0 else echo $MAXTAGE return 1 fi } # checkTag # berechnet das Osterdatum nach Gauss # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Ostersonntag im Format YYYY-MM-DD getOstersonntag() { TESTJAHR=${1:-0} [ $TESTJAHR -eq 0 ] && TESTJAHR=$(date +%Y) G=$(expr $TESTJAHR % 19 + 1) C=$(expr $TESTJAHR / 100 + 1) X=$(expr \( $C / 4 - 4 \) \* 3) Z=$(expr \( $C \* 8 + 5 \) / 25 - 5) D=$(expr $TESTJAHR \* 5 / 4 - $X - 10) E=$(expr \( $G \* 11 + $Z - $X + 20 \) % 30) [ $E -lt 0 ] && E=$(expr $E + 30) [ $E -eq 25 -a $G -gt 11 -o $E -eq 24 ] && E=$(expr $E + 1) TAG=$(expr 44 - $E) [ $TAG -lt 21 ] && TAG=$(expr $TAG + 30) TAG=$(expr $TAG + 7 - \( $D + $TAG \) % 7) if [ $TAG -gt 31 ] ; then TAG=$(expr $TAG - 31) MON=4 else MON=3 fi echo "$TESTJAHR-$MON-$TAG" } # getOstersonntag # berechnet das Datum des 1. Advent # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Datum im Format YYYY-MM-DD getAdvent1() { DJHR=$1 # numerisch? TSTJHR=$(echo "DJHR" | tr -d '[0-9]') [ -z "$TSTJHR" ] || DJHR=$(date +%Y) # lang genug? [ "x${#DJHR}" = 'x4' ] || DJHR=$(date +%Y) DMON=11 DTAG=27 ZIELWOTAG='So' ISTWOTAG=$(getWochentag $DTAG $DMON $DJHR) while [ "$ZIELWOTAG" != "$ISTWOTAG" ]; do STICHSEK=$(date -d "${DJHR}-${DMON}-${DTAG}" +%s) let "STICHSEK += 86400" DTAG=$(date -d "@$STICHSEK" +%d) DMON=$(date -d "@$STICHSEK" +%m) ISTWOTAG=$(getWochentag $DTAG $DMON $DJHR) done date -d "@$STICHSEK" +'%Y-%m-%d' } # getAdvent1 # berechnet das Datum des 2. Advent # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Datum im Format YYYY-MM-DD getAdvent2() { DJHR=$1 # numerisch? TSTJHR=$(echo "DJHR" | tr -d '[0-9]') [ -z "$TSTJHR" ] || DJHR=$(date +%Y) # lang genug? [ "x${#DJHR}" = 'x4' ] || DJHR=$(date +%Y) # Basis: 1. Advent ADV1=$(getAdvent1 $DJHR) SEKDAT=$(date -d "$ADV1" +%s) # plus eine Woche DIFF=$((7 * 86400)) let "SEKDAT += DIFF" date -d "@$SEKDAT" +'%Y-%m-%d' } # getAdvent2 # berechnet das Datum des 3. Advent # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Datum im Format YYYY-MM-DD getAdvent3() { DJHR=$1 # numerisch? TSTJHR=$(echo "DJHR" | tr -d '[0-9]') [ -z "$TSTJHR" ] || DJHR=$(date +%Y) # lang genug? [ "x${#DJHR}" = 'x4' ] || DJHR=$(date +%Y) # Basis: 1. Advent ADV1=$(getAdvent1 $DJHR) SEKDAT=$(date -d "$ADV1" +%s) # plus zwei Wochen DIFF=$((2 * 7 * 86400)) let "SEKDAT += DIFF" date -d "@$SEKDAT" +'%Y-%m-%d' } # getAdvent3 # berechnet das Datum des 4. Advent # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Datum im Format YYYY-MM-DD getAdvent4() { DJHR=$1 # numerisch? TSTJHR=$(echo "DJHR" | tr -d '[0-9]') [ -z "$TSTJHR" ] || DJHR=$(date +%Y) # lang genug? [ "x${#DJHR}" = 'x4' ] || DJHR=$(date +%Y) # Basis: 1. Advent ADV1=$(getAdvent1 $DJHR) SEKDAT=$(date -d "$ADV1" +%s) # plus drei Wochen DIFF=$((3 * 7 * 86400)) let "SEKDAT += DIFF" date -d "@$SEKDAT" +'%Y-%m-%d' } # getAdvent4 # berechnet das Datum des Buß- und Bettags # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Datum im Format YYYY-MM-DD getBusstag() { DJHR=$1 # numerisch? TSTJHR=$(echo "$DJHR" | tr -d '[0-9]') [ -z "$TSTJHR" ] || DJHR=$(date +%Y) # lang genug? [ "x${#DJHR}" = 'x4' ] || DJHR=$(date +%Y) # Basis: 1. Advent ADV1=$(getAdvent1 $DJHR) SEKDAT=$(date -d "$ADV1" +%s) # minus 10 tage DIFF=$((10 * 86400)) let "SEKDAT -= $DIFF" date -d "@$SEKDAT" +'%Y-%m-%d' } # getBusstag # berechnet das Datum des Ostermontags # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Datum im Format YYYY-MM-DD getOstermontag() { ODAT=$1 [ -z "$ODAT" ] && return 1 # in Sekunden umwandeln OSONN=$(date -d "$ODAT" +%s) # Differenz hinzu OMONTAG=$(($OSONN + 86400)) # im Format YYYY-MM-DD ausgeben date -d "@$OMONTAG" +'%Y-%m-%d' } # getOstermontag # berechnet das Datum des Karfreitags # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Datum im Format YYYY-MM-DD getKarfreitag() { ODAT=$1 [ -z "$ODAT" ] && return 1 # in Sekunden umwandeln OSONN=$(date -d "$ODAT" +%s) # Differenz hinzu SEKDIFF=$((2 * 86400)) KARFTAG=$(($OSONN + $SEKDIFF)) # im Format YYYY-MM-DD ausgeben date -d "@$KARFTAG" +'%Y-%m-%d' } # getKarfreitag # berechnet das Datum des Pfingstsonntags # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Datum im Format YYYY-MM-DD getPfingstsonntag() { ODAT=$1 [ -z "$ODAT" ] && return 1 # in Sekunden umwandeln OSONN=$(date -d "$ODAT" +%s) # Differenz hinzu SEKDIFF=$((49 * 86400)) PFSONN=$(($OSONN + $SEKDIFF)) # im Format YYYY-MM-DD ausgeben date -d "@$PFSONN" +'%Y-%m-%d' } # getPfingstsonntag # berechnet das Datum des Pfingstmontags # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Datum im Format YYYY-MM-DD getPfingstmontag() { ODAT=$1 [ -z "$ODAT" ] && return 1 # in Sekunden umwandeln OSONN=$(date -d "$ODAT" +%s) # Differenz hinzu SEKDIFF=$((50 * 86400)) PFMON=$(($OSONN + $SEKDIFF)) # im Format YYYY-MM-DD ausgeben date -d "@$PFMON" +'%Y-%m-%d' } # getPfingstmontag # berechnet das Datum von Christi Himmelfahrt # Param1: Jahreszahl, siehe checkJahr # Rückgabe: Datum im Format YYYY-MM-DD getHimmelfahrt() { ODAT=$1 [ -z "$ODAT" ] && return 1 # in Sekunden umwandeln OSONN=$(date -d "$ODAT" +%s) # Differenz hinzu SEKDIFF=$((39 * 86400)) HIFAH=$(($OSONN + $SEKDIFF)) # im Format YYYY-MM-DD ausgeben date -d "@$HIFAH" +'%Y-%m-%d' } # getHimmelfahrt # prüft, ob es sich beim übergebenen Datum um einen Feiertag handelt # Param1: Tageszahl, siehe checkTag # Param2: Monatszahl, siehe checkMonat # Param3: Jahreszahl, siehe checkJahr # Param4: Region; 'SN' -> Sachsen, 'SA' -> Sachsen-Anhalt # Rückgabe: 1 wenn Feiertag, 0 sonst # $? wird entsprechend gesetzt istFeiertag() { TAG=${1:-0} MONAT=${2:-0} JAHR=${3:-0} INREGION=${4:-'SN'} RETID=0 [ "x$JAHR" = "x0" ] && JAHR=$(date +%Y) [ "x$MONAT" = "x0" ] && MONAT=$(date +%m) [ "x$TAG" = "x0" ] && TAG=$(date +%d) JAHR_OK=$(checkJahr $JAHR) if [ "x$JAHR_OK" != 'x0' ]; then echo 0 return 1 fi [ "x${TAG:0:1}" = 'x0' ] && TAG=${TAG:1:1} [ "x${MON:0:1}" = 'x0' ] && MON=${MON:1:1} VGLTAG=$(checkTag $TAG $MONAT $JAHR) [ $VGLTAG -ne $TAG ] && return 1 FEIER=0 if [ $MONAT -eq 12 ]; then case "$TAG" in 24|25|26|31) FEIER=1 ;; esac elif [ $MONAT -eq 10 ]; then case $TAG in 3|31) FEIER=1 ;; esac elif [ $MONAT -eq 5 -a $TAG -eq 1 ]; then FEIER=1 elif [ $MONAT -eq 1 -a $TAG -eq 1 ]; then FEIER=1 fi if [ $FEIER -eq 0 ]; then VGLDAT="${JAHR}-${MONAT}-${TAG}" # jetzt die beweglichen Feiertage # Lassen wir die Sonntage mal raus ;-) KFDAT=$(getKarfreitag $JAHR) # OSDAT=$(getOstersonntag $JAHR) OMDAT=$(getOstermontag $JAHR) HFDAT=$(getHimmelfahrt $JAHR) # PSDAT=$(getPfingstsonntag $JAHR) PMDAT=$(getPfingstmontag $JAHR) # A1DAT=$(getAdvent1 $JAHR) # A2DAT=$(getAdvent2 $JAHR) # A3DAT=$(getAdvent3 $JAHR) # A4DAT=$(getAdvent4 $JAHR) if [ "$VGLDAT" = "$KFDAT" ]; then FEIER=1 # elif [ "$VGLDAT" = "$OSDAT" ]; then # FEIER=1 elif [ "$VGLDAT" = "$OMDAT" ]; then FEIER=1 elif [ "$VGLDAT" = "$HFDAT" ]; then FEIER=1 # elif [ "$VGLDAT" = "$PSDAT" ]; then # FEIER=1 elif [ "$VGLDAT" = "$PMDAT" ]; then FEIER=1 # elif [ "$VGLDAT" = "$A1DAT" ]; then # FEIER=1 # elif [ "$VGLDAT" = "$A2DAT" ]; then # FEIER=1 # elif [ "$VGLDAT" = "$A3DAT" ]; then # FEIER=1 # elif [ "$VGLDAT" = "$A4DAT" ]; then # FEIER=1 fi fi if [ $FEIER -eq 0 ]; then # jetzt die bundeslandabhängigen Feiertage if [ "$INREGION" = 'SN' ]; then BBDAT=$(getBusstag $JAHR) if [ "x$VGLDAT" = "x$BBDAT" ]; then FEIER=1 fi else if [ "$VGLDAT" = "${JAHR}-01-06" ]; then FEIER=1 fi fi fi echo "$FEIER" return $FEIER } # istFeiertag