Alexander Förster

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:


CREATE FUNCTION [dbo].[FuncOstersonntag] (@Jahr AS BIGINT)
returns DATETIME
AS
BEGIN
— Declare the return variable here
— Osterfunktion nach Carl Friedrich Gauß (1800). Rückgabewert
— ist das Datum des Ostersonntags im angegebenen 
— Jahr. Gültigkeitsbereich: 1583 – 8702 (auf das
— Auslösen von Laufzeitfehlern bei Unter- oder Überschreitung
— dieses Gültigkeitsbereichs wird hier absichtlich verzichtet).
DECLARE @a AS BIGINT
DECLARE @b AS BIGINT
DECLARE @c AS BIGINT
DECLARE @d AS BIGINT
DECLARE @e AS BIGINT
DECLARE @f AS BIGINT
DECLARE @date AS CHAR(8)

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


CREATE FUNCTION [dbo].[Gettageinklfeiertage] (@Jahr VARCHAR(4))
returns @TageinklFeiertag TABLE (
datum       DATE NOT NULL,
istfeiertag INT NOT NULL,
feiertag    VARCHAR(20) NOT NULL,
wochentag   VARCHAR(20) NOT NULL )
AS
BEGIN
DECLARE @Datum AS DATE
DECLARE @hdatum AS DATE
DECLARE @Feiertagname AS VARCHAR(20)
DECLARE @istftag AS INT
DECLARE @Ostern AS DATE

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

            INSERT INTO @TageinklFeiertag
VALUES      (@Datum,
@istftag,
@Feiertagname,
Datename(dw, @Datum))

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.