17.10.2016

Bewegliche Feiertage berechnen

Technical Value

Beim Aufbau eines Data Warehouse gehören Datumsdimensionen zum Standard. Im Regelfall sollen hierbei auch die Feiertage mit einfließen – eine einfache Anforderung, so lange es um ein feststehendes Datum geht. Will man indes bewegliche Feiertage berechnen, gestaltet sich diese Aufgabe ungleich komplexer. Zwar besteht die Möglichkeit, die betreffenden Daten anhand von statistischen Tabellen – wie man sie beispielsweise auf schulferien.org findet – manuell einzupflegen. Für moderne DWH-Lösungen mit hohem Automatisierungsgrad darf dies jedoch keine Option sein.

Bewegliche Feiertage berechnen mit Gauß-Algorithmus

Eine generische Lösung bietet der Gauß-Algorithmus zur Berechnung des Ostersonntags. Liegt dieser erst einmal vor, lassen sich auch weitere bewegliche Feiertage berechnen. Microsoft stellt in diesem Kontext eine Implementierung für Visual Basic  zur Verfügung. Jedoch lässt sich nicht in jedem Projekt ein selbstgeschriebener Code als CLR im SQL Server installieren. Daher ist es sinnvoll, diesen Code zunächst in die TSQL-Syntax zu überführen:

  1. CREATE FUNCTION [dbo].[FuncOstersonntag] (@Jahr AS BIGINT)
  2. returns DATETIME
  3. AS
  4. BEGIN
  5. -- Declare the return variable here
  6. -- Osterfunktion nach Carl Friedrich Gauß (1800). Rückgabewert
  7. -- ist das Datum des Ostersonntags im angegebenen
  8. -- Jahr. Gültigkeitsbereich: 1583 - 8702 (auf das
  9. -- Auslösen von Laufzeitfehlern bei Unter- oder Überschreitung
  10. -- dieses Gültigkeitsbereichs wird hier absichtlich verzichtet).
  11. DECLARE @a AS BIGINT
  12. DECLARE @b AS BIGINT
  13. DECLARE @c AS BIGINT
  14. DECLARE @d AS BIGINT
  15. DECLARE @e AS BIGINT
  16. DECLARE @f AS BIGINT
  17. DECLARE @date AS CHAR(8)
  18.  
  19. - Die "magische" Gauss-Formel anwenden: SET @a = @Jahr % 19 SET @b = @Jahr / 100 SET @c = ( 8 * @b + 13 ) / 25 - 2 SET @d = @b - ( @Jahr / 400 ) - 2 SET @e = ( 19 * ( @Jahr % 19 ) + ( ( 15 - @c + @d ) % 30 ) ) % 30 IF @e = 28 BEGIN IF @a > 10 BEGIN SET @e = 27 END END ELSE BEGIN IF @e = 29 BEGIN SET @e = 28 END END SET @f = ( @d + 6 * @e + 2 * ( @Jahr % 4 ) + 4 * ( @Jahr % 7 ) + 6 ) % 7 -- Rückgabewert als Datum bereitstellen SET @date = CONVERT(CHAR(8), @Jahr * 10000 + 101, 112) SET @date = CONVERT(CHAR(8), Dateadd(mm, 2, @date), 112) SET @date = CONVERT(CHAR(8), Dateadd(dd, @e + @f + 21, @date), 112) RETURN Cast(@date AS DATETIME) END

Nun muss man nur noch feste und bewegliche Feiertage berechnen. Im Folgenden eine Funktion, die die Feiertage für ein Jahr generiert:

  1. CREATE FUNCTION [dbo].[Gettageinklfeiertage] (@Jahr VARCHAR(4))
  2. returns @TageinklFeiertag TABLE (
  3. datum       DATE NOT NULL,
  4. istfeiertag INT NOT NULL,
  5. feiertag    VARCHAR(20) NOT NULL,
  6. wochentag   VARCHAR(20) NOT NULL )
  7. AS
  8. BEGIN
  9. DECLARE @Datum AS DATE
  10. DECLARE @hdatum AS DATE
  11. DECLARE @Feiertagname AS VARCHAR(20)
  12. DECLARE @istftag AS INT
  13. DECLARE @Ostern AS DATE
  14. SELECT @ostern = [dbo].[Funcostersonntag] (@jahr) SET @Datum =Cast (@Jahr * 10000 + 100 + 1 AS VARCHAR(10)) SET @hdatum = CONVERT (VARCHAR (10), ( Cast (@Jahr AS INT) + 1 ) * 10000 + 100 + 1) WHILE @Datum < @hdatum BEGIN SET @Feiertagname ='' SET @istftag = 0 IF Day(@Datum) = 1 AND Month (@Datum) = 1 BEGIN SET @Feiertagname = 'Neujahr' SET @istftag = 1 END IF Day(@Datum) = 1 AND Month (@Datum) = 5 BEGIN SET @Feiertagname = 'Tag der Arbeit' SET @istftag = 1 END IF Day(@Datum) = 3 AND Month (@Datum) = 10 BEGIN SET @Feiertagname = 'Tag der Deutschen Einheit' SET @istftag = 1 END IF Day(@Datum) = 1 AND Month (@Datum) = 11 BEGIN SET @Feiertagname = 'Allerheiligen' SET @istftag = 1 END IF Day(@Datum) = 25 AND Month (@Datum) = 12 BEGIN SET @Feiertagname = '1.Weihnachstag' SET @istftag = 1 END IF Day(@Datum) = 26 AND Month (@Datum) = 12 BEGIN SET @Feiertagname = '2.Weihnachstag' SET @istftag = 1 END IF @Datum = Dateadd(d, -2, @Ostern) BEGIN SET @Feiertagname = 'Karfreitag' SET @istftag = 1 END IF @Datum = @Ostern BEGIN SET @Feiertagname = 'Ostersonntag' SET @istftag = 1 END IF @Datum = Dateadd(d, 1, @Ostern) BEGIN SET @Feiertagname = 'Ostermontag' SET @istftag = 1 END IF @Datum = Dateadd(d, 39, @Ostern) BEGIN SET @Feiertagname = 'Christi Himmelfahrt' SET @istftag = 1 END IF @Datum = Dateadd(d, 50, @Ostern) BEGIN SET @Feiertagname = 'Pfingstmontag' SET @istftag = 1 END IF @Datum = Dateadd(d, 60, @Ostern) BEGIN SET @Feiertagname = 'Fronleichnahm' SET @istftag = 1 END
  15.             INSERT INTO @TageinklFeiertag
  16. VALUES      (@Datum,
  17. @istftag,
  18. @Feiertagname,
  19. Datename(dw, @Datum))
  20. SET @Datum= Dateadd(d, 1, @Datum) END RETURN END

Die resultierende Tabelle kann natürlich durch weitere, erforderliche Datumsinformationen ergänzt werden. Zudem sollte die Tabelle zumindest für Ostersonntag bis ins Jahr 8702 die richtigen Ergebnisse liefern. Aktuell sind nur die Feiertage aus Nordrhein-Westfalen implementiert. Dabei ist zu beachten, dass in 2017 aufgrund des 500. Jahrestages der Reformation auch der 31. Oktober hinzu kommt. Wie sich unschwer am Code erkennen lässt, ist dieser Sachverhalt ebenfalls noch nicht berücksichtigt.

Teilen auf

Newsletter Anmeldung

Abonnieren Sie unseren Newsletter!
Lassen Sie sich regelmäßig über alle Neuigkeiten rundum ORAYLIS und die BI- & Big-Data-Branche informieren.

Jetzt anmelden