Neuer Release, neues Glück! In INOSIM ist es seit Version 2025.1 möglich, Schichtkalender dynamisch zu ändern. In diesem Tipp & Trick stellen wir Ihnen vor, wie Sie per VBA regelmäßige Arbeitszeiten, Feiertage und Ausnahmen setzen können. Ein Beispielmodell ist ebenfalls verlinkt. Nach einer allgemeinen Einführung in das neue Feature präsentieren wir Ihnen drei Methoden, wie Sie Feiertage aus öffentlich zugänglichen Datenquellen bzw. aus eigenen Listen automatisiert in INOSIM einlesen oder direkt anhand der Gauß’schen Osterformel berechnen können.

Allgemeines zur Änderbarkeit des Schichtkalenders

Änderungen am Schichtkalender sind nur möglich, solange dieser noch nicht von der Simulation aufgerufen wurde, also z. B. in der Simulationsinitialisierung. Das ShiftCalendar-Objekt besitzt die folgenden Eigenschaften:

  • EnableValue-Eigenschaft. Legt fest, ob ein Schichtkalender Werte oder Verfügbarkeiten enthält (True oder False)
  • DefaultValue-Eigenschaft. Legt den Standardwert eines Schichtkalenders fest. (0: nicht Verfügbar; >0 : Verfügbar / Wert wenn EnableValue=True)
  • WorkingDays-Eigenschaft. Liefert die Zeitbereiche der Wochentage eines Schichtkalenders.
  • Holidays-Eigenschaft. Liefert die Datumsbereiche der Feiertage eines Schichtkalenders.
  • Exceptions-Eigenschaft. Liefert die Datumsbereiche der Ausnahmen eines Schichtkalenders.

Eintragen regelmäßiger Arbeitszeiten in den Schichtkalender

Regelmäßige Arbeitszeiten können über die WorkingDays-Eigenschaft dem Schichtkalender hinzugefügt werden. Zunächst werden DayPeriod-Objekte generiert, die den Wert des Schichtkalenders für einen Zeitbereich eines Wochentags festlegen. Dafür müssen die Eigenschaften Weekday, StartTime und EndTime gesetzt werden.

  • Die Weekday-Eigenschaft legt den Wochentag eines Zeitbereichs eines Schichtkalenders fest und wird über Integer-Werte (1-7, entsprechen Sonntag – Samstag) oder die Basic-Konstanten vbMonday, vbTuesday usw. gesetzt.
  • Die StartTime-Eigenschaft legt die Startzeit des Zeitbereichs fest und wird über Integer-Werte (Sekunden seit Mitternacht) gesetzt. Ist die Eigenschaft Nothing, entspricht dies dem Tagesbeginn.
  • Die EndTime-Eigenschaft legt die Endzeit des Zeitbereichs fest und wird über Integer-Werte (Sekunden seit Mitternacht) gesetzt. Ist die Eigenschaft Nothing, entspricht dies dem Ende des Tages.

Ein DayPeriod-Objekt kann der WorkingDays-Eigenschaft des Schichtkalenders über die Add-Methode hinzugefügt werden (zusätzlich existieren auch eine Remove– und eine Clear-Methode).

Mit der folgenden Subroutine werden DayPeriods für Arbeitszeiten von Montag bis Freitag zwischen 8:00 und 17: Uhr gesetzt und dem Schichtkalender hinzugefügt.

Sub AddWorkingDays
    Dim Cal As ShiftCalendar
    Dim i As Integer

    Cal = ShiftCalendars("Schichtkalender")
    'Default: no working time
    Cal.DefaultValue = 0

    For i = vbMonday To vbFriday
        Dim pWeek As New DayPeriod
        pWeek.Weekday = i
        'Start work at 08:00 am
        pWeek.StartTime = 8 * 3600
        'Stop work at 05:00 pm
        pWeek.EndTime = 17 * 3600
        'Set working hours as working time
        pWeek.Value = 1
        'Add working hours to shift calendar
        Cal.WorkingDays.Add pWeek
    Next
End Sub

Eintragen von Feiertagen in den Schichtkalender

Feiertage können über die Holidays-Eigenschaft dem Schichtkalender hinzugefügt werden. Dafür werden DatePeriod-Objekte generiert, die die Datumsbereiche der Feiertage enthalten. Hier müssen die Eigenschaften StartDate und EndDate gesetzt werden.

  • Die StartDate-Eigenschaft legt das Startdatum des Zeitbereichs fest und kann als Simulationsdatum gesetzt werden. Ist die Eigenschaft Nothing, entspricht dies dem Startzeitpunkt der simulierten Zeit.
  • Die EndDate-Eigenschaft legt die Endzeit des Zeitbereichs fest und kann als Simulationsdatum gesetzt werden. Ist die Eigenschaft Nothing, entspricht dies dem Endzeitpunkt der simulierten Zeit.

Ein DatePeriod-Objekt kann der Holidays-Eigenschaft des Schichtkalenders über die Add-Methode hinzugefügt werden (zusätzlich existieren auch eine Remove– und eine Clear-Methode).

Mit der hier gezeigten Subroutine werden dem Schichtkalender Feiertage aus einer (zuvor definierten) Feiertagstabelle Holiday_Table hinzugefügt.

Sub AddHolidays
    Dim Cal As ShiftCalendar
    Dim Holiday_Table As New Table
    Dim i As Integer

    Cal = ShiftCalendars("Schichtkalender")
    'calcHolidays(Holiday_Table)
    'readICS(Holiday_Table, "C:\Users\MaxMustermann\Holidays.ics")  
    'readHolidayTable(Holiday_Table, "NW")

    For i = 1 To Holiday_Table.RowCount
        Dim pHoliday As New DatePeriod
        'Add holidays from holidays table to holiday period
        pHoliday.StartDate = Holiday_Table(i, 1)
        'Check if holidays table has 2nd column (holiday end dates; for ICS import)
        If Holiday_Table.ColumnCount = 2 Then
            pHoliday.EndDate = Holiday_Table(i, 2)
        Else
            'If no 2nd column, set end date in 24 hours
            pHoliday.EndDate = pHoliday.StartDate + 24 * 3600
        End If
        'Set holiday period as no working time
        pHoliday.Value = 0
        'Add holiday period to shift calendar
        Cal.Holidays.Add pHoliday
    Next
End Sub

Die Feiertagstabelle wird in diesem Beispiel mithilfe einer der drei auskommentierten Subroutinen

  • calcHolidays
  • readHolidayTable
  • readICS

gefüllt. Welche Optionen sich dahinter verbergen, erfahren Sie im Abschnitt Subroutinen zum Setzen von Feiertagen.

Die Verwendung einer Tabelle ist optional, es können auch Daten direkt übergeben werden, z. B. via

  • pHoliday.StartDate = DateToSimDate(New Date(yyyy, mm, dd))
  • pHoliday.EndDate = DateToSimDate(New Date(yyyy, mm, dd))

Eintragen von Ausnahmeregelungen in den Schichtkalender

Ausnahmen können über die Exceptions-Eigenschaft dem Schichtkalender hinzugefügt werden. Dafür werden, analog zur Behandlung von Feiertagen, DatePeriod-Objekte generiert, die die Datumsbereiche der Ausnahmen enthalten.

Die folgende Subroutine setzt für Heiligabend und Silvester eine Schichtpause ab 13:00 Uhr bis zum Beginn des Folgetages.

Sub AddExceptions
    Dim Cal As ShiftCalendar
    Dim pExceptions_hl_abend As New DatePeriod
    Dim pExceptions_silvester As New DatePeriod

    Cal = ShiftCalendars("Schichtkalender")

    'define start date (and time) of exceptions
    pExceptions_hl_abend.StartDate = DateToSimDate(New Date(2025, 12, 24, 13, 00, 00))
    pExceptions_silvester.StartDate = DateToSimDate(New Date(2025, 12, 31, 13, 00, 00))
    'define end date (and time) of exceptions
    pExceptions_hl_abend.EndDate = DateToSimDate(New Date(2025, 12, 25))
    pExceptions_silvester.EndDate = DateToSimDate(New Date(2026, 01, 01))
    'define exception periods as no working time
    pExceptions_hl_abend.Value = 0
    pExceptions_silvester.Value = 0
    'Add exception periods to shift calendar
    Cal.Exceptions.Add pExceptions_hl_abend
    Cal.Exceptions.Add pExceptions_silvester
End Sub

Subroutinen zum Setzen von Feiertagen

An dieser Stelle werden drei Möglichkeiten vorgestellt, mit denen das ehemals mühselige Eintragen von Feiertagen in den Schichtkalender weitestgehend automatisiert oder durch einen Import aus einer Excel-Tabelle oder sogar aus einer ICS-Datei realisiert werden kann.

Die hier gezeigten Beispiele beziehen sich auf Feiertage in Deutschland, können natürlich aber auch leicht auf andere Länder angepasst und um bspw. Revisionstermine oder Betriebsferien erweitert werden.

Automatische Erstellung von Feiertagen

Viele Feiertage sind an feste Daten gebunden und daher leicht zu hinterlegen. Ein paar Feiertage aber sind recht variabel. In Deutschland sind dies die Feiertage, die sich an Ostern orientieren. Ist der Ostersonntag bspw. bekannt, lassen sich die Daten aller anderen Feiertage leicht errechnen. Glücklicherweise lässt sich auch der Ostersonntag berechnen, dank der Gaußschen Osterformel.

In diesem Codebeispiel werden die Daten aller Feiertage eines Jahres in eine Feiertagstabelle geschrieben, wobei die Feiertage, die sich an Ostern orientieren, mithilfe der Gaußschen Osterformel berechnet werden. Die Implementierung der Formel wurde hier von der Physikalisch-Technischen Bundesanstalt übernommen.

Die für Ihr Bundesland spezifischen Feiertage können einfach „einkommentiert“ werden.

Als Eingangsparameter der Subroutine werden übergeben:

  • Die Feiertagstabelle Holiday_Table
Sub calcHolidays(ByRef Holiday_Table As Table)
    '#############################################################################
    'Calculates variable holidays referring to Easter via Gauss's Easter Algorithm,
    'other fixed holidays are also defined.
    'State specific (Germany) holidays may be commented in.

    'Holiday_Table: Return table containing the read holidays
    '############################################################################
    Dim  i As Integer, j As Integer, k As Integer, l As Integer, m As Integer, n As Integer, o As Integer, p As Integer, q As Integer
    Dim cur_year As Integer

    cur_year = Year(SimDateToDate(Simulation.SimDate))

    'Holidays not referring to Easter
 '##########################################################################################

    'Neujahr
    Holiday_Table(1, 1) = DateToSimDate(DateSerial(cur_year, 01, 01))

    'Hl. drei Könige; Baden-Württemberg, Bayern und Sachsen-Anhalt
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 01, 06))

    'Int Frauentag; Berlin und Mecklenburg-Vorpommern
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 03, 08))

    'Tag der Arbeit 
    Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 05, 01))

    'Jahrestag der Befreiung vom Nationalsozialismus und Ende des Zweiten Weltkriegs; Berlin
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 05, 08))

    'Augsburger Friedensfest; Bayern
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 08, 08))

    'Mariä Himmelfahrt; Bayern und Saarland
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 08, 15))

    'Weltkindertag; Thüringen
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 09, 20))

    'Tag der deutschen Einheit
    Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 10, 03))

    'Reformationstag; Brandenburg, Bremen, Hamburg, Mecklenburg-Vorpommern, Niedersachsen, Sachsen, Sachsen-Anhalt, Schleswig-Holstein und Thüringen
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 10, 31))

    'Allerheiligen; Baden-Württemberg, Bayern, Nordrhein-Westfalen, Rheinland-Pfalz und Saarland
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 11, 01))

    'Buß- und Bettag; Sachsen
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 11, 19))

    '1. Weihnachtsfeiertag
    Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 12, 25))

    '2. Weihnachtsfeiertag
    Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 12, 26))

    'Calculation of Easter Sunday based on Gauss's Easter Algorithm, implementation taken from Physikalisch-Technische Bundesanstalt
    'https://www.ptb.de/cms/ptb/fachabteilungen/abt4/fb-44/ag-441/darstellung-der-gesetzlichen-zeit/wann-ist-ostern.html
    '###############################################################################################################################

  i = Int(cur_year / 100)
  j = 15 + Int((3 * i + 3) / 4) - Int((8 * i + 13) / 25)
  k = 2 - Int((3 * i + 3) / 4)
  l = cur_year Mod 19
  m = (19 * l + j) Mod 30
  n = Int(m / 29) + (Int(m / 28) - Int(m / 29)) * Int(l / 11)
  o = 21 + m - n
  p = 7 - (cur_year + Int(cur_year / 4) + k) Mod 7
  q = o + 7 - (o - p) Mod 7

    'Holidays referring to Easter
 '##########################################################################################

    'Karfreitag
    Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 03, q - 2) )

    'Ostersonntag; Brandenburg
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 03, q))

    'Ostermontag
    Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 03, q + 1))

    'Christi Himmelfahrt
    Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 03, q + 39))

    'Pfingstsonntag; Brandenburg
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 03, q + 49))

    'Pfingstmontag
    Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 03, q + 50))

    'Fronleichnam; Baden-Württemberg, Bayern, Hessen, Nordrhein-Westfalen, Rheinland-Pfalz und Saarland
    'Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(cur_year, 03, q + 60)) 
End Sub

Auslesen von Feiertagen aus Excel-Tabellen

Eine weitere Möglichkeit besteht darin, Feiertage, Ferien usw. in einer Exceltabelle zu hinterlegen.

Excel-Tabelle mit deutschen Feiertagen

Im hier gezeigten Beispiel sind in einer Excel-Tabelle alle möglichen Feiertage des Jahres 2025 in Deutschland aufgelistet und es wurde hinterlegt, in welchem Bundesland der Tag auch wirklich ein Feiertag ist. So lassen sich diese Feiertage ganz einfach auslesen und in den Schichtkalender integrieren. Hierfür benötigen Sie die Funktion Read_Table, die Sie in einem anderen Tipp & Trick (Table-Objekte anwenden) finden können.

An die Subroutine übergeben werden hier:

  • Die Feiertagstabelle Holiday_Table
  • Das Bundeslandkürzel BL
Sub readHolidayTable(ByRef Holiday_Table As Table, ByVal BL As String)
    '##########################################################################################
    'Reads a table from an Excel sheet containing the information on the date of a holiday and
    'checks whether the holiday is actually a holiday in the specified state of Germany.
    'Holiday_Table: Return table containing the holidays selected by state
    'BL: State, possible input: BW  BY  BE  BB  HB  HH  HE  MV  NI  NW  RP  SL  SN  ST  SH  TH
'##########################################################################################

    Dim HolidayBLTable As New Table
    Dim i As Integer
    'read Excel sheet
    HolidayBLTable = Read_Table("Holidays", 1, 1, 0, 0, 1, 1)
    'check if the holiday is holiday in the selected state and write into return table
    For i = 1 To HolidayBLTable.RowCount
        If HolidayBLTable(i, BL) Then Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(HolidayBLTable(i, "Datum"))
    Next
End Sub

Auslesen von Feiertagen aus ICS-Dateien

Haben Sie einen Feiertagskalender auf dem PC oder dem Smartphone? Dann exportieren Sie ihn doch in eine ICS-Datei (auch iCalendar-Datei genannt) und importieren die Datei in Ihren INOSIM Schichtkalender mit Hilfe des letzten Beispiels.

In der hier vorgestellten Subroutine übergeben Sie

  • die Feiertagstabelle Holiday_Table
  • den Pfad zu Ihrer ICS-Datei Path (z. B. C:\Users\Max Mustermann\Kalender.ics)
Sub readICS(Holiday_Table As Table, Path As String)
    '#################################################################
    'Reads an ICS calendar file and extracts all specified dates. Make sure the 
    'file only contains dates that should be imported (e.g. state specific holidays / exclude dates of other states).
    '**IMPORTANT** For dates marked as frequently (e.g., by:"RRULE:FREQ=YEARLY") only the first occurrence is imported.
    'Holiday_Table: Return table containing the read holidays
    'Path: Path to the specified ICS file
    '#################################################################

    Dim ICS_Date As String
    Dim Holidays() As String
    Dim i, row As Integer
    
    FileOpen(1, Path, OpenMode.Input)
    While Not EOF(1)
        ICS_Date = LineInput(1)
        If InStr(ICS_Date, "DTSTART;VALUE=DATE:") <> 0 Then
            'Read start date of holiday(s), convert and save to return table
            Holidays() = Split(ICS_Date, "DTSTART;VALUE=DATE:")
            For i = 1 To Holidays.Length - 1
                Holiday_Table(Holiday_Table.RowCount + 1, 1) = DateToSimDate(DateSerial(CShort(Mid(Holidays(i), _
1, 4)), CShort(Mid(Holidays(i), 5, 2)), CShort(Mid(Holidays(i), 7, 2))))
            Next
        End If

        If InStr(ICS_Date, "DTEND;VALUE=DATE:") <> 0 Then
            'If an end date is specified in ICS file, read end date of holiday(s), convert and save to 2nd column of return table
            Holidays() = Split(ICS_Date, "DTEND;VALUE=DATE:")
            For i = 1 To Holidays.Length - 1
                Holiday_Table(row + 1, 2) = DateToSimDate(DateSerial(CShort(Mid(Holidays(i), 1, 4)),_
 CShort(Mid(Holidays(i), 5, 2)), CShort(Mid(Holidays(i), 7, 2))))
                row += 1
            Next
        End If
    End While
    FileClose(1)
End Sub

Doch Vorsicht: Die Subroutine unterscheidet nicht nach der Art der Termine in der ICS-Datei. Es werden alle Kalendereinträge importiert, egal ob Feiertage, die Geburtstage der Kinder oder der Zahnarzttermin. Stellen Sie also sicher, nur die gewünschten Daten zu exportieren.

Außerdem kann bei Terminen, die mit der Einstellung einer bestimmten Wiederholfrequenz hinterlegt wurden, nur der erste Eintrag berücksichtigt werden.

Download

Ein Beispielmodell mit allen hier gezeigten Subroutinen inklusive der benötigten Funktion zum Lesen von Tabellen steht im Download-Bereich zur Verfügung.

Downloads

Fragen?

Möchten Sie mehr über dieses Thema erfahren oder haben weitere Fragen? Bitte kontaktieren Sie uns.

Array ( [posts_per_page] => 3 [post_type] => [category__in] => Array ( [0] => 171 ) [orderby] => rand [order] => ASC )

Mehr Tipps & Tricks

15. November 2023

Transferanalyse mit Power BI

In diesem Tipp und Trick wird das Transfer-Analyse-Dashboard vorgestellt. Es handelt sich um eine Erweiterung des Power BI-Standard-Dashboards, mit dem Sie den Netto-Massenfluss einer Teilanlage…

Oft werden INOSIM-Modelle von mehreren Personen gewartet. Eine Herausforderung besteht dann darin, allen Beteiligten die aktuellste Version zur Verfügung zu stellen und sie konsistent zu…

Nutzen Sie Ihr Excel-Wissen bei der Arbeit mit INOSIM In diesem Tipp und Trick möchten wir Ihnen zeigen, wie Sie die Standard-Visual Basic Befehle von…

mehr

INOSIM Kontakt

Zu den lokalen Geschäftszeiten

Deutschland +49 231 97 00 250