Mit Zeiten rechnen

Zeit- und Datumsangaben sind elementarer Bestandteil vieler Anwendungen. Da sollte man wissen, wie man mit diesen Daten rechnet. Dabei ist es einerlei, ob es um das Ermitteln einer Zeitspanne zwischen zwei Zeitangaben oder um das Summieren von Zeiten geht – Stolperfallen gibt es dabei eine ganze Reihe. Dieser Beitrag zeigt, wie Sie die Klippen beim Rechnen mit Zeiten umgehen, egal ob in VBA, Abfragen, Formularen oder Berichten.

Wie Access Zeiten speichert

Grundlegende Voraussetzung für die Arbeit mit Datums- und Zeitwerten ist die Kenntnis der Art der internen Speicherung dieser Daten unter Access. Es speichert Zeiten im Double-Format, wobei die Stellen vor dem Komma die Anzahl der Tage seit dem 30.12.1899 angeben (1 bedeutet 31.12.1899, 498 steht für den 12.3.2023) und die Stellen hinter dem Komma die Uhrzeit, wobei 0 genau Mitternacht entspricht und 0,5 zwölf Uhr mittags. Entgegen den üblichen Vorstellungen kann Access auch mit Zeiten vor dem 30.12.1899 arbeiten. Dazu verwenden Sie einfach ganze Zahlen mit negativem Vorzeichen. Experimente zeigten, dass beim 1.1.100 aber definitiv Schluss ist und der Geburtstag von Jesus Christus zumindest nicht als Jahreszahl in Ihre Datenbank Einzug halten wird. Grundlegende Informationen zu diesem Thema erhalten Sie im Beitrag Datum und Zeit mit Access (www.access-im-unternehmen.de/1432). Dort erläutern wir auch die von VBA bereitgestellten Funktionen etwa zur formatierten Ausgabe von Zeitangaben oder zum Rechnen mit den Funktionen DateAdd oder DateDiff.

Zeitspannen berechnen

Die DateDiff-Funktion berechnet die Zeitspanne zwischen zwei Daten, wobei die Zeitspanne in einer beliebigen Zeiteinheit wie beispielsweise Sekunden, Stunden oder auch Tagen angegeben werden kann. Ein separates Pendant für die Berechnung von Differenzen zwischen zwei Uhrzeiten findet sich unter VBA nicht. Unter VBA berechnen Sie Zeitspannen etwa ganz einfach als Differenz zweier Literale. Im Direktfenster würde eine Beispieleingabe folgendes Ergebnis liefern:

  #10:00:00# - #8:00:00#
  8,33333333333334E-02 

Das Ergebnis ist eine in wissenschaftlicher Schreibweise dargestellte Zahl, die in ein Datum umgewandelt so aussieht:

  CDate(8.33333333284827E-02 )
02:00:00 

Beachten Sie, dass Access die Differenz im Direktfenster mit einem Komma als Dezimaltrennzeichen ausgibt, bei Berechnungen jedoch einen Punkt als solches erwartet. Schwieriger wird es bei Zeitspannen, deren Anfangs- und Endzeit nicht das gleiche Datum haben. Die Berechnung einer typischen Nachtschicht von 22 Uhr bis 6 Uhr am folgenden Tag führt so zu einer negativen Zahl:

  #6:00:00# - #22:00:00#
-0,666666666666667 

Das ist auch logisch, wenn man sich ansieht, wie die Double-Zahlen aussehen, mit denen Access intern rechnet:

  CDbl(#6:00:00#)
  0,25 
  CDbl(#22:00:00#)
  0,916666666666667 

Was also tun, wenn Sie Zeitspannen zweier Zeiten ermitteln möchten, die nicht an einem Tag liegen? Das Problem lässt sich leicht lösen, wenn es um Zeiten geht, die auf den gleichen Tag fallen oder maximal einen Tag auseinanderliegen – also beispielsweise Arbeitszeiten. Obiges Beispiel würde man mit folgender IIf-Bedingung abrunden:

  IIf(#6:00:00# - #22:00:00# > 0, #6:00:00# - #22:00:00#, #6:00:00# - #22:00:00# + 1)
  0,333333333333333 

Diese prüft, ob die Differenz negativ ist, und addiert in dem Fall den Wert 1 hinzu. Dies entspricht genau einem Tag – Sie erinnern sich, die Stellen vor dem Komma sind mit der Anzahl der Tage seit dem 30.12.1899 gleichzusetzen. Einfacher und zuverlässiger, auch für größere Zeitspannen, funktioniert dies, wenn Sie das Datum mit angeben:

  #1/3/2008 6:00:00# - #1/2/2008 22:00:00#
  0,333333333335759 

Aber halt: Das Ergebnis stimmt in den hinteren Nachkommastellen nicht mit dem tatsächlichen Ergebnis überein – was ist dort schiefgelaufen? Der Grund liegt ganz einfach in Ungenauigkeiten beim Rechnen mit Gleitkommazahlen. Für genaue Ergebnisse gibt es zwei Möglichkeiten: Entweder man rechnet getrennt nach Datum und Zeit oder man verwendet doch die DateDiff-Funktion.

Die getrennte Rechnung sieht so aus:

  #1/3/2008# - #1/2/2008# + #6:00:00# - #22:00:00#
  0,333333333333333 

Mit DateDiff geht es folgendermaßen:

Function ZeitdifferenzHMS(dat1 As Date, dat2 As Date) As String
     Dim s As Long
     Dim h As Long
     Dim n As Long
     s = DateDiff("s", dat1, dat2)
     h = Int(s / 3600)
     n = Int((s Mod 3600) / 60)
     s = Int(n Mod 60)
     ZeitdifferenzHMS = Format(h, "00") & ":" _
         & Format(n, "00") & ":" & Format(s, "00")
End Function

Dabei wird DateDiff in eine weitere Funktion gepackt. Der Hintergrund ist, dass DateDiff die Zeitdifferenz als Ergebnis nur in Form einer einzigen Zahl zurückgibt, wobei diese Zahl mit der im Funktionsaufruf angegebenen Einheit zu verknüpfen ist – hier also beispielsweise Sekunden. Die Funktion ZeitdifferenzHMS liefert diese Differenz im Format hh:nn:ss, wobei auch Differenzen größer als 24 Stunden möglich sind. Dafür kommt der Wert auch als String und nicht als Date. Der Versuch, das Resultat mit der CDate-Funktion in einen Datumswert umzuwandeln, schlägt fehl, weil CDate nicht mit Stundenwerten größer als 23 umgehen kann.

Zeitdifferenzen in Abfragen

Beim Ermitteln von Zeitdifferenzen in Abfragen ist es am einfachsten, wenn Datum und Zeit im gleichen Feld gespeichert sind. In einem weiteren Feld ermitteln Sie dann mit einem Ausdruck wie Dauer: [Endzeit]-[Startzeit] die Zeitdifferenz (siehe Bild 1). In der Formularansicht zeigt Access die Differenz dann als Dezimalzahl an (siehe Bild 2). Verpasst man diesem Ausdruck noch die oben vorgestellte Funktion ZeitdifferenzHMS, wird ein Schuh draus: Die Anzahl der Stunden wird in der folgenden Spalte im richtigen Format angezeigt, auch für Zeitspannen, die sich über Mitternacht oder mehr als 24 Stunden erstrecken:

In der Datenblattansicht zeigen sich die Zeitdifferenzen als Dezimalzahlen.

Bild 1: In der Datenblattansicht zeigen sich die Zeitdifferenzen als Dezimalzahlen.

Entwurfsansicht einer Abfrage zum Berechnen von Zeitdifferenzen

Bild 2: Entwurfsansicht einer Abfrage zum Berechnen von Zeitdifferenzen

DauerHMW: ZeitdifferenzHMS([Startzeit];[Endzeit])

Zeiten addieren

Wenn Sie die Arbeitszeiten für einzelne Tage ermittelt haben, möchten Sie diese vielleicht auch summieren, etwa um die Wochen- oder Monatsarbeitszeit zu ermitteln. Das ist prinzipiell einfach:

  #8:00:00# + #8:00:00#
16:00:00 

Ab einer Summe von 24 Stunden muss man das Ergebnis einer weiteren Bearbeitung unterziehen, denn dieses sieht dann so aus:

  #8:00:00# + #8:00:00# + #8:00:00#
31.12.1899 

Der 31.12.1899 entspricht dem Double-Wert 1,0, was eigentlich richtig ist – 24 Stunden entsprechen nun einmal einem Tag und dies wird durch den Wert 1 exakt ausgedrückt. Trotzdem hätte man vielleicht gern den Wert in Stunden angegeben. Dort hilft die folgende Funktion weiter, die ein Datum rein in das Format hh:nn:ss umwandelt:

Function ZeitHHNNSS(dat As Date) As String
     ZeitHHNNSS = CLng(dat) * 24 + _        Format(dat, "hh") & ":" & _        Format(dat, "nn") & ":" & _        Format(dat, "ss")
End Function

Der folgende Aufruf zeigt das Ergebnis im Direktbereich:

  ZeitHHNNSS("31.12.1899 10:00:00")
34:00:00

Gegebenenfalls möchten Sie diese Angabe als Dezimalzahl verpacken, die vor dem Komma die Anzahl der Stunden enthält. Dies erledigt die nächste Funktion:

Function ZeitHDezimal(dat As Date) As Double
     ZeitHDezimal = CLng(dat) * 24 + _
         Format(dat, "hh") + _
         Format(dat, "nn") / 60 + _
         Format(dat, "ss") / 3600
End Function

Es geht aber auch viel einfacher, nämlich so:

Function ZeitHDezimal(dat As Date) As Double
     ZeitHDezimal = 24 * CDbl(dat)
End Function

Ein Beispielaufruf liefert etwa Folgendes:

  ZeitHDezimal("31.12.1899 10:59:59")
  34,9997222222222

Zeiten in Abfragen addieren

Ausgehend von der obigen Abfrage sollen nun Zeiten addiert werden. Dies erledigt man in einer Abfrage normalerweise durch den Einsatz einer Funktion wie in Bild 3.

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar

Mit Zeiten rechnen

Zeit- und Datumsangaben sind elementarer Bestandteil vieler Anwendungen. Da sollte man wissen, wie man mit diesen Daten rechnet. Dabei ist es einerlei, ob es um das Ermitteln einer Zeitspanne zwischen zwei Zeitangaben oder um das Summieren von Zeiten geht – Stolperfallen gibt es dabei eine ganze Reihe. Dieser Beitrag zeigt, wie Sie die Klippen beim Rechnen mit Zeiten umgehen, egal ob in VBA, Abfragen, Formularen oder Berichten.

Wie Access Zeiten speichert

Grundlegende Voraussetzung für die Arbeit mit Datums- und Zeitwerten ist die Kenntnis der Art der internen Speicherung dieser Daten unter Access. Es speichert Zeiten im Double-Format, wobei die Stellen vor dem Komma die Anzahl der Tage seit dem 30.12.1899 angeben (1 bedeutet 31.12.1899, 39539 steht für den 1.4.2008) und die Stellen hinter dem Komma die Uhrzeit, wobei 0 genau Mitternacht entspricht und 0,5 zwölf Uhr mittags.

Entgegen den üblichen Vorstellungen kann Access auch mit Zeiten vor dem 30.12.1899 arbeiten. Dazu verwenden Sie einfach ganze Zahlen mit negativem Vorzeichen. Experimente zeigten, dass beim 1.1.100 aber definitiv Schluss ist und der Geburtstag von Jesus Christus zumindest nicht als Jahreszahl in Ihre Datenbank Einzug halten wird. Grundlegende Informationen zu diesem Thema erhalten Sie im Beitrag Datum und Zeit mit Access (Shortlink 216). Dort erläutern wir auch die von VBA bereitgestellten Funktionen etwa zur formatierten Ausgabe von Zeitangaben oder zum Rechnen mit den Funktionen DateAdd oder DateDiff.

Zeitspannen berechnen

Die DateDiff-Funktion berechnet die Zeitspanne zwischen zwei Daten, wobei die Zeitspanne in einer beliebigen Zeiteinheit wie beispielsweise Sekunden, Stunden oder auch Tagen angegeben werden kann. Ein separates Pendant für die Berechnung von Differenzen zwischen zwei Uhrzeiten findet sich unter VBA nicht. Unter VBA berechnen Sie Zeitspannen etwa ganz einfach als Differenz zweier Literale. Im Direktfenster würde eine Beispieleingabe folgendes Ergebnis liefern:

? #10:00:00# - #8:00:00#
8,33333333333334E-02

Das Ergebnis ist eine in wissenschaftlicher Schreibweise dargestellte Zahl, die in ein Datum umgewandelt so aussieht:

? CDate(8.33333333284827E-02 )
02:00:00

Beachten Sie, dass Access die Differenz im Direktfenster mit einem Komma als Dezimaltrennzeichen ausgibt, bei Berechnungen jedoch einen Punkt als solches erwartet.

Schwieriger wird es bei Zeitspannen, deren Anfangs- und Endzeit nicht das gleiche Datum haben. Die Berechnung einer typischen Nachtschicht von 22 Uhr bis 6 Uhr am folgenden Tag führt so zu einer negativen Zahl:

? #6:00:00# - #22:00:00#
-0,666666666666667

Das ist auch logisch, wenn man sich ansieht, wie die Double-Zahlen aussehen, mit denen Access intern rechnet:

? CDbl(#6:00:00#)
0,25
? CDbl(#22:00:00#)
0,916666666666667

Was also tun, wenn Sie Zeitspannen zweier Zeiten ermitteln möchten, die nicht an einem Tag liegen? Das Problem lässt sich leicht lösen, wenn es um Zeiten geht, die auf den gleichen Tag fallen oder maximal einen Tag auseinanderliegen – also beispielsweise Arbeitszeiten. Obiges Beispiel würde man mit folgender IIf-Bedingung abrunden:

? IIf(#6:00:00# - #22:00:00# > 0, #6:00:00# - #22:00:00#, #6:00:00# - #22:00:00# + 1)
0,333333333333333

Diese prüft, ob die Differenz negativ ist, und addiert in dem Fall den Wert 1 hinzu. Dies entspricht genau einem Tag – Sie erinnern sich, die Stellen vor dem Komma sind mit der Anzahl der Tage seit dem 30.12.1899 gleichzusetzen. Einfacher und zuverlässiger, auch für größere Zeitspannen, funktioniert dies, wenn Sie das Datum mit angeben:

? #1/3/2008 6:00:00# - #1/2/2008 22:00:00#
0,333333333335759

Aber halt: Das Ergebnis stimmt in den hinteren Nachkommastellen nicht mit dem tatsächlichen Ergebnis überein – was ist dort schiefgelaufen?

Der Grund liegt ganz einfach in Ungenauigkeiten beim Rechnen mit Gleitkommazahlen. Für genaue Ergebnisse gibt es zwei Möglichkeiten: Entweder man rechnet getrennt nach Datum und Zeit oder man verwendet doch die DateDiff-Funktion.

Die getrennte Rechnung sieht so aus:

? #1/3/2008# - #1/2/2008# + #6:00:00# - #22:00:00#
0,333333333333333

Mit DateDiff geht es folgendermaßen:

Function ZeitdifferenzHMS(dat1 As Date, dat2 As Date) As String
    Dim s As Long
    Dim h As Long
    Dim n As Long
    s = DateDiff("s", dat1, dat2)
    h = Int(s / 3600)
    n = Int((s Mod 3600) / 60)
    s = Int(n Mod 60)
    ZeitdifferenzHMS = Format(h, "00") & ":" & Format(n, "00") & ":" & Format(s, "00")
End Function

Dabei wird DateDiff in eine weitere Funktion gepackt. Der Hintergrund ist, dass DateDiff die Zeitdifferenz als Ergebnis nur in Form einer einzigen Zahl zurückgibt, wobei diese Zahl mit der im Funktionsaufruf angegebenen Einheit zu verknüpfen ist – hier also beispielsweise Sekunden. Die Funktion Zeitdifferenz liefert diese Differenz im Format hh:nn:ss, wobei auch Differenzen größer als 24 Stunden möglich sind. Dafür kommt der Wert auch als String und nicht als Date. Der Versuch, das Resultat mit der CDate-Funktion in einen Datumswert umzuwandeln, schlägt fehl, weil CDate nicht mit Stundenwerten größer als 23 umgehen kann.

Zeitdifferenzen in Abfragen

Beim Ermitteln von Zeitdifferenzen in Abfragen ist es am einfachsten, wenn Datum und Zeit im gleichen Feld gespeichert sind. In einem weiteren Feld ermitteln Sie dann mit einem Ausdruck wie Dauer: [Endzeit]-[Startzeit] die Zeitdifferenz (s. Abb. 1). In der Formularansicht zeigt Access die Differenz dann als Dezimalzahl an (s. Abb. 2). Verpasst man diesem Ausdruck noch die oben vorgestellte Funktion ZeitdifferenzHMS, wird ein Schuh draus: Die Anzahl der Stunden wird im richtigen Format angezeigt, auch für Zeitspannen, die sich über Mitternacht oder mehr als 24 Stunden erstrecken (siehe ebenfalls Abb. 2):

pic001.tif

Abb. 1: Entwurfsansicht einer Abfrage zum Berechnen von Zeitdifferenzen

pic002.tif

Abb. 2: In der Formularansicht zeigen sich die Zeitdifferenzen als Dezimalzahlen.

DauerHMW: ZeitdifferenzHMS([Startzeit];[Endzeit])

Zeiten addieren

Wenn Sie die Arbeitszeiten für einzelne Tage ermittelt haben, möchten Sie diese vielleicht auch summieren, etwa um die Wochen- oder Monatsarbeitszeit zu ermitteln. Das ist prinzipiell einfach:

? #8:00:00# + #8:00:00#
16:00:00

Ab einer Summe von 24 Stunden muss man das Ergebnis einer weiteren Bearbeitung unterziehen, denn dieses sieht dann so aus:

? #8:00:00# + #8:00:00# + #8:00:00#
31.12.1899

Der 31.12.1899 entspricht dem Double-Wert 1,0, was eigentlich richtig ist – 24 Stunden entsprechen nun einmal einem Tag und dies wird durch den Wert 1 exakt ausgedrückt. Trotzdem hätte man vielleicht gern den Wert in Stunden angegeben. Dort hilft die folgende Funktion weiter, die ein Datum rein in das Format hh:nn:ss umwandelt:

Function ZeitHHNNSS(dat As Date) As String
    ZeitHHNNSS = CLng(dat) * 24 + _
    Format(dat, "hh") & ":" & _
    Format(dat, "nn") & ":" & _
    Format(dat, "ss")
End Function

Der folgende Aufruf zeigt, wie dies im Direktfenster aussieht:

? ZeitHHNNSS("31.12.1899 10:00:00")
34:00:00

Gegebenenfalls möchten Sie diese Angabe als Dezimalzahl verpacken, die vor dem Komma die Anzahl der Stunden enthält. Dies erledigt die nächste Funktion:

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar