Datums- und Stundenberechnung mit Feldern, Textmarken, Textformularfeldern, Tabellenzellen und Textfeldern auf einer UserForm

1. Ich möchte ein Haltbarkeitsdatum als dynamisch berechnendes Feld in eine Dokumentenvorlage einbringen. Wie sieht die Syntax des Feldcodes dazu aus?

2. Eine Feldfunktion Quartal kennt MS Word nicht. Wie lässt sich eine solche Feldfunktion - Dank Bedingungsfeldern - dennoch implementieren?

3. Eine Feldfunktion Kalenderwoche kennt MS Word nicht. Wie kann eine entsprechende benutzerdefinierte Funktion implementiert werden?

4. Ich möchte mit Textmarken Datumsberechnungen durchführen. Wie stelle ich das an?

5. Ich möchte mit Textformularfeldern Datumsberechnungen durchführen. Wie stelle ich das an?

6. Ich möchte meine Präsenszeiten in einer Word-Tabelle führen und berechnen. Wie muss ich dabei vorgehen?

7. Ich möchte für eine UserForm die Werktage (ohne Samstage, Sonntage und Feiertage) berechen. Wie sieht eine entsprechende Ereignisprozedur aus?

Diese Seite

Einleitung

Word-Felder beherrschen die Datumsarithmetik nicht. Mit Biegen und Brechen ist es aber dennoch möglich, einfache Datumsberechnungen durchzuführen. Mit monströsen Felddefinitionen ist es auch möglich, immerhin bis zu 28 Tage nach vorne zu rechnen, wie die Arbeit von Chris Woodman beweist. Chris stellt auch gleich eine komfortable Benutzeroberfläche zur Verfügung, welche es erlaubt, solche Monsterfelddefinitionen vollautomatisch zu erstellen. Dieses Skript wird darauf noch zurückkommen.

Auch mit Textmarkenfeldern und Textformularfeldern gelingen mit vernünftigem Aufwand nur sehr einfache Datumsberechnungen. Dieselbe Aussage trifft auf Datumseingaben in Tabellenzellen zu. Sobald zwei unterschiedliche Monate bei der Berechnung involviert sind, hört es mit der Herrlichkeit bereits auf. Die Berechnung liefert dann nämlich unsinnige Ergebnisse. Für komplexere Berechnungen in diesem Bereich, sollten Sie deshalb auf VBA (Makros) zurückgreifen, um vom Komfort und der konsistenten Syntax einer veritablen Programmiersprache, profitieren zu können. Dieses Skript zeigt auf, wie Sie dabei vorgehen können.

Wie gibt man eine Feldfunktion in MS Word ein?

Tippen Sie die geschweiften Klammern nicht konventionell ein, das bringt gar nichts! Sie können die besprochenen Feldfunktionen wie folgt in das Word-Dokument einbringen:

  1. Betätigen Sie die Tastenkombination [Alt + F9] um die Ansicht der Feldcodes zu aktivieren.

  2. Generieren Sie das Gerüst für den neuen Feldcode, indem Sie die Tastenkombination [Strg + F9] betätigen. Sie sollten nun ein Pärchen geschweifter Klammern sehen. Wiederholen Sie diesen Vorgang allenfalls, wenn die Feldfunktion verschachtelt ist.

  3. Schreiben Sie nun in die generierten Klammern die Gleichung, wie in diesem Skript abgebildet.

  4. Betätigen Sie abermals die Tastenkombination [Alt + F9] um den Feldcode auszublenden und in die Normalansicht der Felder zurückzukehren, damit Sie das Resultat der Feldfunktion würdigen können.

Einfache Datumsberechnung mittels dynamischem Feldcode

Folgender Feldcode berechnet das Datum: Heute in zwei Jahre. Die Schaltjahrproblematik bleibt dabei unberücksichtigt. Statt der Feldfunktion TIME (bzw. ZEIT) könnten Sie 1:1 auch die Feldfunktion DATE (bzw. AKTUALDAT in Word 97) einsetzen.

Ab Word2000

{ TIME \@ "d.M." }{ = { TIME \@ "yyyy" } + 2}

(Dynmaische Feldfunktion - ab Word 2000 -, welche um 2 Jahre vordatiert.)


Word 97

{ ZEIT \@ "t.M." }{ = { ZEIT \@ "jjjj" } + 2}

(Dynmaische Feldfunktion - für Word 97 -, welche um 2 Jahre vordatiert.)


Folgender Feldcode berechnet das Datum des letzen Monats (ohne Tag). Mit dieser Funktion könnten Sie einen sich dynamisch anpassenden Brief im folgenden Sinne schreiben: Heute ist der 1. Februar 1999. Ich erlaube mir, Ihnen die Abrechnung für den Monat Januar 1999 zukommen zu lassen. Falls Sie ausschliesslich den Monat (ohne Jahr) anzeigen wollen, dann ersetzen Sie am Ende des Codes MMMM yyyy (bzw. MMMM jjjj) mit MMMM.

Ab Word2000

{ SET RP20030806 { IF { TIME \@ "M" } = 1 "1/12/{ = { TIME \@ "yyyy" } - 1 }" "1/{ = { TIME \@ "M" } - 1}/{ TIME \@ "yyyy" }" } }{ REF RP20030806 \@ "MMMM yyyy"}

(Dynmaische Feldfunktion, - ab Word2000 -, welche Monat und Jahr des verflossenen Monats anzeigen kann.)


Word 97

{ BESTIMMEN RP20030806 { WENN { ZEIT \@ "M" } = 1 "1/12/{ = { ZEIT \@ "jjjj" } - 1 }" "1/{ = { ZEIT \@ "M" } - 1}/{ ZEIT \@ "jjjj" }" } }{ REF RP20030806 \@ "MMMM jjjj"}

(Dynmaische Feldfunktion, - für Word 97 -, welche Monat und Jahr des verflossenen Monats anzeigen kann.)


Komplexer Feldcode zur Datumsberechnung von Chris Woodman

Funktionalität

DatBer1.gifWie schon in der Einleitung erwähnt wurde, gibt es eine Person, welche genug Können und Geduld aufgebracht hat, den Feldcode für eine Funktion heraus zu tüfteln, welche es erlaubt bis zu 28 Tagen zum aktuellen Datum hinzuzurechnen. Mehr noch, diese Person hat auch eine komfortable Oberfläche entwickelt, welche diesen Feldcode, automatisch generiert. Neben der Verzögerung in Tagen, können Sie auch das Format, in dem das mittels Feldfunktion errechnete Datum erscheinen soll, bestimmen.

Damit Sie sich ein Bild davon machen können, was Chris da geleistet hat: der Feldcode für eine Verzögerung von 14 Tagen sieht ab MS Word2000 wie folgt aus:


{ QUOTE { SET Delay "14" }{ SET "NextPrevMonth" { IF { DATE \@ "MM" } = 12 "1/97" "{ = { DATE \@ "MM" } + 1 }/97" } }{ SET "DaysInMonth" { IF { DATE \@ "MM" } <> 2 { = INT(30575*{ DATE \@ "MM" }/1000+1/2)-INT(30575*{ = { DATE \@ "MM" } - 1 }/1000+1/2) }{ IF { = INT({ DATE \@"yy" }/4 ) } = { = { DATE \@"yy" }/4 }"29" "28" } } }{ IF { = { DATE \@ "dd" } + { REF Delay } } <= { DaysInMonth }{ QUOTE { = { DATE \@ "dd" } + { REF Delay } \# "0" }" - "{ DATE \@ "MMM" }" - "{ DATE \@ "yy" }"" }{ QUOTE { = { DATE \@ "dd" } + { REF Delay } - { DaysInMonth } \# "0" }" - "{ NextPrevMonth \@ "MMM" }" - "{ IF { DATE \@ "MM" } <> 12 { DATE \@ "yy" }{ = { DATE \@ "yyyy" } + 1 \# "xx" } }"" } } }

(Ausgehend vom aktuellen Datum berechnet dieser Monsterfeldcode z.B. 14 Tage nach vorne. Entwickelt hat diesen Code "Chris Woodman". Chris stellt auch eine Oberfläche zur Verfügung, um solche Würmer, automatisch zu generieren.)


Bezugsquelle

Sie können die Lösung von Chris über den folgenden Link herunter laden:

Lösung verfügbar machen

Die Implementierung gestaltet sich einfach. Gehen Sie konkret wie folgt vor:

  1. Laden Sie die ZIP-Datei herunter, welche Sie auf der genannten Webseite vorfinden.

  2. Entzippen Sie die heruntergeladene Datei. Sie enthält eine einzige Datei mit dem Namen DelayDat.dot.

  3. Führen Sie einen Doppelklick auf diese Dokumentenvorlage aus.

  4. Starten Sie über die Symbolleiste, welche angezeigt wird, den Dialog.

  5. Bestimmen Sie wie viele Tage dazu gerechnet werden sollen und geben Sie das Format vor, indem das Datum angezeigt werden soll.

  6. Wenn Sie nun die Schaltfläche Ok betätigen, wird die Feldfunktion bei der Einfügemarke im Dokument eingefügt.

Datumsberechnungen für Felder mit Hilfe von VBA

Wesentlich flexibler und auch konsistenter kriegen Sie diese Sache in den Griff, wenn Sie VBA-Code einsetzen. Optional können Sie den VBA-Code nachdem Sie ihn getestet habe, in einem Prozess mit dem reservierten Name AutoOpen bzw. AutoNew einsetzen. Damit steht einer automatisierten Lösung eigentlich nichts mehr im Wege. Wie Sie dabei vorgehen müssen, erfahren Sie im folgenden Kapitel: Allgemeine Felder (Platzhalter).

Feldcode, um das aktuelle Quartal dynamisch anzuzeigen

Im Folgenden finden Sie den Feldcode, welcher benötigt wird, um in den Monaten Januar - März den Wert 1, zwischen April - Juni den Wert 2, zwischen Juli - September den Wert 3 und in den Monaten Oktober - Dezember den Wert 4, anzeigen zu lassen:

Ab Word2000

{ IF { TIME \@ "M" } < 4 "1" "{ IF { TIME \@ "M" } < 7 "2" "{ IF { TIME \@ "M" } < 10 "3" "4" }" }" }

(Feldfunktion, - ab Word2000 -, welche das aktuelle Quartal im Dokument anzeigen kann.)


Word 97

{ WENN { ZEIT \@ "M" } < 4 "1" "{ WENN { ZEIT \@ "M" } < 7 "2" "{ WENN { ZEIT \@ "M" } < 10 "3" "4" }" }" }

(Feldfunktion, - für Word 97 -, welche das aktuelle Quartal im Dokument anzeigen kann.)


Alternative


{ =INT(({ Date \@ "M" } -1) /3) + 1 \#"0" }

(Ein eleganterer Feldcode, um das Quartal dynamisch im Dokument anzuzeigen.)


Anmerkung

Feldcode mit einem Makro generieren lassen

Da das Einbringen der Feldfunktion, wegen der nötigen Verschachtelungen etwas kniffelig sein könnte, hier der VBA-Code, welche diese Feldfunktion an der Stelle der Einfügemarke in das Dokument oder in die Dokumentvorlage einbringt.


Sub QuartalFeldcodeEinbringen()
  Q = Chr(34)
  Dim oRange As Range, F1 As Field
  Set oRange = ActiveDocument.Range(Selection.Start, Selection.End)
  For i = 1 To 3
    Set F1 = ActiveDocument.Fields.Add(oRange, wdFieldIf, "", False)
    oRange.SetRange F1.Code.End - 1, F1.Code.End - 1
    ActiveDocument.Fields.Add oRange, wdFieldTime, "\@ " & Q & "M" & Q, False
    If i > 1 Then
      F1.Select: Selection.InsertAfter Q
    End If
    oRange.SetRange F1.Code.End - 1, F1.Code.End - 1
    oRange.InsertAfter " < " & i * 3 + 1 & " " & Q & i & Q & " " & Q
    oRange.SetRange F1.Code.End - 1, F1.Code.End - 1
  Next i
  oRange.InsertAfter "4" & Q: Selection.Collapse: F1.Update
End Sub

(Bringt den Feldcode zum Anzeigen des aktuellen Quartals an der Stelle der Einfügemarke in das Dokument oder in die Dokumentvorlage ein.)


Allgemeine Felder (Platzhalter)

Einleitung

In diesem Kapitel wird erklärt, wie Sie das Ergebnis einer Berechnung, welche mit einem Makro (VBA) erreicht wurde, an geeigneter Stelle in das Dokument einbringen können. Am einfachsten wäre dies mit der Selection.TypeText Methode zu erreichen. Diese Methode setzt dann das Ergebnis dort ein, wo sich die Einfügemarke befindet. Ein lauffähiges Makro würde dann also wie folgt aussehen:


Sub Vordatieren()
  Dim d As Date
  d = Date + 14
  Selection.TypeText Text:=Format(d, "d. MMMM. yyyy")
End Sub

(Fügt am Ort der Einfügemarke das Datum "heute in 14 Tagen" ein. Der Formatschalter - im Beispiel "d. MMMM. yyyy" - kann selbstverständlich angepasst werden.)


Wenn das Dokument (bzw. die Dokumentvorlage) eine Art Maske darstellen soll, ist diese Methode allerdings nicht der Weisheit letzter Schluss. Viel mehr sind dann Platzhalter gefragt, welche Sie zur Entwurfszeit in die Dokumentvorlage einbringen können. Diese Platzhalter sollen dann später, z.B. wenn ein neues Dokument auf Basis der Vorlage erstellt wird, mit dem Ergebnis der VBA-Funktion versorgt werden.

Dazu eigenen sich Textmarkenfelder, welche in diesem Skript auch noch besprochen werden aber insbesondere auch Dokumentvariable oder Dokumenteigenschaften.

Dieses Kapitel beschränkt sich vorerst auf Dokumentvariable. Der Grund, wieso Dokumenteigenschaften in diesem Skript ebenfalls ins Spiel kommen werden, ist in einem Fehlverhalten von Word 97 zu suchen. Word 97 schmiert Ihnen nämlich reproduzierbar ab, sollten Sie versuchen, einen Verweis auf eine Dokumentvariable, statt in den Hauptteil des Dokumentes, in die Kopf-/Fusszeile oder in ein Textfeld einzubringen. Erst mit Word2000 wurde dieses krasse Fehlverhalten von Microsoft beseitigt.

Der Weg zu einer benutzerdefinierten Feldfunktion

Sie können in Ihrem Dokument oder in Ihrer Dokumentvorlage eine beliebige Anzahl von Dokumentvariablen definieren. Tun Sie dies in der Dokumentvorlage, dann werden diese Variablen an die Dokumente, welche auf Basis dieser Vorlage erstellt werden, vererbt. Im weiteren brauchen Sie einen Platzhalter im Dokument oder in der Dokumentvorlage, welcher auf die Dokumentvariable verweist und schliesslich benötigen Sie noch ein Stück VBA-Code, welcher einerseits die Berechnung durchführt und andererseits diese Dokumentvariable versorgt.

Ich versuche diesen etwas komplexen, aber sehr flexibel einsetzbaren Mechanismus zusammenzufassen:

  1. Sie fügen der Dokumentvorlage eine Dokumentvariable hinzu. Dabei handelt es sich vorerst einzig um eine Definition. Diese Variable wird beim Vorgang Datei - Neu... an das neu generierte Dokument vererbt. Das Definieren der Dokumentvariablen geschieht in der Regel genau einmal und ist nur unter Einsatz von VBA möglich. Wir sind nun so weit, dass die Dokumentvorlage (und später deren Kinder), von dieser Variable Kenntnis hat/haben. Sehen tun Sie noch nichts, es handelt sich lediglich um eine Veranlagung.

  2. Nun fügen Sie der Dokumentvorlage an geeigneter Stelle einen Platzhalter (man kann auch sagen eine Referenz auf diese Dokumentvariable) hinzu. Auch dieser Platzhalter wird an das künftige Dokument vererbt. Auch dieser Vorgang geschieht in der Regel genau einmal und zwar indem Sie über Einfügen - Feld... der Dokumentvorlage das Feld mit der Bezeichnung DocVariable hinzufügen. Damit wird bestimmt, an welcher Stelle im Dokument die Früchte unserer Bemühungen erscheinen sollen. Nun müssen wir aber auch noch liefern.

  3. Nun wird eine VBA-Routine (Makro), welche die Berechnung durchführt und die Dokumentvariable mit dem Ergebnis versorgt, angestossen. Damit der Platzhalter das (neue) Ergebnis der Dokumentvariablen auch annimmt, muss weiter sichergestellt werden, dass die Feldfunktion aktualisiert wird. Der Anstoss zu diesen beiden Vorgängen kann (bzw. soll) automatisch geschehen; typischerweise zum Zeitpunkt wenn ein neues Dokument generiert wird oder wenn ein bestehendes Dokument geöffnet wird. Im Gegensatz zu den ersten beiden Vorgängen ist dies ein sich ständig wiederholender Prozess, also eine dynamischer Angelegenheit. Dies ist auch das Ziel, denn wenn ich vierzehn Tage vom aktuellen Datum hinzu zähle, dann erhalte ich morgen schon ein anders Ergebnis als heute.

Mit diesen drei Schritten, haben wir genau das erreicht, was eine von MS Word intern zur Verfügung gestellte Feldfunktion auch tut. Der Unterschied liegt also beim Lösungsweg. Bei einer Word-integrierten Feldfunktion fallen die Schritte eins und drei weg.

Anmerkung zum Vorgehen

Szenario I - Benutzerdefinierte Feldfunktion: Heute in 14 Tage

In diesem Szenario geht es um folgendes: Es soll eine benutzerdefinierte Feldfunktion implementiert werden, welche das Datum Heute in 14 Tagen berechnet und das Resultat dynamisch im Dokument anzeigen kann.

Dokumentvariable in die Dokumentvorlage einbringen

Wie Im Vorspann bereits erwähnt wurde, ist das Definieren einer Dokumentvariablen ausschliesslich unter Einsatz von VBA möglich. Dabei handelt es sich aber um keine grosse Sache. Zuerst einigen wir uns auf den Namen für diese Variable und nennen sie Verfallsdatum. Konkret gehen Sie wie folgt vor:

  1. Öffnen Sie Ihre Dokumentvorlage explizit über den Dialog Datei - Öffnen....

  2. Wechseln Sie mit der Tastenkombination [Alt+F11] in die VBA-Umgebung.

  3. Im VBA-Editor wählen Sie ausgehend vom Hauptmenü Einfügen - Modul.

  4. Kopieren Sie den nachfolgenden drei-Zeiler in das leere Codefenster.

  5. Führen Sie das Makro mit dem Namen DokVariableHinzfuegen einmal aus.


Sub DokVariableHinzfuegen()
  ActiveDocument.Variables.Add Name:="Verfallsdatum"
End Sub

(Die Variable mit dem Namen "Verfallsdatum" wird der Dokumentvorlage bekannt gemacht.)


Das Hinzufügen einer Dokumentvariablen hinterlässt keine sichtbaren Spuren. Wenn Sie überprüfen wollen, ob es geklappt hat, rufen Sie das Makro gleich nochmals auf, dann erfolgt nämlich eine Fehlermeldung, welche besagt, dass das Ding bereits vorhanden ist. Sie erkennen daran, dass dieser Aufruf nur einmal im Leben der Vorlage geschehen muss bzw. geschehen soll.

Platzhalter in der Dokumentvorlage einfügen

DatBer2.gifDie nächste Aktion, dürfte Ihnen geläufig sein. Sie sollen eine Feldfunktion an geeigneter Stelle (also an der Stelle in der Vorlage, an der das Verfallsdatum erscheinen soll) einbringen. Gehen Sie dabei im Einzelnen wie folgt vor:

  1. Setzen Sie in der Dokumentvorlage die Einfügemarke an den Ort, an der das Verfallsdatum erscheinen soll.

  2. Ausgehend vom Word-Hauptmenü wählen Sie den Dialog Einfügen - Feld....

  3. Im Folgedialog markieren Sie in der linken Liste mit der Bezeichnung Kategorien: den Eintrag Dokumentautomation.

  4. In der rechten Liste mit der Bezeichnung Feldnamen: markieren Sie den Eintrag DokVariable falls Sie noch mit Word 97 arbeiten oder aber DocVariable ab Word2000.

  5. Als nächstes müssen Sie das Textfeld in diesem Dialog ergänzen. Setzen Sie also die Einfügemarke hinter den Feldnamen DokVariable (bzw. DocVariable), achten Sie darauf, dass ein Leerschlag vorhanden ist und tippen Sie "Verfallsdatum" und zwar inklusive Gänsefüsschen. Der ganze Ausdruck sieht nun also wie folgt aus: DokVariable "Verfallsdatum" Word 97 bzw. DocVariable "Verfallsdatum" ab Word2000.

  6. Schliessen Sie nun den Dialog mit Ok ab und sichern Sie Ihre Dokumentvorlage.

VBA-Code, welcher die Dokumentvariable mit dem Ergebnis versorgt, einbringen

Schliesslich folgt nun noch der VBA-Code, welcher die Dokumentvariable mit dem Ergebnis beliefert und auch dafür sorgt, dass die Feldfunktion aktualisiert wird.

Da das Starten des Makros automatisch erfolgen soll, verwenden wir die reservierten Namen AutoNew und AutoOpen. Damit ist sichergestellt, dass der Prozess automatisch anläuft, wenn ein neues Dokument auf Grundlage unserer Dokumentvorlage erstellt wird, oder wenn ein Dokument, welches auf dieser Vorlage basiert, geöffnet wird.

Die Funktion, um dem aktuellen Datum 14 Tage hinzuzuzählen, ist recht einfach und erscheint im folgenden Beispiel als Unterfunktion. Konkret gehen Sie bitte wie folgt vor:

  1. Öffnen Sie Ihre Dokumentvariable explizit über den Dialog Datei - Öffnen....

  2. Wechseln Sie mit der Tastenkombination [Alt+F11] in die VBA-Umgebung.

  3. Heil im VBA-Editor angekommen, wählen Sie ausgehend vom Hauptmenü Einfügen - Modul.

  4. Kopieren Sie den folgenden Code in das leere Codefenster.

  5. Sie können den Datumsschalter (im Beispiel d. MMMM yyyy) Ihren Bedürfnissen anpassen.

  6. Speichern und schliessen Sie die Vorlage.

  7. Erstellen Sie ein neues Dokument auf Basis dieser Dokumentvorlage.


Sub AutoNew()
  DokVariableVersorgen
End Sub

Sub AutoOpen()
  DokVariableVersorgen
End Sub

Private Sub DokVariableVersorgen()
  ActiveDocument.Variables("Verfallsdatum").Value = Vordatieren(14)
  ActiveDocument.Fields.Update
End Sub

Private Function Vordatieren(d As Integer) As String
  Dim t As Date
  t = Date + d
  Vordatieren = Format(t, "d. MMMM yyyy")
End Function

(Diese Routine, welche in eine Dokumentvorlage kopiert werden sollte, läuft beim Erstellen eines neuen Dokumentes oder beim Öffnen des Dokumentes automatisch ab. Dabei wird eine Dokumentvariable mit dem Namen "Verfallsdatum" versorgt, wobei die Routine das Datum "heute in 14 Tagen" berechnet.)


Anmerkung zum Code

Sollte sich der Platzhalter (also die Feldfunktion DocVariable) nicht im Hauptteil des Dokumentes befinden, sondern in der Kopf-/Fusszeile oder in einem Textfeld, dann muss das Makro erweitert werden. In diesem Fall kommt folgender Code zum Zuge:


Sub AutoNew()
  DokVariableVersorgen
End Sub

Sub AutoOpen()
  DokVariableVersorgen
End Sub

Private Sub DokVariableVersorgen()
  ActiveDocument.Variables("Verfallsdatum").Value = Vordatieren(14)
  Dim oStory As Range, oField As Field
  For Each oStory In ActiveDocument.StoryRanges
    For Each oField In oStory.Fields
      If oField.Type = wdFieldDocVariable Then oField.Update
    Next
    While Not (oStory.NextStoryRange Is Nothing)
      Set oStory = oStory.NextStoryRange
      For Each oField In oStory.Fields
        If oField.Type = wdFieldDocVariable Then oField.Update
      Next
    Wend
  Next
End Sub

Private Function Vordatieren(d As Integer) As String
  Dim t As Date
  t = Date + d
  Vordatieren = Format(t, "d. MMMM yyyy")
End Function

(Sollte sich die Dokumentvariable ausserhalb des Hauptteils des Dokumentes befinden, dann muss der eben besprochene Code wie oben abgebildet, ausgebaut werden, da die Codezeile "ActiveDocument.Fields.Update" ausschliesslich auf den Hauptteil des Dokumentes einwirkt.)


Anmerkung zum Code
Anmerkung zum Verfahren

Szenario II - Benutzerdefinierte Feldfunktion: Kalenderwoche

Im zweiten Szenario geht es um folgendes: Es soll eine benutzerdefinierte Feldfunktion implementiert werden, welche die aktuelle Kalenderwoche berechnet und das Resultat dynamisch im Dokument anzeigen kann.

Implementierung

Grundsätzlich müssen dieselben Schritte wie bereits im ersten Szenario durchgespielt werden; nur der Teil im Makro, welcher die Berechnung durchführt, unterscheidet sich selbstverständlich vom ersten Beispiel. Hier nochmals in Kürze, was von Ihnen erwartet wird:

  1. Öffnen Sie Ihre Dokumentvorlage explizit über den Dialog Datei - Öffnen....

  2. Wechseln Sie mit der Tastenkombination [Alt+F11] in die VBA-Umgebung.

  3. Heil im VBA-Editor angekommen, wählen Sie ausgehend vom Hauptmenü Einfügen - Modul.

  4. Kopieren Sie den nachfolgenden VBA-Code in das leere Codefenster.

  5. Führen Sie das Makro mit dem Namen DokVariableKalenderwochHinzfuegen einmal aus.

  6. Setzen Sie in der Dokumentvorlage die Einfügemarke an den Ort, an der die Kalenderwoche erscheinen soll.

  7. Ausgehend vom Word-Hauptmenü wählen Sie den Dialog Einfügen - Feld....

  8. Im Folgedialog markieren Sie in der linken Liste mit der Bezeichnung Kategorien: den Eintrag Dokumentautomation.

  9. In der rechten Liste mit der Bezeichnung Feldnamen: markieren Sie den Eintrag DokVariable falls Sie noch mit Word 97 arbeiten oder aber DocVariable ab Word2000.

  10. Als nächstes müssen Sie das Textfeld in diesem Dialog ergänzen. Setzen Sie also die Einfügemarke hinter den Feldnamen DokVariable (bzw. DocVariable), achten Sie darauf, dass ein Leerschlag vorhanden ist und tippen Sie "Kalenderwoche" und zwar inklusive Gänsefüsschen. Der ganze Ausdruck sieht nun also wie folgt aus: DokVariable "Kalenderwoche" Word 97 bzw. DocVariable "Kalenderwoche" ab Word2000.

  11. Schliessen Sie nun den Dialog mit Ok ab.

  12. Speichern und schliessen Sie die Vorlage.

  13. Erstellen Sie ein neues Dokument auf Basis dieser Dokumentvorlage.

VBA-Code

Sub DokVariableKalenderwochHinzfuegen()
  ActiveDocument.Variables.Add Name:="Kalenderwoche"
End Sub

Sub AutoNew()
  DokVariableVersorgen
End Sub

Sub AutoOpen()
  DokVariableVersorgen
End Sub

Private Sub DokVariableVersorgen()
  ActiveDocument.Variables("Kalenderwoche").Value = KWoche(Date)
  Dim oStory As Range, oField As Field
  For Each oStory In ActiveDocument.StoryRanges
    For Each oField In oStory.Fields
      If oField.Type = wdFieldDocVariable Then oField.Update
    Next
    While Not (oStory.NextStoryRange Is Nothing)
      Set oStory = oStory.NextStoryRange
      For Each oField In oStory.Fields
        If oField.Type = wdFieldDocVariable Then oField.Update
      Next
    Wend
  Next
End Sub

Private Function KWoche(d As Date) As Integer
'Kalenderwochen nach DIN 1355
'Algorithmus von Christoph Kremer, Aachen
  Dim t&
  t = DateSerial(Year(d + (8 - Weekday(d)) Mod 7 - 3), 1, 1)
  KWoche = (d - t - 3 + (Weekday(t) + 1) Mod 7) \ 7 + 1
End Function

(Der gesamte Code für die benutzerdefinierte Feldfunktion "Kalenderwoche". Diese Routine, welche für eine Dokumentvorlage gedacht ist, wird jedes Mal automatisch aufgerufen, wenn ein neues Dokument auf Grundlage der Vorlage erstellt wird oder wenn ein Dokument, welches auf der Vorlage basiert, geöffnet wird.)


Anmerkung zum Verfahren

Ausweichlösung mit einer benutzerdefinierten Dokumenteigenschaft

Funktion "Verfallsdatum" beim Einsatz einer benutzerdefinierten Dokumenteigenschaft

Satt Dokumentvariable können auch benutzerdefinierte Dokumenteigenschaften, welche ein sehr ähnliches Ziel verfolgen, zum Einsatz kommen. Die Mimik unterscheidet sich dabei kaum von der Technik, welche auf Dokumentvariable setzt.

Einsatzgebiet

Arbeiten Sie mit benutzerdefinierten Dokumenteigenschaften, falls folgende beiden Bedingungen zutreffen:

Der Grund, wieso Sie in diesem Fall auf Dokumenteigenschaften ausweichen müssen ist der, dass MS Word 97 reproduzierbar abschmiert, falls Sie ein DokVariable-Feld in einen der genannten Dokumentteile einbringen wollen.

Kurzbeschreibung
Implementierung

Wenn Sie ausgehend vom Word-Hauptmenü den Dialog Einfügen - Feld... aufrufen, dann verwenden Sie an der Stelle einer Dokumentvariablen die Feldfunktion Dokumentinformation - DokEigenschaft Word 97 bzw. DocProperty ab Word2000. Das übrige Vorgehen bleibt genau so, wie dies im Kapitel Szenario I - Benutzerdefinierte Feldfunktion: Heute in 14 Tage bereits beschrieben wurde.

VBA-Code

Nachfolgend finden Sie den gesamten Code für die Funktion heute in 14 Tagen, falls Sie statt einer Dokumentvariablen eine benutzerdefinierte Dokumenteigenschaft einsetzen wollen oder müssen:


Sub AutoNew()
  DokEigenschaftVersorgen
End Sub

Sub AutoOpen()
  DokEigenschaftVersorgen
End Sub

Private Sub DokEigenschaftVersorgen()
  CheckExistance "Verfallsdatum"
  ActiveDocument.CustomDocumentProperties("Verfallsdatum").Value = Vordatieren(14)
  Dim oStory As Range, oField As Field
  For Each oStory In ActiveDocument.StoryRanges
    For Each oField In oStory.Fields
      If oField.Type = wdFieldDocProperty Then oField.Update
    Next
    While Not (oStory.NextStoryRange Is Nothing)
      Set oStory = oStory.NextStoryRange
      For Each oField In oStory.Fields
        If oField.Type = wdFieldDocProperty Then oField.Update
      Next
    Wend
  Next
End Sub

Private Function CheckExistance(vName As String)
  Dim v As DocumentProperty
  Flag = False
  For Each v In ActiveDocument.CustomDocumentProperties
    If v.Name = vName Then
      Flag = True
      Exit For
    End If
  Next
  If Flag = False Then
    ActiveDocument.CustomDocumentProperties.Add Name:=vName, _
       LinkToContent:=False, Type:=msoPropertyTypeString, Value:=0
  End If
End Function

Private Function Vordatieren(d As Integer) As String
  Dim t As Date
  t = Date + d
  Vordatieren = Format(t, "d. MMMM yyyy")
End Function

(Statt einer Dokumentvariablen, kann auch eine benutzerdefinierte Dokumenteigenschaft eingesetzt werden. Hier der entsprechende Code für die Funktion "heute in 14 Tagen".)


Funktion "Kalenderwoche" beim Einsatz einer benutzerdefinierten Dokumenteigenschaft

Satt Dokumentvariable können auch benutzerdefinierte Dokumenteigenschaften, welche ein sehr ähnliches Ziel verfolgen, zum Einsatz kommen. Die Mimik unterscheidet sich dabei kaum von der Technik, welche auf Dokumentvariable setzt.

Einsatzgebiet

Arbeiten Sie mit benutzerdefinierten Dokumenteigenschaften, falls folgende beiden Bedingungen zutreffen:

Der Grund, wieso Sie in diesem Fall auf Dokumenteigenschaften ausweichen müssen ist der, dass MS Word 97 reproduzierbar abschmiert, falls Sie ein DokVariable-Feld in einen der genannten Dokumentteile einbringen wollen.

Kurzbeschreibung
Implementierung

Wenn Sie ausgehend vom Word-Hauptmenü den Dialog Einfügen - Feld... aufrufen, dann verwenden Sie an der Stelle einer Dokumentvariablen die Feldfunktion Dokumentinformation - DokEigenschaft Word 97 bzw. DocProperty ab Word2000. Das übrige Vorgehen bleibt genau so, wie dies im Kapitel Szenario II - Benutzerdefinierte Feldfunktion: Kalenderwoche bereits beschrieben wurde.

VBA-Code

Nachfolgend finden Sie den gesamten Code für die Funktion Kalenderwoche, falls Sie statt einer Dokumentvariablen eine benutzerdefinierte Dokumenteigenschaft einsetzen wollen oder müssen:


Sub AutoNew()
  DokEigenschaftVersorgen
End Sub

Sub AutoOpen()
  DokEigenschaftVersorgen
End Sub

Private Sub DokEigenschaftVersorgen()
  CheckExistance "Kalenderwoche"
  ActiveDocument.CustomDocumentProperties("Kalenderwoche").Value = KWoche(Date)
  Dim oStory As Range, oField As Field
  For Each oStory In ActiveDocument.StoryRanges
    For Each oField In oStory.Fields
      If oField.Type = wdFieldDocProperty Then oField.Update
    Next
    While Not (oStory.NextStoryRange Is Nothing)
      Set oStory = oStory.NextStoryRange
      For Each oField In oStory.Fields
        If oField.Type = wdFieldDocProperty Then oField.Update
      Next
    Wend
  Next
End Sub

Private Function CheckExistance(vName As String)
  Dim v As DocumentProperty
  Flag = False
  For Each v In ActiveDocument.CustomDocumentProperties
    If v.Name = vName Then
      Flag = True
      Exit For
    End If
  Next
  If Flag = False Then
    ActiveDocument.CustomDocumentProperties.Add Name:=vName, _
       LinkToContent:=False, Type:=msoPropertyTypeNumber, Value:=0
  End If
End Function

Private Function KWoche(d As Date) As Integer
  'Kalenderwochen nach DIN 1355
  'Algorithmus von Christoph Kremer, Aachen
  Dim t&
  t = DateSerial(Year(d + (8 - Weekday(d)) Mod 7 - 3), 1, 1)
  KWoche = (d - t - 3 + (Weekday(t) + 1) Mod 7) \ 7 + 1
End Function

(Statt einer Dokumentvariablen, kann auch eine benutzerdefinierte Dokumenteigenschaft eingesetzt werden. Hier der entsprechende Code für die Funktion "Kalenderwoche".)


Datumsberechnungen mit Textmarkenfeldern

Voraussetzungen

Um das folgende Makro zu testen, sollten Sie die folgenden Voraussetzungen schaffen:

  1. Erstellen Sie ein neues Dokument.

  2. Schreiben Sie ein gültiges Datum z.B. 11.03.2001 an eine beliebige Stelle im Dokument.

  3. Markieren Sie dieses Datum.

  4. Vom Word-Hauptmenü wählen Sie den Befehl Einfügen - Textmarke....

  5. Vergeben Sie der Textmarke den Namen Eingegangen und klicken Sie Hinzufügen.

  6. Schreiben Sie an einer anderen Stelle ein weiteres gültiges Datum z.B. 01.11.2002.

  7. Markieren Sie auch dieses Datum.

  8. Wählen Sie wiederum den Befehl Einfügen - Textmarke....

  9. Vergeben Sie der Textmarke den Namen Erledigt und klicken Sie Hinzufügen.

  10. Schreiben Sie an einer anderen Stelle im Dokument z.B. das Wort Ergebnis. Was Sie hier schreiben ist eigentlich unwichtig. Diese Textstelle wird vom Code nämlich eh überschrieben.

  11. Markieren Sie dieses Wort.

  12. Wählen Sie wiederum den Befehl Einfügen - Textmarke....

  13. Vergeben Sie der Textmarke den Namen Dauer und klicken Sie Hinzufügen.

VBA-Code (statische Variante)

Kopieren Sie den folgenden Code in der VBA-Umgebung in ein neues Modul und lassen Sie ihn ausführen.


Sub TMTageBerechnenStatisch()
  Dim oDoc As Document
  Set oDoc = ActiveDocument
  Ein = oDoc.Bookmarks("Eingegangen").Range.Text
  Aus = oDoc.Bookmarks("Erledigt").Range.Text
  If IsDate(Ein) And IsDate(Aus) Then
    oDoc.Bookmarks("Dauer").Range.Font.Color = wdColorBlack
    oDoc.Bookmarks("Dauer").Range.Text = _ 
       DateDiff("d", DateValue(Ein), DateValue(Aus))
  Else
    oDoc.Bookmarks("Dauer").Range.Font.Color = wdColorRed
    oDoc.Bookmarks("Dauer").Range.Text = "Fehler!"
  End If
End Sub

(Aus zwei Datumsangaben in Textmarkenfeldern wird die Differenz errechnet. Das Resultat wird einem dritten Textmarkenfeld zugewiesen. Dieser Prozess kann in dieser Form nur einmal ablaufen, da die Zielmarke überschrieben wird.)


Anmerkungen


Sub TMTageBerechnenStatisch2()
  If Not ActiveDocument.Bookmarks.Exists("Dauer") Then
    MsgBox "Die Textmarke mit dem Namen 'Dauer' existiert nicht."
    Exit Sub
  End If
  'Weiterer Code
End Sub

(Dieser Code zeigt auf, wie das Vorhandensein einer bestimmtem Textmarke überprüft werden kann.)


VBA-Code (Dynamische Variante)

Das folgende Beispiel geht von den genau gleichen Voraussetzungen aus und verfolgt auch fast dasselbe Ziel, wie der eben diskutierte Code. Diesmal wird aber sichergestellt, dass die Zielmarke erhalten bleibt. Das Makro kann somit beliebig oft eingesetzt werden, falls entweder das Eingangs- oder das Erledigtdatum verändert wird.


Sub TMTageBerechnenDynamisch()
  Dim oDoc As Document, oRange As Range
  Set oDoc = ActiveDocument
  Ein = oDoc.Bookmarks("Eingegangen").Range.Text
  Aus = oDoc.Bookmarks("Erledigt").Range.Text
  Set oRange = oDoc.Bookmarks("Dauer").Range
  If IsDate(Ein) And IsDate(Aus) Then
    oRange.Text = DateDiff("d", DateValue(Ein), DateValue(Aus))
  Else
    oRange.Text = "Fehler!"
  End If
  oDoc.Bookmarks.Add Name:="Dauer", Range:=oRange
End Sub

(Nach der statischen nun die dynamische Lösung. Die Zielmarke bleibt bei dieser Mimik faktisch erhalten.)


Datumsberechnungen mit Textformularfeldern

Noch besser, als mit Textmarkenfelder, lässt es sich mit Textformularfelder rechnen, welche Sie über die Symbolleiste mit der Bezeichnung Formular in ein Dokument oder in eine Dokumentvorlage einbringen können.

Voraussetzungen und Ausführung

Um das folgende Makro zu testen, sollten Sie die folgenden Voraussetzungen schaffen:

  1. Erstellen Sie eine neue Dokumentenvorlage.

  2. Kopieren Sie das unten abgebildete Makro mit dem Namen FFTageBerechnen im VBA-Editor in ein neues Modul.

  3. Lassen Sie sich die Symbolleiste Formular anzeigen.

  4. Fügen Sie an beliebiger Stelle ein Textformularfeld in das Dokument ein.

  5. Führen Sie einen Doppelklick auf dieses Formularfeld aus, womit die Optionen des Formularfelds angezeigt werden.

  6. Stellen Sie im Kombinationsfeld mit der Bezeichnung Typ den Wert Datum ein und passen Sie auf Wunsch auch das Datumsformat an.

  7. Im selben Dialog unter Textmarke: sollten Sie dem Feld einen aussagekräftigen Namen vergeben. Nennen Sie das Feld CheckIn.

  8. Im Kombinationsfeld mit der Bezeichnung Makro beim Verlassen Word 97 bzw. Makro ausführen bei Beenden ab Word2000 suchen Sie nun in der Liste nach dem Makro mit dem Namen FFTageBerechnen und stellen diesen Wert ein.

  9. Aktivieren Sie schliesslich noch die Option Beim Verlassen berechnen.

  10. Schliessen Sie den Dialog mit Ok ab.

  11. Setzen Sie nun die Einfügemarke an eine andere Stelle im Dokument und fügen Sie dort ein weiteres Textformularfeld ein.

  12. Führen Sie einen Doppelklick auf dieses Feld aus und profilieren Sie dieses Textformularfeld wie das erste, mit dem wichtigen Unterschied, dass dieses Feld CheckOut heissen soll. Auch dieses Feld soll die Exit-Prozedur mit dem Namen FFTageBerechnen aufrufen und vergessen Sie auch nicht die Option Beim Verlassen berechnen zu aktivieren.

  13. Setzen Sie nun die Einfügemarke an eine andere Stelle im Dokument und fügen Sie dort ein weiteres Textformularfeld ein.

  14. Führen Sie einen Doppelklick auf dieses Formularfeld aus und entfernen Sie im Eigenschaftsdialog (Optionen) das Häkchen bei Eingabe zulassen.

  15. Nennen Sie dieses Feld Nächte und schliessen Sie den Dialog mit Ok ab.

  16. Klicken Sie nun auf der kontextbezogenen Symbolleiste auf die Schaltfläche Formular schützen.

  17. Speichern und Schliessen Sie die Vorlage.

  18. Erstellen Sie ein neues Dokument auf der Basis dieser Vorlage und testen Sie Ihr Werk, in dem Sie in die ersten zwei Textformularfelder ein gültiges Datum eintragen.

VBA-Code (Formularfeld-Exit Prozeduren)

Kopieren Sie die folgenden Makros in der VBA-Umgebung in ein neues Modul und zwar in die Dokumentvorlage, welche die Formularfelder enthält.


Sub FFTageBerechnen()
  Dim oDoc As Document
  Set oDoc = ActiveDocument
  CIn = oDoc.FormFields("CheckIn").Result
  COut = oDoc.FormFields("CheckOut").Result
  If IsDate(CIn) And IsDate(COut) Then _
  oDoc.FormFields("Nächte").Result = DateDiff("d", DateValue(CIn), DateValue(COut))
End Sub

(Aus zwei Textformularfeldern wird die Datumsdifferenz in Tagen berechnet und in ein drittes Formularfeld geschrieben. Diese Prozedur ist als Feld-Exit gedacht.)


Bei den folgenden Beispielen wird aus zwei Zeitangaben die Differenz, ausgedrückt in Stunden und Minuten (erstes Beispiel) bzw. ausgedrückt in Stunden, Minuten und Sekunden (zweites Beispiel), berechnet. Diese Beispiele lehnen sich an das vorhergehende Exempel an. Diesmal lauten die Namen der Formularfelder allerdings anders, nämlich: Anfang, Ende und Arbeitszeit. Auch sollten Sie das Datumsformat der Formularfelder Anfang und Ende im Eigenschaftsdialog nach HH:mm bzw. HH:mm:ss anpassen. Zudem heissen die Feld-Exit-Prozeduren nun anders.


Sub FFSundenMinutenBerechnen()
  Dim oDoc As Document
  Set oDoc = ActiveDocument
  Ein = oDoc.FormFields("Anfang").Result
  Aus = oDoc.FormFields("Ende").Result
  If IsDate(Ein) And IsDate(Aus) Then
    mm = DateDiff("n", TimeValue(Ein), TimeValue(Aus))
    hmm = Format(TimeSerial(0, mm, 0), "h:mm")
    oDoc.FormFields("Arbeitszeit").Result = hmm
  End If
End Sub

(Statt der Datumsdifferenz, ausgedrückt in Tagen, wird die Stundendifferenz, ausgedrückt in Stunden und Minuten errechnet.)



Sub FFSundenMinutenSekundenBerechnen()
  Dim oDoc As Document
  Set oDoc = ActiveDocument
  Ein = oDoc.FormFields("Anfang").Result
  Aus = oDoc.FormFields("Ende").Result
  If IsDate(Ein) And IsDate(Aus) Then
    ss = DateDiff("s", TimeValue(Ein), TimeValue(Aus))
    hmmss = Format(TimeSerial(0, 0, ss), "h:mm:ss")
    oDoc.FormFields("Arbeitszeit").Result = hmmss
  End If
End Sub

(Das Selbe nochmals. Diesmal wird die Differenz allerdings in Stunden, Minuten und Sekunden ausgedrückt.)


Datumsberechnungen in einer Word-Tabelle

Dank VBA lässt sich auch in Word-Tabellen ganz ordentlich rechnen, wenn vielleicht auch nicht ganz so Excellent. Das nachfolgende Beispiel sieht folgendes Szenario vor:

Szenario

  1. Erstellen Sie ein neues Word-Dokument.

  2. Fügen Sie eine dreispaltige Tabelle in das Dokument ein.

  3. Füllen Sie diese Tabelle etwa so aus, wie unten angezeigt.


Eingang           Erledigt          Dauer
11.11.97            24.12.99
14. Dezember 1999   23.01.00
02.05.26            02.05.27
11.11.11            12.11.11
Dauer Total

Fügen Sie nun den folgenden Code in der VBA-Umgebung in ein neues Modul und lassen Sie das Makro mit dem Namen TabelleTageBerechnen ablaufen. Dabei muss sich die Einfügemarke in der fraglichen Tabelle befinden. Testen Sie den Code zusätzlich, in dem Sie allenfalls die Werte in der Tabelle verändern oder auch zusätzliche, analoge Zeilen hinzufügen.

Die Teilergebnisse (Dauer) und das Grand Total (Dauer Total) wird jeweils in der dritten Spalte eingefügt. Das Ergebnis präsentiert sich somit etwa wie folgt:


Eingang           Erledigt           Dauer
11.11.97            24.12.99               773
14. Dezember 1999   23.01.00                40
02.05.93            02.05.94               365
11.11.2002          12.11.2002               1
Dauer Total                               1179

Anmerkungen

Sub TabelleTageBerechnen()
  If Selection.Tables.Count = 0 Then
    MsgBox "Keine Tabelle in Sicht."
    End
  End If
  Dim oTable As Table
  Set oTable = Selection.Tables(1)
  If oTable.Columns.Count < 3 Or oTable.Rows.Count < 3 Then
    MsgBox "Die Tabelle sollte mindestens 3 Zeilen und 3 Spalten aufweisen."
    End
  End If
  s = 3
  i = 0
  For Each Zeile In oTable.Rows
    i = i + 1
    If i > 1 And i < oTable.Rows.Count Then
      Ein = Left(Zeile.Cells(1), Len(Zeile.Cells(1)) - 2)
      Aus = Left(Zeile.Cells(2), Len(Zeile.Cells(2)) - 2)
      If Len(Ein) + Len(Aus) > 0 Then
        If IsDate(Ein) And IsDate(Aus) Then
          Zeile.Cells(s) = DateDiff("d", DateValue(Ein), DateValue(Aus))
          Zeile.Cells(s).Range.Font.Color = wdColorBlack
        Else
          Zeile.Cells(s) = "Fehler!"
          Zeile.Cells(s).Range.Font.Color = wdColorRed
        End If
      Else
        Zeile.Cells(s) = ""
        Zeile.Cells(s).Range.Font.Color = wdColorBlack
      End If
    End If
  Next
  TotalTage = 0
  i = 0
  For Each Zelle In oTable.Columns(s).Cells
    i = i + 1
    If i > 1 And i < oTable.Rows.Count Then
      Wert = Left(Zelle, Len(Zelle) - 2)
      If IsNumeric(Wert) Then
        TotalTage = TotalTage + Wert
      End If
    End If
  Next
  oTable.Rows(oTable.Rows.Count).Cells(s).Range.Text = TotalTage
  oTable.Rows(oTable.Rows.Count).Cells(s).Range.Font.Bold = True
End Sub

(Datumsberechnung für eine Word-Tabelle.)


Berechnung der Werktage mittels UserForm

Einleitung

Als Werktage gelten in der heutigen Zeit Tage, welche nicht auf einen Samstag oder Sonntag fallen und auch keine Feiertage darstellen.

Wogegen Samstage und Sonntag problemlos programmtechnisch eruiert werden können, ist dies bei Feiertagen nicht möglich. Zwar gibt es die berühmte Osterformel von C. F. Gauß, welche sich auch in VBA umsetzen liesse. Leider können damit aber nur jene beweglichen (und meist kirchlichen) Festtage berechnet werden, welche sich nach der Folge der Mondphasen richten. Andere, vor allem lokale Ruhetage, erfasst die Osterformel nicht und in einer weitgehend globalisierten Welt, muss eigentlich jeder Feiertag als lokal eingestuft werden.

Konkret bedeutet dies, dass Sie eine Liste unterhalten müssen, die alle Ruhetage aufzählt, welche nicht auf einen Samstag oder Sonntag fallen, damit im Programm ein Abgleich stattfinden kann.

MS Word ist bekanntlich keine Datenbankanwendung und die Möglichkeiten, intern eine Liste zu unterhalten sind damit eher begrenzt. Trotzdem lassen sich Orte finden, wo diese Liste abgelegt werden kann. Da sich meines Erachtens keiner dieser Orte als klar überlegen aufdrängt, finden Sie im Folgenden zwei Lösungsansätze, wobei, abhängig vom Ansatz, auf jeweils eine der folgenden Einrichtung bzw. Komponenten abstellt wird:

Liste der Feiertage in einer AutoText-Kategorie unterhalten

Um das folgende Beispiel zu testen, sind folgende Vorarbeiten angesagt:

Implementierung
  1. Erstellen Sie eine neue Dokumentvorlage.

  2. Erstellen Sie über Format - Formatvorlage... - Neu... eine neue Formatvorlage vom Typus Absatz mit dem Namen Feiertage.

  3. Stellen Sie sicher, dass im Textteil der Dokumentvorlage die Formatvorlage, welche Sie eben erstellt haben, aktiv ist.

  4. Tippen Sie z.B. 25/12/2005 in den Textteil der Dokumentvorlage ein und markieren Sie dieses Datum.

  5. Ausgehend vom Word-Hauptmenü wählen Sie nun den Dialog Einfügen - AutoText - Neu....

  6. Ändern Sie den vorgeschlagenen Namen des AutoText-Eintrages von 25/12/2005 nach Weihnachtstag2005 und klicken Sie Ok.

  7. Wiederholen Sie die Schritte 4 - 6 für die Feiertage für die nächsten ca. 15 Monate in analoger Art und Weise. Festtage, welche eh auf einen Samstag oder Sonntag fallen, können Sie sich dabei schenken.

  8. Wechseln Sie mit der Tastenkombination [Alt + F11] in den VBA-Editor.

  9. Achten Sie darauf, dass im Projektexplorer Ihre ebenerstellte Dokumentvorlage markiert ist.

  10. Wählen Sie nun ausgehend vom Hauptmenü Einfügen - UserForm.

  11. Über die Werkzeugsammlung platzieren Sie folgende Komponenten auf dieser UserForm:

  12. Führen Sie nun einen Rechtsklick auf eine freie Stelle der UserForm aus und wählen Sie im Kontextmenü Code anzeigen.

  13. Löschen Sie die beiden Zeilen, welche Ihnen MS Word automatisch generiert hat und kopieren Sie den unten abgebildeten VBA-Code in das nun leere Codefenster der UserForm.

  14. Im Kopf des Codes können Sie auf Wunsch einige Konstanten an Ihre speziellen Bedürfnisse anpassen. Diese Konstanten haben die folgende Bedeutung:

  15. Sie sind nun bereit, die UserForm aufzurufen und die Routine mit verschiedenen Datumswerten zu testen. Geben Sie dazu Datumsangaben z.B. im Format 22/05/2005 in die Textfelder ein.

VBA-Code

'**** Start anpassbare Werte *****
Private Const Titel = "Werktage berechnen"

Private Const FVName = "Feiertage"   'Name der Formatvorlage, unter welcher
                                     'die AutoText-Einträge aufgenommen wurden

Private Const KeinListenfeld = False 'True, wenn *kein* Listenfeld mit dem Namen
                                     '"lbFVName" zur Anzeige der Feiertage
                                     'auf der UserForm vorhanden ist

Private Const MaxSpanne = 366        'Spanne (in Tagen),
                                     'für welche Feiertage definiert wurden
'**** Ende Anpassbare Werte *****

Private FTListeN() As String
Private FTListe() As String
Private KeineFeiertage As Boolean

Private Sub UserForm_Activate()
  Caption = Titel
  txtWerktage.Locked = True
  txtWerktage.TabStop = False
  txtWerktage.TextAlign = fmTextAlignRight
  FeiertageLaden
  If KeinListenfeld = False And KeineFeiertage = False Then ListenfeldLaden
End Sub

Private Sub txtEintritt_Exit(ByVal Cancel As MSForms.ReturnBoolean)
  If Not IsDate(txtEintritt.Text) Then
    Beep
    MsgBox "Das ist kein gültiges Datum."
    Cancel = True
    txtEintritt.SelStart = 0
    txtEintritt.SelLength = txtEintritt.TextLength
  Else
    WerktageBerechnen
  End If
End Sub

Private Sub txtAustritt_Exit(ByVal Cancel As MSForms.ReturnBoolean)
  If Not IsDate(txtAustritt.Text) Then
    Beep
    MsgBox "Das ist kein gültiges Datum."
    Cancel = True
    txtAustritt.SelStart = 0
    txtAustritt.SelLength = txtAustritt.TextLength
  Else
    WerktageBerechnen
  End If
End Sub

Private Sub WerktageBerechnen()
  Dim x As Date, y As Date, z As Integer, wt As Integer
  Anfang = txtEintritt.Text
  Ende = txtAustritt.Text
  If Not IsDate(Anfang) Or Not IsDate(Ende) Then Exit Sub
  x = Anfang
  y = Ende
  z = 1 'zählt die Tage für die max. Spanne
  While x <= y And z <= MaxSpanne
    flag = False
    If Weekday(x, vbMonday) > 5 Then flag = True 'wenn Samstag oder Sonntag
    If flag = False And KeineFeiertage = False Then 'wenn kein Sa. oder So.
      For i = 0 To UBound(FTListe) 'Abgleich gegen jedes Datum in der Liste
        If x = CDate(FTListe(i, 1)) Then
          flag = True 'Yep, das ist ein Feiertag
          Exit For 'Weitere Einträge sind jetzt irrelevant
        End If
      Next i
    End If
    If flag = False Then wt = wt + 1 'Weder Samstag, Sonntag noch Feiertag
    x = x + 1 'Nächster Tag
    z = z + 1
  Wend
  If z >= MaxSpanne Then
    txtWerktage.Text = 0
    MsgBox "Maximale Spanne von " & MaxSpanne & " Tagen überschritten!", _
       vbExclamation, Titel
  Else
    txtWerktage.Text = wt
  End If
End Sub

Private Sub FeiertageLaden()
  Dim oAT As AutoTextEntry, tmp() As String
  i = -1
  For Each oAT In ActiveDocument.AttachedTemplate.AutoTextEntries
    If oAT.StyleName = FVName Then 'Wenn die fragliche AutoText-Kategorie
      chk = oAT.Value
      If Right(chk, 1) = Chr(13) Then chk = Left(chk, Len(chk) - 1) 'strip ZS
      If Not IsDate(chk) Then
        MsgBox "Ungültiges Datum (" & chk & ") im AutoText-Eintrag " & oAT.Name, _
           vbExclamation, Titel
      Else
        i = i + 1
        ReDim Preserve tmp(i)
        tmp(i) = oAT.Name & Chr(1) & chk 'Name und Wert des AutoText-Eintrages
      End If
    End If
  Next
  If i = -1 Then
    KeineFeiertage = True 'Keine Feiertage definiert
    Exit Sub
  End If
  'Jetzt, wo die Anzahl der Feiertage in der Liste bekannt ist...
  ReDim FTListe(i, 1) '...kann die Sache sauber strukturiert werden
  For i = 0 To UBound(FTListe) '1-dimensionaler Array nach 2-dimensionalen Array
    FTListe(i, 0) = Left(tmp(i), InStr(tmp(i), Chr(1)) - 1) 'Name des AT-Eintrages
    FTListe(i, 1) = Mid(tmp(i), InStr(tmp(i), Chr(1)) + 1) 'Wert des AT-Eintrages
  Next i
End Sub

Private Sub ListenfeldLaden()
  'Diese Unterroutine ist nur dann nötig, wenn die Feiertage in einem Listenfeld
  'dargestellt werden sollen
  lbFeiertage.TabStop = False: lbFeiertage.ColumnCount = 2
  lbFeiertage.List = FTListe()
End Sub

(Diese Routine gehört in das Codefenster einer UserForm und berechnet die Werktage zwischen zwei vom Benutzer eingegebenen Datumsangaben. Dabei wird vorausgesetzt, dass die Feiertage als AutoText-Einträge unter der Formatvorlage mit der Bezeichnung "Feiertage" aufgenommen wurden.)


Liste der Feiertage in einer externen INI-Datei unterhalten

Um das folgende Beispiel zu testen, sind folgende Vorarbeiten angesagt:

Implementierung
  1. Erstellen Sie in Notepad oder in einem vergleichbaren Tool eine Datei, welche die Feiertage für die ca. nächsten 15 Monate wie im Kapitel Die INI-Datei aufgezeigt, auflistet. Festtage, welche eh auf einen Samstag oder Sonntag fallen, können Sie sich dabei schenken.

  2. Speichern Sie diese Datei im Programmverzeichnis von MS Office unter dem Namen Feiertage.ini ab. Beim angesprochenen Programmverzeichnis handelt es sich um jenen Ordner, in dem sich auch z.B. die winword.exe befindet.

  3. Erstellen Sie nun eine neue Dokumentvorlage.

  4. Wechseln Sie mit der Tastenkombination [Alt + F11] in den VBA-Editor.

  5. Achten Sie darauf, dass im Projektexplorer Ihre ebenerstellte Dokumentvorlage markiert ist.

  6. Wählen Sie nun ausgehend vom Hauptmenü Einfügen - UserForm.

  7. Über die Werkzeugsammlung platzieren Sie folgende Komponenten auf dieser UserForm:

  8. Führen Sie nun einen Rechtsklick auf eine freie Stelle der UserForm aus und wählen Sie im Kontextmenü Code anzeigen.

  9. Löschen Sie die beiden Zeilen, welche Ihnen MS Word automatisch generiert hat und kopieren Sie den unten abgebildeten VBA-Code in das nun leere Codefenster der UserForm.

  10. Im Kopf des Codes können Sie auf Wunsch einige Konstanten an Ihre speziellen Bedürfnisse anpassen. Diese Konstanten haben die folgende Bedeutung:

  11. Sie sind nun bereit, die UserForm aufzurufen und die Routine mit verschiedenen Datumswerten zu testen. Geben Sie dazu Datumsangaben z.B. im Format 22/05/2005 in die Textfelder ein.

Die INI-Datei

Im Folgenden sehen Sie ein Beispiel, wie der Inhalt der INI-Datei aussehen sollte. Bedenken Sie dabei, dass die Nummerierung der Feiertage aufsteigend und lückenlos (1=..., 2=..., 3=..., etc.) sein muss. Es kann auf Wunsch auch eine anderes gültiges Datumsformat zur Anwendung kommen.


[Datumsangaben]
1=01/01/2004
2=02/01/2004
3=06/01/2004
4=01/03/2004
5=01/05/2004
6=01/08/2004
7=25/01/2004
8=26/01/2004
9=01/01/2005
10=02/01/2005

(Diese Daten speichern Sie unter dem Namen "Feiertage.ini" im Programmverzeichnis von MS Office ab.)


VBA-Code

Finden Sie anschliessen den VBA-Code, welcher Sie in das Codefenster einer UserForm kopieren müssen. Welche Komponenten auf dieser UserForm enthalten sein müssen, wurde bereits erwähnt.


'**** Start anpassbare Werte *****
Private Const Titel = "Werktage berechnen"

Private Const INIDatei = "Feiertage.ini"  'Name der INI-Datei, in welcher
                                          'die Feiertage aufgeführt sind

Private Const Kategorie = "Datumsangaben" 'Abschnitt der INI-Datei, in welcher
                                          'die Feiertage aufgeführt sind

Private Const KeinListenfeld = False      'True, wenn *kein* Listenfeld mit dem
                                          'Namen "lbFeiertage" zur Anzeige der
                                          'Feiertage auf der UserForm vorhanden ist

Private Const MaxSpanne = 366             'Spanne (in Tagen),
                                          'für welche Feiertage definiert wurden
'**** Ende anpassbare Werte *****

Private FTListe() As String
Private KeineFeiertage As Boolean

Private Sub UserForm_Activate()
  Caption = Titel
  txtWerktage.Locked = True
  txtWerktage.TabStop = False
  txtWerktage.TextAlign = fmTextAlignRight
  FeiertageLaden
  If KeinListenfeld = False And KeineFeiertage = False Then ListenfeldLaden
End Sub

Private Sub txtEintritt_Exit(ByVal Cancel As MSForms.ReturnBoolean)
  If Not IsDate(txtEintritt.Text) Then
    Beep
    MsgBox "Das ist kein gültiges Datum."
    Cancel = True
    txtEintritt.SelStart = 0
    txtEintritt.SelLength = txtEintritt.TextLength
  Else
    WerktageBerechnen
  End If
End Sub

Private Sub txtAustritt_Exit(ByVal Cancel As MSForms.ReturnBoolean)
  If Not IsDate(txtAustritt.Text) Then
    Beep
    MsgBox "Das ist kein gültiges Datum."
    Cancel = True
    txtAustritt.SelStart = 0
    txtAustritt.SelLength = txtAustritt.TextLength
  Else
    WerktageBerechnen
  End If
End Sub

Private Sub WerktageBerechnen()
  Dim x As Date, y As Date, z As Integer, wt As Integer
  Anfang = txtEintritt.Text
  Ende = txtAustritt.Text
  If Not IsDate(Anfang) Or Not IsDate(Ende) Then Exit Sub
  x = Anfang
  y = Ende
  z = 1 'zählt die Tage für die max. Spanne
  While x <= y And z <= MaxSpanne
    flag = False
    If Weekday(x, vbMonday) > 5 Then flag = True 'wenn Samstag oder Sonntag
    If flag = False And KeineFeiertage = False Then 'wenn kein Sa. oder So.
      For i = 0 To UBound(FTListe)
        If x = CDate(FTListe(i)) Then
          flag = True 'Yep, das ist ein Feiertag
          Exit For 'weitere Einträge sind jetzt irrelevant
        End If
      Next i
    End If
    If flag = False Then wt = wt + 1
    x = x + 1 'nächster Tag
    z = z + 1
  Wend
  If z > MaxSpanne Then
    txtWerktage.Text = 0
    MsgBox "Maximale Spanne von " & MaxSpanne & " Tagen überschritten", _
       vbExclamation, Titel
  Else
    txtWerktage.Text = wt
  End If
End Sub

Private Sub FeiertageLaden()
  i = 1
  j = -1
  INIFile = Options.DefaultFilePath(wdProgramPath) & "\" & INIDatei
  If Dir(INIFile) = "" Then
    MsgBox "Die INI-Datei mit dem Namen " & INIFile & " existiert nicht.", _
       vbExclamation, Titel
    KeineFeiertage = True
  End If
  tmp = System.PrivateProfileString(INIFile, Kategorie, CStr(i)) '1. Eintrag
  While Not tmp = "" 'solange bis Einträge vorhanden
    If Not IsDate(tmp) Then
      MsgBox "Ungültiges Datum (" & tmp & ") in INI-Datei " & INIFile, _
         vbExclamation, Titel
    Else
      j = j + 1
      ReDim Preserve FTListe(j)
      FTListe(j) = tmp 'Feiertag ins Datenfeld laden
    End If
    i = i + 1
    tmp = System.PrivateProfileString(INIFile, Kategorie, CStr(i)) 'nächster E.
  Wend
  If j = -1 Then KeineFeiertage = True 'Keine Einträge erkannt
End Sub

Private Sub ListenfeldLaden()
  'Diese Unterroutine ist nur dann nötig, wenn die Feiertage in einem Listenfeld
  'dargestellt werden sollen
  lbFeiertage.TabStop = False
  lbFeiertage.List = FTListe()
End Sub

(Diese Routine gehört in das Codefenster einer UserForm und berechnet die Werktage zwischen zwei vom Benutzer eingegebenen Datumsangaben. Dabei wird vorausgesetzt, dass die Feiertage in der INI-Datei mit dem Namen "Feiertage.ini" im Abschnitt mit der Bezeichnung "Datumsangaben" administriert werden.)


Links zu diesem Thema

Eigene Links

Microsoft

Chris Woodman

URL

http://mypage.bluewin.ch/reprobst/WordFAQ/DatBer.htm

René Probst, September 2004

Änderungen

    

    


Wasserbett