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.
Ä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:
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.
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
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.
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
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
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
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.
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:
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
Eine weitere Möglichkeit besteht darin, Feiertage, Ferien usw. in einer Exceltabelle zu hinterlegen.
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:
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
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
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.
Ein Beispielmodell mit allen hier gezeigten Subroutinen inklusive der benötigten Funktion zum Lesen von Tabellen steht im Download-Bereich zur Verfügung.
Möchten Sie mehr über dieses Thema erfahren oder haben weitere Fragen? Bitte kontaktieren Sie uns.
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…