Benutzer-Werkzeuge

Webseiten-Werkzeuge


comp:ftagebash

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 :-)

/usr/local/bin/kalender.sh
#!/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
comp/ftagebash.txt · Zuletzt geändert: 2014-08-12 1702 von werner

Falls nicht anders bezeichnet, ist der Inhalt dieses Wikis unter der folgenden Lizenz veröffentlicht: Public Domain
Public Domain Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki