smarthomeNG / smarthome

Device integration platform for your smart home
https://www.smarthomeNG.de
GNU General Public License v3.0
123 stars 91 forks source link

Logic Feiertagsberechnung #24

Closed Bonze255 closed 6 years ago

Bonze255 commented 8 years ago

Man könnte eine allgemeine Feiertagsberechnung implementieren wie sie hier http://www.stephanjohn.de/blog/2011/jan/07/berechnung-von-feiertagen/ beschrieben ist, sodass man in den Rollladenskripten auf if sh......item_feiertag() prüfen kann

ohinckel commented 8 years ago

Vielleicht kann man das mit dem ical-Plugin kombinieren und einen Standard-Kalender hinterlegen, der die Feiertage enthaelt? Ich hatte sowas aehnliches bereits implementiert.

schraegervogel commented 8 years ago

Hallo, ich verwende smarthome/smartvisu seit Jahren und verfolge Eure Bemühungen das Projekt zu reanimieren, bin sehr begeistert!!!

Verwende die angehängte Funktion isfeiertag(datum) für meine Jalousiesteuerung (Sa/So/Feiertag gehen sie später auf), enthält dzt. nur österreichische Feiertage, weitere können jedoch jederzeit eingefügt werden.

Michael

feiertage.zip

PS: war/ist eines meiner ersten Pythonscripts - möglicherweise etwas dirty ;-)

Bonze255 commented 8 years ago

Vielleicht sollten wir das so dynamisch wie möglich gestalten, sodass man bei der Benutzung wenig wie garkeine Arbeit hat, das Script oben, sollte die Feiertage über Formeln berechnen, muss allerdings zugeben, das ichs noch nicht ausprobiert habe.

psilo909 commented 8 years ago

ich würde die berechnung als (standard-)plugin dazunehmen, ich denke als "standard" in den kern zu packen, ist eher die falsche richtung. das ding sollte schmall und schnell bleiben.

ohinckel commented 8 years ago

Man koennte die Berechnung der Feiertage auch im ical-Plugin implementieren und zur Verfuegung stellen. Meine Erweiterung geht eher in die Richtung, dass man damit mehrere Kalender hinterlegen kann, die man sich dann per sh.ical('public') holen und damit arbeiten kann.

Vielleicht kann man aber auch sowas machen, dass interne Kalender, die auf Berechnungen baisieren, ebenfalls fuer diese API geholt werden koennen. Ich habe da spontan an sowas wie sh.ical('<holidays>') - ueber den Bezeichner fuer diese Kalender kann man aber noch nachdenken.

Damit waere die API die gleiche und es waere somit auch egal, ob das ein richtiger Kalender ist, oder einer, der berechnet wird.

schraegervogel commented 8 years ago

Ich habe mein oben gepostetes Skript im lib-Verzeichnis abgelegt, Verwendung findet es in meiner Jalousien-Logik:

from lib.feiertage import *

if sh.Diverses.JalousieProgramme():
    # weekday(): 0...Montag, 1...Dienstag, ...
    # ist Feiertag oder Sa/So und übergebener Wert = 1
    if (isfeiertag(heute) or heute.weekday() > 4) and logic.IsSaSoFeiertag == '1':
        # Jalousie-Szene aktivieren
        if sh.Diverses.Sommer():
            mySommer(sh)
        else:   
            winter(sh)
psilo909 commented 8 years ago

Wenn wir uns wirklich entscheiden das in den Core aufzunehmen, dann bitte englische Methodenbezeichnungen ;-) ICAL habe ich bisher nicht im Einsatz, kann mir aber gut vorstellen die Feiertags-Info auch ohne zu verwenden.

psilo909 commented 8 years ago

Ggf. auch nochmal mit hier abgleichen: https://knx-user-forum.de/forum/supportforen/smarthome-py/940073-feiertagsberechnung

Bonze255 commented 8 years ago
#!/usr/bin/env python
 # -*- coding: utf-8 -*-
# --------------------------------------------------------------------------#
# Mit diesem Script können die Feiertage in Deutschland berechnet und         #
# ausgegeben werden. Wird dem Script ein Bundesland übergeben, werden alle    #
# Feiertage dieses Bundeslandes ausgegeben. Wird kein Bundesland übergeben,   #
# so werden nur die bundeseinheitlichen Feiertage ausgegeben.                 #
#                                                                             #
# Autor: Stephan John                                                         #
# Version: 1.1                                                                #
# Datum: 25.05.2012                                                           #
#                                                                             #
# Danke an Paul Wachendorf für die Hinweise                                   #
#-----------------------------------------------------------------------------#
#2016 erweitert für smarthomeNG.py

import datetime
import pprint
state_codes = {
                #Deutschland
                'Baden-Württemberg':'BW',
                'Bayern':'BY',
                'Berlin':'BE',
                'Brandenburg':'BB',
                'Bremen':'HB',
                'Hamburg':'HH',
                'Hessen':'HE',
                'Mecklenburg-Vorpommern':'MV',
                'Niedersachsen':'NI',
                'Nordrhein-Westfalen':'NW',
                'Rheinland-Pfalz':'RP',
                'Saarland':'SL',
                'Sachsen':'SN',
                'Sachsen-Anhalt':'SA',
                'Schleswig-Holstein':'SH',
                'Thüringen':'TH',

                ##Oesterreich
                'Burgenland':'B',               
                'Kärnten':'K',
                'Niederösterreich':'N',
                'Oberösterreich':'O',
                'Land Salzburg':'S',
                'Steiermark':'ST',
                'Tirol':'T',
                'Vorarlberg':'V',
                'Wien':'W',

                ##Schweiz
                'Kanton Zürich':'KZH',               
                'Kanton Bern':'KBE',
                'Kanton Luzern':'KLU',
                'Kanton Uri':'KUR',
                'Kanton Schwyz':'KSZ',
                'Kanton Obwalden':'KOW',
                'Kanton Nidwalden':'KNW',
                'Kanton Glaraus':'KGL',
                'Kanton Zug':'KZG',
                'Kanton Freiburg':'KFR',               
                'Kanton Solothurn':'KSO',
                'Kanton Basel Stadt':'KBS',
                'Kanton Basel Landschaft':'KBL',
                'Kanton Schaffhausen':'KSH',
                'Kanton Appenzell Ausserhoden':'KAR',
                'Kanton Appenzell Innerhoden':'KAL',
                'Kanton St.Gallen':'KSG',
                'Kanton Graubünden':'KGR',
                'Kanton Aargau':'KAG',
                'Kanton Thurgau':'KTG',
                'Kanton Tessin':'KTI',
                'Kanton Waadt':'KVD',
                'Kanton Wallis':'KVS',
                'Kanton Neuenburg':'KNE',
                'Kanton Genf':'KGE',
                'Kanton Jura':'KJU',
               }

def holidays(year, state=None):

    """
    prüft die eingegebenen Werte für die Berechnung der Feiertage
    year  => Jahreszahl ab 1970
    state => Bundesland als offizielle Abkürzung oder Vollname
    """

    try:
        year = int(year)
        if year < 1970:
            year = 1970
            print ('Jahreszahl wurde auf 1970 geändert')
    except ValueError:
        print ('Fehlerhafte Angabe der Jahreszahl')
        return
    if state:
        if state in state_codes.keys():
            state_code = state_codes[state]
        else:
            if state.upper() in state_codes.values():
                state_code = state.upper()
            else:
                state_code = None
    else:
        state_code = None
    if not state_code:
        print ('Es werden nur die deutschlandweit gültigen Feiertage ausgegeben')
    hl = Holidays(year, state_code)
    print(year, state_code)
    holidays = hl.get_holiday_list()
    for h in holidays:
        print (h[1],  h[0])

    return None

class Holidays:

    """
    Berechnet die Feiertage für ein Jahr. Wird ein Bundesland übergeben, werden
    alle Feiertage des Bundeslandes zurückgegeben. Das erfolgt über die
    Funktion get_holiday_list().
    Das Bundesland (state_code) muss mit der offiziellen zweistelligen
    Bezeichnung übergeben werden (z.B. Sachsen mit SN)

    Holidays(year(int), [state_code(str)])
    """

    def __init__(self, year, state_code):
        self.year = int(year)
        if self.year < 1970:
            self.year = 1970
        if state_code:
            self.state_code = state_code.upper()
            if self.state_code not in state_codes.values():
                self.state_code = None
        self.easter_date = self.get_easter_date(self.year)
        self.holiday_list = []
        self.general_public_holidays(state_code)
        if state_code:
            self.get_three_kings(state_code)
            self.get_assumption_day(state_code)
            self.get_reformation_day(state_code)
            self.get_all_saints_day(state_code)
            self.get_repentance_and_prayer_day(state_code)
            self.get_corpus_christi(state_code)

    def get_holiday_list(self):

        """
        Gibt die Liste mit den Feiertagen zurück
        """

        self.holiday_list.sort()
        return self.holiday_list

    def general_public_holidays(self, state_code = None):

        """
        Alle bundeseinheitlichen Feiertage werden der Feiertagsliste
        zugefügt.
        """

        # feste Feiertage Deutschland/Oesterreich:
        newyear = datetime.date(self.year, 1, 1) 
        self.holiday_list.append([newyear, u'Neujahr'])

        if state_code in ['K','ST','T', 'V']:#nur Teile von Österreich
            josef = datetime.date(self.year, 3, 19)
            self.holiday_list.append([josef, u'Josef'])

        may = datetime.date(self.year, 5, 1)
        self.holiday_list.append([may, u'1. Mai'])

        if state_code in ['O']:#nur Teile von Österreich
            florian = datetime.date(self.year, 5, 4)
            self.holiday_list.append([florian, u'Florian'])

        if state_code in ['S']:#nur Teile von Österreich
            rupert = datetime.date(self.year, 9, 24)
            self.holiday_list.append([rupert, u'Rupert'])

        if state_code in ['K']:#nur Teile von Österreich
            dofv = datetime.date(self.year, 10, 10)
            self.holiday_list.append([dofv, u'Tag der Volksabstimmung'])

        if state_code in ['B','K', 'N','O', 'S', 'ST','T', 'V', 'W']:#nur Teile von Österreich
            dofv = datetime.date(self.year, 10, 24)
            self.holiday_list.append([dofv, u'Nationalfeiertag'])

        if state_code in ['BW','BY', 'BE','BB', 'HB', 'HH','HE', 'MV', 'NI','NW','RP', 'SL','SN', 'SA', 'SH','TH']:#nur Deutschland
            union = datetime.date(self.year, 10, 3)
            self.holiday_list.append([union, u'Tag der deutschen Einheit'])

        if state_code in ['B']:#nur Teile von Österreich
            martin = datetime.date(self.year, 11, 11)
            self.holiday_list.append([martin, u'Martin'])

        if state_code in ['N','W']:#nur Teile von Österreich
            leopold = datetime.date(self.year, 11, 15)
            self.holiday_list.append([leopold, u'Leopold'])

        if state_code in ['B','K', 'N','O', 'S', 'ST','T', 'V', 'W']:#nur Teile von Österreich
            maria = datetime.date(self.year, 12, 8)
            self.holiday_list.append([maria, u'Maria Empfaengnis'])   

        christmas1 = datetime.date(self.year, 12, 25)
        if state_code in [ 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W']:#Andrer Name in Österreich
            self.holiday_list.append([christmas1, u'Christtag'])
        else:
            self.holiday_list.append([christmas1, u'Erster Weihnachtsfeiertag'])

        christmas2 = datetime.date(self.year, 12, 26)
        if state_code in [ 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W']:#Andrer Name in Österreich
            self.holiday_list.append([christmas2, u'Stefanietag'])
        else:
            self.holiday_list.append([christmas2, u'Zweiter Weihnachtsfeiertag'])

        if state_code in ['B','K', 'N','O', 'S', 'ST','T', 'V', 'W']:#nur Teile von Österreich
            sylvester = datetime.date(self.year, 12, 31)
            self.holiday_list.append([sylvester, u'Sylvester'])   

        #bewegliche Feiertage:
        self.holiday_list.append([self.get_holiday(2, _type='minus'), u'Karfreitag'])
        self.holiday_list.append([self.easter_date, u'Ostersonntag'])
        self.holiday_list.append([self.get_holiday(1), u'Ostermontag'])
        self.holiday_list.append([self.get_holiday(39), u'Christi Himmelfahrt'])
        self.holiday_list.append([self.get_holiday(49), u'Pfingstsonntag'])
        self.holiday_list.append([self.get_holiday(50), u'Pfingstmontag'])

    def get_holiday(self, days, _type='plus'):

        """
        Berechnet anhand des Ostersonntages und der übergebenen Anzahl Tage
        das Datum des gewünschten Feiertages. Mit _type wird bestimmt, ob die Anzahl
        Tage addiert oder subtrahiert wird.
        """

        delta = datetime.timedelta(days=days)
        if _type == 'minus':
            return self.easter_date - delta
        else:
            return self.easter_date + delta

    def get_three_kings(self, state_code):
        """ Heilige Drei Könige """
        """ Oesttereicher Tage hinzugefuegt ab 4.""" 
        valid = ['BY', 'BW', 'ST', 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W']
        if state_code in valid:
            three_kings = datetime.date(self.year, 1, 6)
            self.holiday_list.append([three_kings, u'Heilige Drei Könige'])

    def get_assumption_day(self, state_code):
        """ Mariä Himmelfahrt """
        """ Oesttereicher Tage hinzugefuegt ab 3.""" 
        valid = ['BY', 'SL', 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W']
        if state_code in valid:
            assumption_day = datetime.date(self.year, 8, 15)
            self.holiday_list.append([assumption_day, u'Mariä Himmelfahrt'])

    def get_reformation_day(self, state_code):
        """ Reformationstag """
        valid = ['BB', 'MV', 'SN', 'ST', 'TH']
        if state_code in valid:
            reformation_day = datetime.date(self.year, 10, 31)
            self.holiday_list.append([reformation_day, u'Reformationstag'])

    def get_all_saints_day(self, state_code):
        """ Allerheiligen """
        """ Oesttereicher Tage hinzugefuegt ab 6.""" 
        valid = ['BW', 'BY', 'NW', 'RP', 'SL', 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W']
        if state_code in valid:
            all_saints_day = datetime.date(self.year, 11, 1)
            self.holiday_list.append([all_saints_day, u'Allerheiligen'])

    def get_repentance_and_prayer_day(self, state_code):
        """
        Buß und Bettag
        (Mittwoch zwischen dem 16. und 22. November)
        """
        valid = ['SN']
        if state_code in valid:
            first_possible_day = datetime.date(self.year, 11, 16)
            rap_day = first_possible_day
            weekday = rap_day.weekday()
            step = datetime.timedelta(days=1)
            while weekday != 2:
                rap_day = rap_day + step
                weekday = rap_day.weekday()
            self.holiday_list.append([rap_day, u'Buß und Bettag'])

    def get_corpus_christi(self, state_code):
        """ Fronleichnam 60 Tage nach Ostersonntag """
        """ Oesttereicher Tage hinzugefuegt ab 7.""" 
        valid = ['BW','BY','HE','NW','RP','SL', 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W']
        if state_code in valid:
            corpus_christi = self.get_holiday(60)
            self.holiday_list.append([corpus_christi, u'Fronleichnam'])

    # Berechnung Ostersonntag nach Lichtenberg 
    # (http://de.wikipedia.org/wiki/Gau%C3%9Fsche_Osterformel)
    #(Korrektur wenn Ostersonntag nach dem 25.April
    def get_easter_date(self, year):

        #self.year = year

        a = self.year % 19
        k = self.year // 100
        m = 15 + (3 * k + 3) // 4 - (8 * k + 13) // 25
        d = (19 * a + m) % 30
        s = 2 - ( 3 * k + 3) // 4
        r = (d + a // 11) // 29
        og = 21 + d -r
        sz = 7 - (self.year  + self.year  // 4 + s) % 7
        oe = 7 - (og - sz) % 7
        os = (og + oe)
        month, day = divmod(os, 31)
        month = month + 3
        easter_day = datetime.date(self.year, month, day)
        return easter_day        

print("init")
frei =  holidays(2016,'RP')

funzt soweit mal,

psilo909 commented 8 years ago

wenn du es als Logik einsteuern magst, könntest Du es ja unter https://github.com/smarthomeNG/smarthome/wiki/Logik-Beispiele dokumentieren?

Bonze255 commented 8 years ago

mit Österreich und Schweiz sind es doch ganz schön viele :1234: ne kleine Übersicht, werd dann alles soweit umstricken, das es für jeden Tag eine Abfrage gibt bzw man nur dann noch sein Bundesland/Kanton angeben muss und Jahr
Microsoft Excel-Arbeitsblatt (neu).xlsx

cstrassburg commented 8 years ago

@psilo909 Wäre eher ein Plugin. Mein Vorschlag wäre ein Kalender Interface, welche Implementierung dahinter ist sollte dann egal sein. Der eine legt seine Feiertage im ical ab, der andere bei Google und der nächste berechnet sie. Ich muss mal drüber nachdenken ob das mit der jetzigen Plugin Implementierung überhaupt möglich wäre.

Bonze255 commented 8 years ago

so, jetzt müssten alle daten drin sein

#!/usr/bin/env python
# -- coding: utf-8 --
#--------------------------------------------------------------------------
#Mit diesem Script können die Feiertage in Deutschland berechnet und
#ausgegeben werden. Wird dem Script ein Bundesland übergeben, werden alle
#Feiertage dieses Bundeslandes ausgegeben. Wird kein Bundesland übergeben,
#so werden nur die bundeseinheitlichen Feiertage ausgegeben.
#Autor: Stephan John
#Version: 1.1
#Datum: 25.05.2012
#Danke an Paul Wachendorf für die Hinweise
#2016 erweitert für smarthomeNG.py fpr D/CH/A von Manuel Holländer (Bonze255/Fragger255)
#-----------------------------------------------------------------------------#
#

import datetime
import pprint
state_codes = {
            #"""Bundesländer Deutschland"""
            'Baden-Württemberg':'BW',
            'Bayern':'BY',
            'Berlin':'BE',
            'Brandenburg':'BB',
            'Bremen':'HB',
            'Hamburg':'HH',
            'Hessen':'HE',
            'Mecklenburg-Vorpommern':'MV',
            'Niedersachsen':'NI',
            'Nordrhein-Westfalen':'NW',
            'Rheinland-Pfalz':'RP',
            'Saarland':'SL',
            'Sachsen':'SN',
            'Sachsen-Anhalt':'SA',
            'Schleswig-Holstein':'SH',
            'Thüringen':'TH',

            #"""Bundesländer Oesterreich"""
            'Burgenland':'B',
            'Kärnten':'K',
            'Niederösterreich':'N',
            'Oberösterreich':'O',
            'Land Salzburg':'S',
            'Steiermark':'ST',
            'Tirol':'T',
            'Vorarlberg':'V',
            'Wien':'W',

            #"""Kantone Schweiz"""
            'Kanton Zürich':'KZH',
            'Kanton Bern':'KBE',
            'Kanton Luzern':'KLU',
            'Kanton Uri':'KUR',
            'Kanton Schwyz':'KSZ',
            'Kanton Obwalden':'KOW',
            'Kanton Nidwalden':'KNW',
            'Kanton Glaraus':'KGL',
            'Kanton Zug':'KZG',
            'Kanton Freiburg':'KFR',
            'Kanton Solothurn':'KSO',
            'Kanton Basel Stadt':'KBS',
            'Kanton Basel Landschaft':'KBL',
            'Kanton Schaffhausen':'KSH',
            'Kanton Appenzell Ausserhoden':'KAR',
            'Kanton Appenzell Innerhoden':'KAL',
            'Kanton St.Gallen':'KSG',
            'Kanton Graubünden':'KGR',
            'Kanton Aargau':'KAG',
            'Kanton Thurgau':'KTG',
            'Kanton Tessin':'KTI',
            'Kanton Waadt':'KVD',
            'Kanton Wallis':'KVS',
            'Kanton Neuenburg':'KNE',
            'Kanton Genf':'KGE',
            'Kanton Jura':'KJU',
           }

def holidays(year, state=None):
    """
    prüft die eingegebenen Werte für die Berechnung der Feiertage
    year  => Jahreszahl ab 1970
    state => Bundesland als offizielle Abkürzung oder Vollname
    """
    try:
        year = int(year)
        if year < 1970:
            year = 1970
            print ('Jahreszahl wurde auf 1970 geändert')
    except ValueError:
        print ('Fehlerhafte Angabe der Jahreszahl')
        return
    if state:
        if state in state_codes.keys():
            state_code = state_codes[state]
        else:
            if state.upper() in state_codes.values():
                state_code = state.upper()
            else:
                state_code = None
    else:
        state_code = None
    if not state_code:
        print ('Es werden nur die deutschlandweit gültigen Feiertage ausgegeben')
    hl = Holidays(year, state_code)
    print(year, state_code)
    holidays = hl.get_holiday_list()
    for h in holidays:
        print (h[1],Holidays.get_day(h[0]), h[0])

    return None

class Holidays:
    """
    Berechnet die Feiertage für ein Jahr. Wird ein Bundesland übergeben, werden
    alle Feiertage des Bundeslandes zurückgegeben. Das erfolgt über die
    Funktion get_holiday_list().
    Das Bundesland (state_code) muss mit der offiziellen zweistelligen
    Bezeichnung übergeben werden (z.B. Sachsen mit SN)

    Holidays(year(int), [state_code(str)])
    """
    def __init__(self, year, state_code):
        self.year = int(year)
        if self.year < 1970:
            self.year = 1970
        if state_code:
            self.state_code = state_code.upper()
            if self.state_code not in state_codes.values():
                self.state_code = None
        self.easter_date = self.get_easter_date(self.year)
        self.holiday_list = []
        self.general_public_holidays(state_code)
        if state_code:
            self.get_three_kings(state_code)
            self.get_assumption_day(state_code)
            self.get_reformation_day(state_code)
            self.get_all_saints_day(state_code)
            self.get_repentance_and_prayer_day(state_code)
            self.get_corpus_christi(state_code)

    """Gibt anhand des übergebenen Datetime Object (YYYY, MM, DD)
    den passenden Wochentag als Wort zurück"""
    def get_day(date):
        wday = date.weekday()
        if wday == 6:
            day = "Sonntag"
        elif wday == 0:
            day = "Montag"
        elif wday == 1:
            day = "Dienstag"
        elif wday == 2:
            day = "Mittowch"
        elif wday == 3:
            day = "Donnerstag"
        elif wday == 4:
            day = "Freitag"
        else:
            day = "Samstag"
        return day

#    """Prüfen ob angegebenes Datum (Datetime Object (YYYY-MM-DD)) in der Liste der Feiertage ist"""
    def is_holiday(date, year, state_code):

        hl = Holidays(year, state_code)
        print(year, state_code)
        holidays = hl.get_holiday_list()
        for h in holidays:
            print (h[1], h[0])
            if date == h[0]:
                return True
            else:
                return False

    def get_holiday_list(self):
        """
        Gibt die Liste mit den Feiertagen zurück
        """
        self.holiday_list.sort()
        return self.holiday_list

    def general_public_holidays(self, state_code = None):

        """
        Alle einheitlichen Feiertage werden der Feiertagsliste
        zugefügt.
        """
        newyear = datetime.date(self.year, 1, 1)
        self.holiday_list.append([newyear, u'Neujahr'])

        may = datetime.date(self.year, 5, 1)
        self.holiday_list.append([may, u'1. Mai'])

        if state_code in ['BW','BY', 'BE','BB', 'HB', 'HH','HE', 'MV', 'NI','NW','RP', 'SL','SN', 'SA', 'SH','TH']:#nur Deutschland
            union = datetime.date(self.year, 10, 3)
            self.holiday_list.append([union, u'Tag der deutschen Einheit'])

        christmas1 = datetime.date(self.year, 12, 25)
        if state_code in [ 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W']:#Andrer Name in Österreich
            self.holiday_list.append([christmas1, u'Christtag'])
        elif state_code in ['KZH','KZH','KBE','KLU','KUR','KSZ','KOW','KNW','KGL','KZW','KFR','KSO','KBS','KBS','KBL', 'KSH','KAR','KAI','KSG','KGR','KAG','KTG','KTI','KVD','KVS','KNE','KGE','KJU']:#Andrer Name in der Schweiz
            self.holiday_list.append([christmas1, u'Weihnachtstag'])
        else:
            self.holiday_list.append([christmas1, u'Erster Weihnachtsfeiertag'])

        christmas2 = datetime.date(self.year, 12, 26)
        if state_code in [ 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W']:#Andrer Name in Österreich
            self.holiday_list.append([christmas2, u'Stefanietag'])
        elif state_code in ['KZH','KZH','KBE','KLU','KUR','KSZ','KOW','KNW','KGL','KZW','KFR','KSO','KBS','KBS','KBL', 'KSH','KAR','KAI','KSG','KGR','KAG','KTG','KTI']:#Andrer Name in der Schweiz
            self.holiday_list.append([christmas1, u'Stephanstag'])
        else:
            self.holiday_list.append([christmas2, u'Zweiter Weihnachtsfeiertag'])

        """
        Alle festen, individuelllen Feiertage werden der Feiertagsliste zugefügt:
        """

        if state_code in ['K','ST','T', 'V']:#nur Teile von Österreich
            josef = datetime.date(self.year, 3, 19)
            self.holiday_list.append([josef, u'Josef'])

        if state_code in ['O']:#nur Teile von Österreich
            florian = datetime.date(self.year, 5, 4)
            self.holiday_list.append([florian, u'Florian'])

        if state_code in ['S']:#nur Teile von Österreich
            rupert = datetime.date(self.year, 9, 24)
            self.holiday_list.append([rupert, u'Rupert'])

        if state_code in ['K']:#nur Teile von Österreich
            dofv = datetime.date(self.year, 10, 10)
            self.holiday_list.append([dofv, u'Tag der Volksabstimmung'])

        if state_code in ['B','K', 'N','O', 'S', 'ST','T', 'V', 'W']:#nur Teile von Österreich
            nft = datetime.date(self.year, 10, 24)
            self.holiday_list.append([nft, u'Nationalfeiertag'])

        if state_code in ['B']:#nur Teile von Österreich
            martin = datetime.date(self.year, 11, 11)
            self.holiday_list.append([martin, u'Martin'])

        if state_code in ['N','W']:#nur Teile von Österreich
            leopold = datetime.date(self.year, 11, 15)
            self.holiday_list.append([leopold, u'Leopold'])

        if state_code in ['B','K', 'N','O', 'S', 'ST','T', 'V', 'W','KLU','KUR', 'KSZ','KOW', 'KNW', 'KZG','KFR', 'KSO', 'KAI', 'KGR', 'KAG', 'KTI', 'KVS']:#nur Teile von Österreich, Schweiz
            maria = datetime.date(self.year, 12, 8)
            self.holiday_list.append([maria, u'Maria Empfaengnis'])

        if state_code in ['B','K', 'N','O', 'S', 'ST','T', 'V', 'W']:#nur Teile von Österreich
            sylvester = datetime.date(self.year, 12, 31)
            self.holiday_list.append([sylvester, u'Sylvester'])

        """Alle beweglichen Feiertage werden berechnet und der Liste hinzugefügt:"""
        self.holiday_list.append([self.get_holiday(3, _type='minus'), u'Karfreitag'])#D, A. CH
        self.holiday_list.append([self.get_holiday(2, _type='minus'), u'Karfreitag'])#D, A. CH
        self.holiday_list.append([self.easter_date, u'Ostersonntag'])#D, A. CH
        self.holiday_list.append([self.get_holiday(1), u'Ostermontag'])#D, A. CH
        self.holiday_list.append([self.get_holiday(39), u'Christi Himmelfahrt'])#D, A. CH
        self.holiday_list.append([self.get_holiday(49), u'Pfingstsonntag'])#D, A. CH
        self.holiday_list.append([self.get_holiday(50), u'Pfingstmontag'])#D, A. CH

    def get_holiday(self, days, _type='plus'):

        """
        Berechnet anhand des Ostersonntages und der übergebenen Anzahl Tage
        das Datum des gewünschten Feiertages. Mit _type wird bestimmt, ob die Anzahl
        Tage addiert oder subtrahiert wird.
        """
        delta = datetime.timedelta(days=days)
        if _type == 'minus':
            return self.easter_date - delta
        else:
            return self.easter_date + delta

    def get_three_kings(self, state_code):
        """ Heilige Drei Könige """
        """ Oesttereicher Tage hinzugefuegt ab 4."""
        valid = ['BY', 'BW', 'ST', 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W']
        if state_code in valid:
            three_kings = datetime.date(self.year, 1, 6)
            self.holiday_list.append([three_kings, u'Heilige Drei Könige'])

    def get_assumption_day(self, state_code):
        """ Mariä Himmelfahrt """
        """ Oesttereicher Tage hinzugefuegt ab 3."""
        """ Schweizer Tage hinzugefuegt ab 11."""
        valid = ['BY', 'SL', 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W','KLU','KUR', 'KSZ','KOW','KNW','KZG','KFR','KSO','KBL','KAI','KGR','KAG','KTI','KVS','KJU']
        if state_code in valid:
            assumption_day = datetime.date(self.year, 8, 15)
            self.holiday_list.append([assumption_day, u'Mariä Himmelfahrt'])

    def get_reformation_day(self, state_code):
        """ Reformationstag """
        valid = ['BB', 'MV', 'SN', 'ST', 'TH']
        if state_code in valid:
            reformation_day = datetime.date(self.year, 10, 31)
            self.holiday_list.append([reformation_day, u'Reformationstag'])

    def get_all_saints_day(self, state_code):
        """ Allerheiligen """
        """ Oesttereicher Tage hinzugefuegt ab 6."""
        """ Schweizer Tage hinzugefuegt ab 15."""
        valid = ['BW', 'BY', 'NW', 'RP', 'SL', 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W','KLU','KUR','KSZ','KOW','KNW','KGL', 'KZG','KFR','KSO','KAI','KSG','KGR','KAG','KTI','KVS','KJU']
        if state_code in valid:
            all_saints_day = datetime.date(self.year, 11, 1)
            self.holiday_list.append([all_saints_day, u'Allerheiligen'])

    def get_repentance_and_prayer_day(self, state_code):
        """
        Buß und Bettag
        (Mittwoch zwischen dem 16. und 22. November)
        """
        valid = ['SN']
        if state_code in valid:
            first_possible_day = datetime.date(self.year, 11, 16)
            rap_day = first_possible_day
            weekday = rap_day.weekday()
            step = datetime.timedelta(days=1)
            while weekday != 2:
                rap_day = rap_day + step
                weekday = rap_day.weekday()
            self.holiday_list.append([rap_day, u'Buß und Bettag'])

    def get_corpus_christi(self, state_code):
        """ Fronleichnam 60 Tage nach Ostersonntag """
        """ Oesttereicher Tage hinzugefuegt ab 7. """
        valid = ['BW','BY','HE','NW','RP','SL', 'B','K', 'N','O', 'S', 'ST','T', 'V', 'W']
        if state_code in valid:
            corpus_christi = self.get_holiday(60)
            self.holiday_list.append([corpus_christi, u'Fronleichnam'])

    def get_easter_date(self, year):
        """ Berechnung Ostersonntag nach Lichtenberg """
        """ (http://de.wikipedia.org/wiki/Gau%C3%9Fsche_Osterformel)"""
        """(Korrektur wenn Ostersonntag nach dem 25.April"""

        a = self.year % 19
        k = self.year // 100
        m = 15 + (3 * k + 3) // 4 - (8 * k + 13) // 25
        d = (19 * a + m) % 30
        s = 2 - ( 3 * k + 3) // 4
        r = (d + a // 11) // 29
        og = 21 + d -r
        sz = 7 - (self.year  + self.year  // 4 + s) % 7
        oe = 7 - (og - sz) % 7
        os = (og + oe)
        month, day = divmod(os, 31)
        month = month + 3
        easter_day = datetime.date(self.year, month, day)
        return easter_day

print("init")
frei = holidays(2016,'KVS')
Holidays.is_holiday(datetime.date.today(),2016,'KVS')

Warum wird das hier so komisch angezeigt? Und wie verfahgren wir weiter? Event das Ical plugin umbauen, das er die Feiertage in den Kalender einträgt? oder irgendwie anders?

Bonze255 commented 8 years ago

hat jemand schon gesehen das wir die dateutil lib von gustavo niemayer https://github.com/dateutil/dateutil unter lib/3rd/dateutil drin haben ..? ich glaube die ist auch ziemlich mächtig...

wäre es nicht besser wir wuerden eine allgemien basic/ helper classe machen, in der solche sachen easy ready to usw drin stehen und man nurnoch mit getXYZ() sich die daten holen kann ?

cstrassburg commented 8 years ago

Kannst du bitte mit dem Autor klären, ob wir seine Entwicklung frei verwenden und verteilen dürfen. Danke

Bonze255 commented 8 years ago

Bin mal gespannt ob man den erreicht. ... Die seite wurde 2011 das letzte mal aktualisiert, sein code iszs aber nurnoch zu 40%, man koennte sogar die eine funktion zur berechnung des Osterfeiertages entfernen und aus dateutil benutzen ..

Bonze255 commented 8 years ago

"Hallo,

ihr könnt meine Feiertagsberechnung gerne verwenden.

VG Stephan John"

dann wär das auch geklärt, dann müssen wir uns nurnoch über ne Form der Verwendung im klaren sein, ical ist doch nur ne Formatierung? gibts da kein "Generatorschnipsel"? der unser Datendict in Ical wandelt?

psilo909 commented 8 years ago

heute wäre übrigens mein erster bedarf für die berechnung gewesen; tor ging um 6:30 trotz feiertag auf.. irgendwie brauchen wir doch ne lösung dafür. werde mir ical mal ansehen. eine env variable die 1 ist wenn feiertag ist wäre aber cooler. das könnte man ja 1 mal nachts berechnen. allerdings bräuchten wir mehr regioneninfos..

psilo909 commented 8 years ago

gerade ical angeschaut. das ding bietet ja defakto nur hilfsfunktionen an die dinge tun, bspw holidays = sh.ical('http://cal.server/holidays.ical')

dementsprechend kann man ja wirklich das feiertagsskript als banales plugin dazunehmen. Ich bereite mal einen vorschlag auf, würde aber ungern ins repo commiten, da ich mit dem autor die klärung nicht erledigt habe und der code ja auch gesamthaft nicht von mir ist. bonze könnte ggf als PR einstellen.

psilo909 commented 8 years ago

so ich habe das teil jetzt als plugin umgebaut, so dass es mir via

import datetime sh.holidays.calculate(datetime.date.today().year, "BY") sh.date.holiday(sh.holidays.is_holiday(datetime.date.today()))

mein bool "date.holiday" item setzt. ich triggere jeden tag 00:01 die Berechnung via Logik neu. Gibt sicher geschmeidigeres, aber ich denke so funzt das erstmal. Kann den angepassten Code gerne mal schicken, habe paar Bugs behoben und die meisten Funktionen/Attribute private gemacht... Bzgl Ausgabe des Namens als String sorge ich mich etwas um die Mehrsprachigkeit von SH.py. Da brauchen wir mal ein Konzept...

ohinckel commented 8 years ago

Wenn es darum geht abhaengig von einem Kalender (ical) Items auf true/false zu setzen, dann kann man auch meine ical-Plugin-Erweiterung verwenden (https://github.com/mknx/smarthome/pull/58). Den Aufwand die Feiertage zu berechnen war mir erstmal zu gross, weshalb ich mir per cron einen ical-Feiertagskalender aus dem Internet herunterlade und diesen dann per ical-Plugin auf Items anwende. Relativ einfach und flexibel.

Waere dann evetuell auch ein PR den man in smarthomeNG integrieren koennte (falls interessant, wuerde ich einen PR dafuer einstellen - siehe auch #52).

psilo909 commented 8 years ago

@ohinckel ist sicher auch eine clevere lösung, sehe ich aber irgendwie als anderen ansatz an, der sich zudem eine weitere "online" abhängigkeit "on board" holt.

ich hatte erst auch gedacht, dass die info über den feiertag nicht soo wichtig ist, aber gestern als das tor + rollläden um 6:00 aufgefahren sind hatte ich dann doch nochmal drüber nachgedacht. ideal wäre, bspw schon beim crontab irgendwie sagen zu können "nicht am Feiertag" bzw "am Feiertag". und wenn die berechnung 1mal am tag ist, dürfte sie die performance auch nicht killen. bei der implementierung oben kann man zudem sicher noch optimieren.

mir aber gleich, als plugin funktioniert es bei mir nun erstmal so.

Bonze255 commented 8 years ago

Was auch gut wäre, wenn wir mit timedelta das so machen, das man prüfen kann, ob morgen ein feiertag ist, dann koennte man heute die rolllaeden verzoegert fahren z.b.

ohinckel commented 8 years ago

zudem eine weitere "online" abhängigkeit "on board" holt.

Ich speichere mit die Kalender lokal nochmal und aktualisiere sie nur wenn notwendig (z.B. aendern sich Feiertage hoechstens einmal im Jahr und dieser Kalender muss nur dann aktualisiert werden).

beim crontab irgendwie sagen zu können "nicht am Feiertag" bzw "am Feiertag".

Der Scheduler berechnet nach der Ausfuehrung des Tasks bzw. beim Startup den naechsten Startzeitpunkt. Dort muesste man dann auch solche Sachen mit einfliessen lassen. Wenn das ueber ein ical-Plugin geht, ist das erstmal schwierig. Wenn die Feiertagsthematik aber ein Core-Bestandteil werden wuerde, waere es vermutlich einfacher (mit einer entsprechenden Syntax).

Ich persoenlich wuerde das vermutlich einfach in die Logik programmieren und dort die Pruefung machen.

Was auch gut wäre, wenn wir mit timedelta das so machen, das man prüfen kann, ob morgen ein feiertag

Das wuerde bereits jetzt schon mit dem aktuellen ical-Plugin gehen (siehen Plugin-Readme).

cstrassburg commented 8 years ago

Die Berechnung der Feiertage würde im Core Sinn manchen. Die könnte man dann auch für die Trigger als condition verwenden. Ical sollte schon ein Plugin bleiben.

Bonze255 commented 8 years ago

Ich dachte eher an dateutil, und auch an den core, ich finde auch, eine lokale Berechnung der Termine, hat den vortei, man macht sich von keiner externen seite abhaengig..

Adminius commented 8 years ago

Was macht man mit Ausnahmen? Z.B. Mariä Himmelfahrt (15.08) ist in Nürnberg kein Feiertag :( Augsburg hat am 8.08 Friedensfest... und es gibt noch mehr davon...

Bonze255 commented 8 years ago

gut, der Code behandelt ja schon die Bundesländereinheitlichen Feiertage, wie man die Stadtspezifischen einbauen könnte, fällt mir auch nicht ein, das wäre denk ich fast unmöglich.

mein voschlag wäre, das man zumindest mal in der daily.py 2 Standart items berechnen lassen würde; z.B. isHoliday.today() und isHoliday.tomorrow() da ich denke, das für 90% der Anwender diese zwei fälle schon ausreihend sind.

daily wird ja nur 1 mal am Tag ausgeführt oder?

Adminius commented 8 years ago

wie wäre es mit Black/White Liste, die User selber pflegen/übergeben muss und wo die o.g. Ausnahmen stehen? Ich könnte zwar auch für mich Plugin anpassen, aber beim nächsten Update sind die Änderungen wieder weg :(

Foxi352 commented 8 years ago

Denkt auch an uns Ausländer 😇 Nicht jeder wohnt in einem "Bundesland" 😉 Eine Feiertagsberechnung müsste die Möglichkeit haben das Land mit anzugeben. Vielleicht wäre es möglich die Berechnung in eine Länderdatei zu schreiben die man als Parameter mit angibt. Ein bischen so wie mit den Backend Sprachdateien ?

ohinckel commented 8 years ago

Vielleicht wäre es möglich die Berechnung in eine Länderdatei zu schreiben die man als Parameter mit angibt.

Wobei ich dann fast schon bei der Frage bin: Warum nicht gleich ICS-Dateien aus dem Internet laden und diese hinterlegen? Bevor wir ein eigens Format/Parsing implementieren, koennte man gleich auf ein standardisiertes Format gehen. Und ich denke Feiertagskalender fuer alle moeglichen Laender gibt es sicherlich irgendwo im Internet.

Ich bin weiterhin noch fuer die Umsetzung via ICS-Dateien :wink: - vielleicht laest sich das ja doch noch mit dem ICAL-Plugin verheiraten...

thernst-de commented 8 years ago

Ich würde das anders machen. Die Feiertage in eine Config Datei. Da kann sich jeder ein- und auskommentieren was er braucht und was nicht. Und wenn jemand zusätzliche Feiertage hat kann er die da einfach zusätzlich eintragen.

Edit: ical ist zwar schön, aber eine manuelle Pflege ist aufwändiger ...

Foxi352 commented 8 years ago

Hmm... das mit dem ics hat aber schon seinen Charme. Aktuell mache ich das schon so mit einem Google Feiertagskalender. Bei mir fahren die Rolladen am Feiertag nicht hoch :-) Die Ausnahmen wie z.B. unseren Urlaub trage ich manuell ein in den bestehenden Kalender... Eine config Datei ist zwar einfach, muss aber immer manuell erstellt werden. Ist aber auch eine saubere Lösung ...

thernst-de commented 8 years ago

Ich würde die config Datei mit allen Feiertagen die hier irgendjemandem einfallen ausliefern. Die gängigen Feiertage aktiv, die anderen auskommentiert. Dann kann das jeder einfach anpassen. Zusätzlich die Datei noch ein wenig mit Kommentaren versehen ...

ohinckel commented 8 years ago

Wenn wir eine solche Datei anbieten, dann solte diese auch komplett sein (vermutlich wird das schwer und sie nie komplett sein). Und wenn man diese Datei sowieso anpassen muss dann wuerde ich mir lieber meinen Kalender mit meinem Smartgeraet anpassen wollen anstatt eine Datei bearbeiten zu muessen (Kalendereintraege habe ich schneler und bequemer angelegt).

Ausserdem waere es gut, wenn man nicht nur Feiertage in Logiken einbauen koennte, sondern auch andere Termine (z.B. wenn Kegelabend, dann Heizung runter oder wenn Geburtstag X, dann VISU-Notify oder ...). Faende ich ziemlich nett und flexibel und koennte ebenfalls als einheitliches Interface fuer Feiertage auch verwendet werden (z.B. wenn Termin in Feiertagskalender, dann Garagentor nicht oeffnen).

Das getrennt zu behandeln ist fuehlt sich nach Verdoppelung der ganzen Implementierung an.

thernst-de commented 8 years ago

Das wird aber dann schwierig mit der Steuerung, auf welche Termine die Steuerung reagieren soll. Sonst fährst du die heizung runter weil im Kalender eine geburtstagsfeier eingetragen ist, aber niemand der Kiste mitgeteilt hat, dass die bei dir daheim stattfindet ...

Die nächste Frage ist dann wie oft sich die Feiertage ändern. Im Regelfall doch eher nicht, oder? Also müssen die Feiertage einmal richtig ausgewählt werden und fertig

ohinckel commented 8 years ago

Das wird aber dann schwierig mit der Steuerung, auf welche Termine die Steuerung reagieren soll.

Das muss man so oder so im Griff haben, egal wie man die Termine ablegt oder verwaltet.

Sonst fährst du die heizung runter weil im Kalender eine geburtstagsfeier eingetragen ist, aber niemand der Kiste mitgeteilt hat, dass die bei dir daheim stattfindet ...

Mein Beispiel hast du falsch verstanden: Kegelabend, dann Heizung runter. Das andere Beispiel war: Geburtstag, dann Notifizierung auf der VISU anzeigen (wer merkt sich schon Geburtstage).

Das waren auch nur zwei Beispiel - da gibt es sicherlich tausend andere.

Die nächste Frage ist dann wie oft sich die Feiertage ändern. Im Regelfall doch eher nicht, oder? Also müssen die Feiertage einmal richtig ausgewählt werden und fertig

Ja, die aendern sich nicht. Mir geht es auch eher um die Bequemlichkeit. Ich setze lieber einen Termin anstatt dass ich in Dateien herumeditiere.

Und zweitens koennte man das ganze nicht nur fuer Feiertagstermine verwenden, sondern auch fuer alle sonstigen Termine auf die man reagieren moechte.

Ich glaube wir kommen aber nicht auf einen gruenen Zweig :wink: Von daher werde ich vermutlich auch mal versuchen, wie ich das im ICAL-Plugin umsetzen kann. Und wenn's dann nur fuer mich zum Einsatz kommt...

Bonze255 commented 8 years ago

Find das mit dem ical auch ok, allerdings muesste man jn der visu vl ein widget haben indem man die termine eintragen kann

thernst-de commented 8 years ago

Mein Beispiel hast du falsch verstanden: Kegelabend, dann Heizung runter. Das andere Beispiel war: Geburtstag, dann Notifizierung auf der VISU anzeigen (wer merkt sich schon Geburtstage).

Doch, ich denke schon, dass ich Dich hier verstanden habe. Meine Anmerkung bezog sich darauf, dass Du dem System ja auch irgendwie sagen musst, auf welchen ICAL-Eintrag das System reagieren soll und welcher Eintrag nur zu einer Notification führen darf.

Im Grunde finde ich ein ICAL-Plugin auch sehr gut. Ich denke nur, dass man die Feiertagsermittlung davon abkoppeln sollte, da man ansonsten die Feiertage immer wieder pflegen muss. Auch wenn es entsprechende ICAL-Dateien im Netz gibt, muss man die doch für jedes Jahr entsprechend einbauen.

Bonze255 commented 8 years ago

Im obigen code ist btw deutschland, schweiz und oesterreich implementiert

psilo909 commented 8 years ago

Ich hatte den Code auch schon zu einem Plugin umgebaut, soweit ich mich erinnere waren noch einige Problemchen zu lösen. Ich möchte mangels Grundlagen zur Klärung der Verwendbarkeit und Herkunft des Codes nur nicht selber einen PR stellen oder pushen und hatte es vor längerem mal @cstrassburg geschickt.. Dementsprechend ist das Ding nur bei mir lokal aktuell am Laufen.

Bonze255 commented 8 years ago

Naja grundlage, war ja von obigem autor, der uns die zustimmung gegeben hat, und ich habs dann auf die 3 laender umgestrickt

psilo909 commented 8 years ago

Die hat er Dir gegeben, ich habe keine Mail etc vorliegen, die das beweist. Zudem ist 90% des Codes ja auch nicht von mir, so dass ich mich nicht in der Rolle sehe das zu publishen - dazu hätte ich vermutlich sowieso noch mehr refactoren müssen und am Ende auch Support machen - AVM, Enigma2 und DWD reicht dazu ;-) Zudem war die Antwort auf die Frage bisher unklar, wie es offiziell eingebunden werden soll.

msinn commented 6 years ago

Da das Issue seit weit über einem Jahr idled und Rechte-Situation des obigen Codes unklar ist, mache ich das Issue erstmal zu.

Bei Bedarf kann es ja wieder geöffnet werden.