Language: Deutsch English















Last Update: 2017 - 11 - 29





Der Datum/Uhrzeit Datentyp in VBA und Access

von Philipp Stiefel, veröffentlicht: 2017-11-29


Article header Datum/Uhrzeit Datentyp in VBA und Access

Basierend auf einem Foto von Estée Janssens, verwendet hier unter CC0 licensing

 

Im Allgemeinen sind wir sehr vertraut mit Daten (Plural von Datum!). Wir haben ständig mit ihnen zu tun, sowohl in unserem echten Leben, als auch im digitalen Leben. Also, was könnte da schiefgehen, wenn wir mit Daten in Access Datenbanken und in der VBA-Programmierung arbeiten?

Eine ganze Menge!

Während Datum und Uhrzeit in Access und VBA auf den ersten Blick sehr einfach und unkompliziert erscheinen, gibt es tatsächliche eine ganze Menge Stolpersteine im Zusammenhang mit der Behandlung von Datumswerten. In Access-bezogenen Foren sehe ich Anfänger in der Access-Entwicklung beinah jeden Tag mit dem Thema kämpfen. – Ich versuche hier verständliche Informationen bereitzustellen und dir einen Leitfaden zu geben, um Probleme zu vermeiden, wenn du in Access und VBA mit Datumswerten arbeitest.

Einführung und Grundlagen

Es gibt einen dedizierten Datum/Uhrzeit Datentyp in Access und einen korrespondierenden Date Datentyp in VBA. Der erste wichtige Punkt, den du verinnerlichen solltest, ist dass in diesem Datentyp immer Datum und Uhrzeit gespeichert ist.

Ein sehr gängiges Missverständnis ist die Idee, dass man ein Feld in einer Access-Tabelle erstellen kann, dafür den Datum/Uhrzeit Datentyp einstellt, ein Format ohne Uhrzeit (z.B. „Datum, kurz“) auswählt und somit eine Nur-Datum-Spalte in der Tabelle erstellt.

Das funktioniert so nicht!

Eine Datum/Uhrzeit-Spalte speichert immer Datum und Uhrzeit. Die Format-Eigenschaft einer Tabellenspalte beeinflusst nur wie dir die Daten angezeigt werden. Es beeinflusst aber nicht, wie die Daten tatsächlich in der Datenbank gespeichert werden. In anderen Worten, es wird dir nur das Datum angezeigt, aber die Uhrzeit wird trotzdem zusätzlich gespeichert. – Du musst dir dessen bewusst sein, andernfalls wirst du früher oder später eine unangenehme Überraschung erleben.

Interne Speicherung

Weil wir gerade dabei sind, lass uns doch mal genauer betrachten, wie der VBA und Access Datum/Uhrzeit Datentyp intern aufgebaut ist. Eine Sache, die man wissen sollte, ist dass der Date(/Time) Datentyp nur eine sehr dünne Kapselung des Double Datentyps ist.

Double ist ein 6 Byte Fließkommadatentyp und kann mindestens 15 signifikante, numerische Stellen speichern. Die Details von Fließkommazahlen würden den Rahmen dieses Artikels sprengen. Allerdings gibt es ein Detail von Fließkommazahlen, das du wissen solltest. Sie sind nicht exakt! Wenn du einer Fließkommavariable einen Wert zuweist oder einen solchen Wert in einer Datenbankspalte speicherst, könnte der gespeicherte Wert extrem nah an der Zahl, die du zugewiesen hast dran sein, aber eben nicht exakt diese Zahl.

Datum/Uhrzeit-Werte werden in dem Double Typ in einer sehr einfachen, aber effektiven Art gespeichert. Der Ganzzahlteil der Zahl repräsentiert die Tage seit dem 30. Dezember 1899. Die Nachkommastellen repräsentieren die Bruchteile des Tages, also die Uhrzeit. Negative Zahlen repräsentieren Daten vor dem 30.12.1899.

Ein Double-Wert von 1,5 entspricht dem Zeitpunkt 31.12.1899 12:00 Uhr mittags. Die aktuelle Zeit (18.06.2017 15:32:01) entspricht dem Double-Wert 42904,64723.

Du kannst die CDbl Typkonvertierungsfunktion verwenden, um explizit ein Datum in einen Double-Typ umzuwandeln. Andersherum kannst du die CDate Funktion verwenden, um einen Double in ein Date umzuwandeln. Diese Umwandlung ist verlustfrei. Du verlierst keinerlei Informationen, wenn du zwischen diesen beiden Typen umwandelst.

Public Sub TestDateConversion() Dim myDate As Date Dim myDouble As Double myDouble = CDbl(Now()) myDate = CDate(myDouble) MsgBox "The Double value " & myDouble & " represents the Date value " & myDate & "!" End Sub

 

Anzeige von Double und Date Werten im message box text

Im obigen Beispiel verwende ich die Now-Funktion um das aktuelle Datum inkl. Uhrzeit zu erhalten. Es wird sofort in einen Double konvertiert und danach zurück in ein Date. Die MessageBox zeigt dann die verschiedenen Textrepräsentationen der Variablen, abhängig von ihren jeweiligen Datentypen, an. Der interne Wert der beiden Variablen ist dabei aber exakt derselbe.

Der spezifische Date-Datentyp erweitert den Double-Kern nicht nennenswert. Sein wesentlicher Vorteil liegt in seiner Ausdrucksklarheit. Allein dadurch, dass er ein Date anstelle nur eines Doubles ist, bekommt der numerische Wert seine wahre Bedeutung. Außerdem wird dann, wenn ein Date angezeigt wird oder explizit in einen String konvertiert wird, automatisch das Format „Standarddatum“ für die Text-Repräsentation des Datums verwendet.

Die CDbl- bzw. CDate-Funktion in dem obigen Beispiel sind übrigens nicht zwingend erforderlich. Eine implizite Konvertierung zwischen Variablen der beiden Datentypen funktioniert ganz genauso. Nichtsdestotrotz bevorzuge ich es immer in meinem Code explizit zu sein. Ich schreibe daher eher die explizite Konvertierungsfunktion um die Intention meines Codes deutlicher zu machen.

VBA Datums- und Uhrzeit-Funktionen

In dem Modul VBA.DateTime gibt es eine ganze Reihe an eingebauten Funktionen für die Arbeit mit Datum/Uhrzeit-Werten. Wenn du dieses Modul noch nicht kennst, empfehle ich dir dringend, den VBA Objektexplorer zu öffnen (Taste [F2] im VBA-Editor) und einen Blick auf diese Funktionen zu werfen.

Ein Wort zur Datumsarithmetik; die Funktion DateAdd kann verwendet werden, um ein Intervall (z.B. Tage, Monate, Jahre) zu einem Datum hinzuzurechnen. Dies ist eine Möglichkeit, um eine beliebige Anzahl an Tagen zu einem Datum hinzuzurechnen. Wegen der internen Abbildung des Date-Datentyps als Double, könntest du genauso schreiben AnyDateVariable +1 , um einen Tag zu einem Datum zu addieren.

Die einfache Addition ist sicherlich weniger teuer (in Rechenzeit), als ein Funktionsaufruf. Dennoch ziehe ich es vor die DateAdd-Funktion zu verwenden, um meine Absicht deutlicher zu formulieren. Wenn du Monate oder Jahre zu einem Datum hinzurechnen willst, benötigst du die Funktion sowieso, da es nicht so geradlinig zu ermitteln ist, welchen numerischen Wert in Tagen du dann zu dem Datum addieren musst um das gewünschte Ergebnis zu erhalten.

Wenn du ein Intervall von einen Datum abziehen möchtest, kannst du die DateAdd-Funktion mit einer negativen Zahl und dem entsprechenden Intervall aufrufen. Die einfache, mathematische Operation funktioniert ebenfalls, um Tage von einem Datum abzuziehen.

Die DateDiff-Funktion ist sehr nützlich, um die Differenz in einem bestimmten Intervall (wieder Tage, Monate, Jahre) zu ermitteln. Es gibt noch einige andere nützliche eingebaute Funktionen im Modul VBA.DateTime. Du solltest also definitiv dort mal reinschauen.

Nach dieser recht langen Einführung, wenden wir uns jetzt ein paar sehr gängigen Problemen mit Datumswerten in VBA und Access zu.

Datumskriterium in Abfragen - Denk dran: Es ist immer Datum und Zeit!

Meistens wollen wir in Abfragen das Datum vergleichen, ohne die Zeit zu berücksichtigen. Wenn du das versuchst, könnte dir ein sehr häufiges Problem mit dem Datum/Uhrzeit-Typ begegnen. Die Anwesenheit von Zeit.

Stell dir vor du hast eine Tabelle mit einen Datum/Uhrzeit-Feld erstellt. Jetzt möchtest du anhand des Datums die Daten eines bestimmten Tages abfragen. Das scheint ein einfaches Vorhaben zu sein.

Du erstellst eine neue Abfrage und gibst das gewünschte Datum als Kriterium ein. - Aber was ist jetzt passiert? Es fehlt ein Datensatz im erwarteten Abfrageergebnis!

Falsche Formulierung der Kriterien und unerwartete Abfrageergebnisse

Wenn du dir die Daten in dem Screenshot genau ansiehst, wird der Grund schnell deutlich. Der Datensatz, bei dem eine Uhrzeit zum Datum eingegeben ist, fehlt im Ergebnis.

Die Daten, die für die anderen Datensätze angezeigt werden, suggerieren, dass diese ohne Uhrzeit gespeichert sind. Das stimmt nicht. Die automatische Formatierung als Standarddatum („General Date“), die hier für die Anzeige der Daten verwendet wird, zeigt nur keine Zeit für den Wert 00:00:00 an. Genauso wird die Zeit automatisch aus den Kriterien entfernt, falls du dort explizit 00:00:00 angibst. Dennoch wird diese Zeit verwendet, um die Kriterien mit den Tabellendaten zu vergleichen.

Die Tabellenspalte als „Datum, kurz“ („Short Date“) zu formatieren, macht ändert nichts an diesem Verhalten. Es macht das Ganze nur noch schlimmer, weil du dann überhaupt keine Zeitangaben mehr in der Tabelle siehst.

Die richtige Lösung für alle tagesbasierenden Abfragen ist es, die Datumswerte so zu filtern, dass ein Datum größer gleich des Anfangstages und kleiner des dem Enddatum nachfolgenden Tages auszugeben.

Der Begrenzer für Datum/Uhrzeit-Literale in Access/Jet-SQL ist das Raute-Zeichen (Hash-Symbol, #). Also sieht der Kriterienausdruck in der Designansicht der Abfrage jetzt so aus:

	>=#23.08.2017# AND <#24.08.2017#

I used the German date format here, you should type the date according to your regional settings. E.g. mm/dd/yyyy if you are in the US.

 

Korrekte Formulierung der Kriterien und erwartete Abfrageergebnisse

Wie du in dem Screenshot sehen kannst, entsprechen die Ergebnisse jetzt unseren Erwartungen.

Verwende in dem Kriterienausdruck nicht den BETWEEN-Operator anstelle von >= AND <. Das würde dann einem >= AND <= entsprechen und damit 00:00 Uhr am Folgetag einschließen und ein anderes, vermutlich falsches, Ergebnis bewirken.

Mein Beispiel zeigt die Abfrage der Daten für einen einzelnen Tag. Aber dies trifft genauso zu, wenn du die Daten einer ganzen Woche, eines Monats oder eines Jahres abfragen möchtest.

Nur-Datum-Werte in einer Tabelle erzwingen

Wie bereits mehrfach erwähnt, das Anzeigeformat einer Datumspalte in einer Tabelle hat keine Auswirkung darauf, wie Daten in dieser Spalte gespeichert werden.

Die einzige Möglichkeit um Nur-Datum-Werte, also Werte mit einer Zeit von genau 00:00:00, in einer Tabelle zu erzwingen, ist eine Gültigkeitsprüfungsregel (Validation Rule) für diese Spalte festzulegen.

Für Spalten, die keine NULL-Werte zulassen (Eingabe Erforderlich=Ja) gibt es einige Ausdrücke die man als Gültigkeitsregel zu diesem Zweck verwenden kann. Schwieriger ist das, wenn die Spalte NULL-Werte zulässt. NULL wird bei den meisten Ausdrücken einen Fehler auslösen und somit das Speichern der Daten verhindern.

Hier ist ein Ausdruck für eine Gültigkeitsregel, den du auch bei Spalte mit NULL-Werten verwenden kannst:

0 = Hour([DateValueCol]) + Minute([DateValueCol]) + Second([DateValueCol]) Or IsNull([DateValueCol])

Hier ist derselbe Ausdruck, eingegeben in die Eigenschaft Gültigkeitsregel (Validation Rule), in der Entwurfsansicht einer Tabelle.

 

Gültigkeitsregel für Tabellenspalte in der Entwurfsansicht

 

SQL Strings mit Datum/Uhrzeit-Kriterien in VBA aufbauen

Datumswerte müssen passend formatiert sein, um sie in die SQL-Zeichenfolge einer Abfrage in VBA einzubetten.

Wenn du einen Datumswert in einen SQL String einbettest, müssen die Datumswerte in ihre korrespondierende Textdarstellung konvertiert werden. Access/VBA verwendet das Format Standarddatum um diese Konvertierung automatisch vorzunehmen. Dieses Format basiert auf den Regionseinstellungen der Windows Systemsteuerung.

Wenn du die Regionseinstellungen auf deinem Rechner für die USA konfiguriert hast, wäre das Format MM/DD/YYYY, wenn du sie für Deutschland eingestellt hast, ist es DD.MM.YYYY.

Wenn du innerhalb von VBA Text in Datum konvertieren willst, dann verwendet die VBA Laufzeitumgebung automatisch das Format aus der Systemsteuerung für die Textanalyse. Sie ist in der Lage das korrekte Datum zu ermitteln, solange es diesen Einstellungen entspricht.

Die Jet-/Ace-Datenbank-Engine ist diesbezüglich wesentlich strenger. Sie erkennt, völlig unabhängig von den Regionaleinstellungen, Datumswerte nur in entweder dem US-Datumsformat (MM/DD/YYYY) oder im internationalen ISO-Datumsformat (YYYY-MM-DD).

Wenn du in einer Umgebung mit US-Datumsformat arbeitest, könntest du einfach faul sein und das Problem komplett ignorieren. Durch Zufall wird bei dir das Datum bereits richtig in den SQL-String eingebettet. - Darauf solltest du dich aber besser nicht verlassen. - Wenn du es doch tust, wird dein Anwendung nicht mehr funktionieren, sobald sie von irgendjemand verwendet wird, der auf seinem Rechner nicht das US-Datumsformat eingestellt hat.

Um das Problem wirklich zu behandeln, empfehle ich jedes Datum, das in einen SQL-String eingebettet wird, explizit im ISO-Format zu formatieren. - Du könntest auch das US-Format verwenden, aber ich ziehe das ISO-Format deutlich vor, weil es optisch wesentlich eindeutiger lesbar ist.

Du kannst die Format-Funktion verwenden, um das gewünschte Format zu erhalten. Die Format-Funktion liefert das Datum in einem Format entsprechend deiner Formatvorgabe zurück.

Dim sql As String sql = "SELECT * " & _ " FROM aTable " & _ " WHERE DateValue >= #" & Format(aDateVariable, "yyyy-mm-dd") & "# " & _ " AND DateValue < #" & Format(anotherDateVariable, "yyyy-mm-dd") & "#;"

Du erinnerst dich an das Raute-/Hash-Zeichen als Begrenzer für Datumswerte in Access-/Jet-SQL? In der grafischen Entwurfsansicht einer Abfrage ergänzt Access dieses Zeichen in den meisten Fällen automatisch für dich. Wenn du selbst SQL-Strings in VBA schreibst, ist es deine Aufgabe dieses Zeichen entsprechend in den Abfragetext einzubauen.

Was ist mit der Zeit in SQL-Abfragen?

Wenn du ausdrücklich die Uhrzeit im Kriterium deiner SQL-Abfrage verwenden möchtest, ist das weniger kompliziert als das Datum selbst. Du verwendest einfach zusätzlich hh:nn:ss im Format für deinen Datum/Uhrzeit-Wert, wobei nn der Platzhalter für die Minuten ist. Dies resultiert dann in dem eindeutigen 24-Std. Zeitformat, das auch von der Jet-/Ace-Datenbank-Engine verstanden wird.

Dim sql As String sql = "SELECT * " & _ " FROM Table1 " & _ " WHERE DateTimeValue >= #" & Format(aDateTimeVariable, "yyyy-mm-dd hh:nn:ss") & "#;"

 

Datum/Uhrzeit in Internationaler Umgebung

Du magst dich fragen, wie du mit Datum/Uhrzeit-Werten in deinen Anwendungen verfahren sollst, wenn diese international, d.h. in verschiedenen Ländern und mit verschiedenen Datums- / Zeitformaten eingesetzt werden.

Es gibt eine einfache Regel, die dir viel Ärger ersparen kann: Weniger ist mehr!

VBA und Access verwenden automatisch die Regional- und Ländereinstellungen des Windows-Betriebssystems um Datums- und Uhrzeitwerte in deiner Anwendung darzustellen und zu parsen. Je weniger du an der Dateneingabe und -darstellung herumfummelst, desto weniger Probleme sind auf Rechnern mit abweichenden Ländereinstellungen zu erwarten.

Verwende vordefinierte Formate anstelle von fixen, benutzerdefinierten Formaten

Wenn du dich in Deutschland befindest und das Format für eine Tabellenspalte oder ein Formularsteuerelement explizit auf das Format „DD.MM.YYYY“ einstellst, wird es genauso angezeigt, wie mit dem vordefinierten, benannten Format „Short Date“ („Datum, kurz“).

Wenn nun aber in Benutzer in einem anderen Land (z.B. den USA) mit einem anderen Datumsformat deine Datenbank öffnet, dein explizit formatiertes Steuerelement wird dann immer noch als „DD.MM.YYYY“ dargestellt. - Das ist sehr verwirrend für Menschen, die nicht mit diesem Format vertraut sind.

Hättest du aber das benannte Format „Datum, kurz“ verwendet, dann würde der Wert im Steuerelement als „MM/DD/YYYY” angezeigt, also passend zu der Erwartung des Benutzers vor seinen kulturellen Hintergrund.

Und die Sache ist sogar noch schlimmer, als sie auf den ersten Blick scheint. Jedes Datum, das in ein solches Feld eingegeben wird, wird basierend auf dem Format aus den Windows Regionaleinstellungen analysiert, nicht anhand deines fixen Formates. In dem obigen Beispiel muss also der Benutzer seine Eingabe im Format „MM/DD/YYYY” machen damit sie korrekt von Access erkannt wird. Diese Eingaben wird ihm aber unmittelbar anschließend in deinem vorgegeben Format „DD.MM.YYYY“ angezeigt.

Vordefinierte Formate im Access Eigeschaftenblatt

Verwende immer vordefinierte, benannte Formate anstelle von benutzerdefinierten, fixen Formaten für die Formatierung von der Benutzeroberfläche deiner Anwendung.

Genau dasselbe trifft auch für Zeitwerte in einem internationalem Szenario zu. Die Zeit wird zwar in allen mir bekannten Kulturen in dem Format „hh:nn:ss“ (nn steht für die Minuten) dargestellt, aber es kann entweder im 24-Stunden Format dargestellt werden, oder im 12-Stunden Format mit einem angehängten AM/PM. Wenn du dich an die gleichen Regeln, wie oben hältst, und die Finger von der Zeitformatierung entweder komplett weglässt, oder ausschließlich benannte Formate (z.B. „Zeit, kurz“) verwendest, dann wirst du und deine Benutzer keine Probleme haben.

Wann explizite Datumsformate benutzen?

Du solltest explizite Formatierungen ausschließlich für Berichte oder im Datenaustausch verwenden, wenn entweder unternehmensweite Vorgaben oder eine Dateispezifikation das Datumsformat festlegen.

In gedruckten (oder PDF) Berichten sollte sich die Datumsformatierung mehr an den Erwartungen der Empfänger orientieren, als an den Einstellungen auf dem Computer des Erstellers. Du kannst die Format-Eigenschaft der Berichtssteuerelemente verwenden, um das Ausgabeformat der Daten in deinem Bericht zu bestimmen. Für Zeiten kannst du explizit eine 12-Std. Formatierung erzwingen, wenn du die „AM/PM“ Uhrzeitspezifikation an die Formatdefinition anhängst.

Wenn deine Anwendung Datum/Uhrzeit-Werte in Textdateien exportieren oder daraus importieren soll, musst du unbedingt eine Spezifikation für diese Textdateien festlegen, oder dich an eine bereits bestehende halten. Diese Spezifikation muss festlegen, welches Datumsformat in den Dateien zu verwenden ist. Wenn du deine Datenimport/-export Routinen entwickelst, musst du das Datumsformat explizit passend zu der Spezifikation einstellen.

In Access kannst du das Datumsformat für Text-Importe/Exporte Einstellen und dieses dann in einer Import-/Export-Spezifikation speichern.

 

Dateum, Zeit and Nummer Einstellungen in Access Dateiimport Spezifikation

Wenn du allerdings Daten in ein Dateiformat, wie z.B. Microsoft Excel oder dBase, exportierst, das selbst ein eigenes Datumsformat hat, dann ist es wieder angeraten die Finger wegzulassen und die Datumstypen ohne irgendwelche Formatierungen zu exportieren.

Eingabeformate (Maske) für Datumswerte

Ich halte generell nicht viel von starren Eingabeformaten (Eingabemasken) zur Dateneingabe. Wenn deine Anwendung nur von Benutzern einer bestimmten Kultur verwendet wird, mögen sie noch erträglich sein. Sobald allerdings Benutzer mit verschiedenen Regionaleinstellungen mit deiner Anwendung arbeiten, werden Eingabemasken noch schlimmer als üblich.

Ein Eingabeformat in Access ist sehr statisch. Wenn du also eine Eingabemaske basierend auf deine aktuellen Ländereinstellungen konfigurierst, wird sich diese nicht an ein abweichendes Eingabeformat anpassen. Erstaunlicherweise richtet das nicht allzu viel Schaden an, wenn sich das Datumformat von DD.MM.YYYY zu MM/DD/YYYY ändert, außer natürlich, dass in der Maske das falsche Trennzeichen angezeigt wird. Wenn der Benutzer aber das Datumsformat YYYY-MM-DD konfiguriert hat, sind die Auswirkungen offensichtlich höchst irritierend.

Ich rate dir hier erneut, die Weniger-ist-mehr-Regel anzuwenden und von Eingabeformaten Abstand zu nehmen. Wenn du dennoch Eingabeformate definieren willst, musst du in deiner Anwendung entsprechende Logik vorsehen, die die Eingabemasken zur Laufzeit an die jeweils aktuellen Regionseinstellungen des Computers anpasst.

Fazit

Es gibt eine Menge Aspekte, die man bei der Behandlung von Datum und Uhrzeit in Access Anwendung beachten sollte. Glücklicherweise sind die Lösungen für fast alle wirklich anspruchsvollen Probleme in Access bereits eingebaut. Du musst also nur wissen, wann und wie du die verfügbaren Werkzeuge richtig einsetzt.

Ich habe niemals erwartet, dass dieser Artikel so lang wird, wie er das jetzt ist. Ich hoffe, dass es mir gelungen ist, dir einen vollständigen und verständlichen Leitfaden für die Entwicklung von Anwendungen mit robuster und flexibler Datumsbehandlung an die Hand zu geben.

 

 

Share this article: Share on Facebook Tweet Share on LinkedIn Share on XING

Abonniere meinen Newsletter

*

Ich werde Deine Email-Addresse niemals weitergeben. Du kannst den Newsletter jederzeit abbestellen.



© 1999 - 2017 by Philipp Stiefel