Tipp 0447 Datumseingabe prüfen
Autor/Einsender:
Datum:
  Angie/Jürgen Beil
18.04.2005
Entwicklungsumgebung:   VB 6/VBA 6
Zur Darstellung des Datums gibt es weltweit sehr verschiedene Schreibweisen. Die selbe Angabe kann in unterschiedlichen Darstellungen für ein unterschiedliches Datum stehen. Eine Datumsangabe in der Form 03/11/04 kann beispielsweise interpretiert werden als
  3.  November 2004
  11. März 2004
  4.  November 2003
Eine eindeutige Datumsangabe kann durch Angabe des Monatsnamens in ausgeschriebener oder abgekürzter Form, und durch eine vierstellige Jahreszahl erzielt werden, unabhängig davon, an welcher Stelle Tag, Monat oder Jahr angegeben werden:
  3. November 2004
  November 3, 2004
  3. Nov. 2004
  Nov. 3, 2004
In diesem Zusammenhang sei hier auf die ISO 8601 in der Wikipedia - die freie Enzyklopädie hingewiesen. ISO 8601 ist ein internationaler Standard der ISO, der Datumsformate und Zeitangaben beschreibt und Empfehlungen für den Gebrauch im internationalen Kontext ausspricht.
Steuerelemente für die Datumseingabe
In der Praxis bietet sich für die Eingabe eines Datums in VB das DTPicker- oder Kalender-Steuerelement an. Damit ist gewährleistet, dass es sich beim Rückgabewert um ein "gültiges" Datum handelt. Eine weitere Prüfung ist also nicht notwendig.
Eine andere Möglichkeit wäre die Eingabe in einer TextBox oder auch InputBox, wobei, wie aus den weiteren Ausführungen deutlich wird, es nicht sinnvoll ist, dem Anwender nur eine TextBox/InputBox für die Datumseingabe anzubieten. Besser sind drei TextBoxen, in denen Tag, Monat und Jahr getrennt voneinander eingegeben werden, oder aber auch drei ComboBoxen.
Rückgabe der IsDate-Funktion
Die IsDate-Funktion gibt einen Wert vom Typ Boolean zurück, der angibt, ob ein Ausdruck in ein Datum umgewandelt werden kann. In der MSDN wird der Datumsausdruck wie folgt beschrieben:
"Jeder Ausdruck, der als Datum angesehen werden kann, einschließlich Datumsliterale, datumsähnliche Zahlen, datumsähnliche Zeichenfolgen und Datumsangaben, die von Funktionen zurückgegeben werden. Ein Datumsausdruck ist auf Zahlen oder Zeichenfolgen in beliebiger Kombination begrenzt, die ein Datum im Bereich vom 1. Januar 100 - 31. Dezember 9999 darstellen können.
Datumsangaben werden als Teil einer reellen Zahl gespeichert. Werte links vom Dezimalzeichen stehen für das Datum, Werte rechts vom Dezimalzeichen für die Uhrzeit. Negative Zahlen geben Daten vor dem 30. Dezember 1899 an."
Weitere Infos zum Thema Datum und Kalender können der MSDN (VB-Hilfe) und den Links zum Thema entnommen werden.
Immer wieder taucht die Frage auf, wie mit VB/VBA ein vom Benutzer eingegebenes Datum auf Gültigkeit geprüft kann. Als Antwort wird meistens gegeben: "Mit der IsDate-Funktion !". Wie aus den folgenden Ausführungen deutlich wird, kann die Verwendung der IsDate-Funktion jedoch zu unerwünschten Ergebnissen führen.
Im deutschsprachigem Raum sieht man oft Codeschnipsel, die wie folgt oder ähnlich aussehen:
 
Dim dtmDate As Date

If IsDate("03.11.2004") Then
   dtmDate = CDate("03.11.2004")
End If
 
Die obige Abfrage mit IsDate gibt nur dann True zurück, wenn in den Regions- und Sprachoptionen genau dieses Datumsformat definiert ist, wie beispielsweise im Gebietsschema Deutsch (Deutschland) ohne manuellen Änderungen.
Bessere, aber nicht unbedingt "sichere" Rückgaben können erzielt werden, wenn statt dem Punkt beispielsweise ein Schrägstrich (/) oder Bindestrich (-) verwendet wird. Die Abfrage
 
Dim dtmDate As Date

If IsDate("1a20-3-12") Then
   dtmDate = CDate("1a20-3-12")
End If
 
gibt auch True zurück, und mit der CDate-Funktion in ein Datum umgewandelt wäre das der 20.03.2012 01:00:00 (Gebietsschema Deutsch (Deutschland)).
Mit der CDate-Funktion kann ein Datumsausdruck in einen Wert vom Typ Date umgewandelt werden. Die Rückgabe von
 
MsgBox Format$(CDate("11/03/04"), "dd. mmmm yyyy")
 
kann jedoch zu unterschiedlichen Ergebnissen führen. Aus folgender Tabelle gehen die verschiedenen Datumsrückgaben, je nach eingestelltem Gebietsschema, deutlich hervor.
Gegenüberstellung Datumsrückgaben mit verschiedenen Gebietsschemata
Zum Vergleich wurden hier die Gebietsschemata Deutsch (Deutschland), Englisch (Großbritannien (UK)) und Englisch (USA) verwendet, ohne im jeweiligen Gebietsschema selbst Veränderungen vorzunehmen. Als Beispiel-Datumseingabe wurde ein String angenommen (entspricht beispielsweise eine Eingabe in einer TextBox), der, je nach Gebietsschema, von der IsDate-Funktion als "gültiger" Datumsausdruck angesehen wurde oder auch nicht! Für die Rückgabe wurde, wenn dies möglich war, der Datumsausdruck mit der Typ-Umwandlungsfunktion CDate in ein Datum umgewandelt und mit der Format-Funktion für eine eindeutige Datumsausgabe im Format dd. mmmm yyyy ausgegeben.
  Datum (Eingabe) Deutsch (D) Englisch (UK) Englisch (USA)
  Beabsichtigtes Rückgabe-Datum: 3. November 2004
1 03.11.2004 03. November 2004 IsDate = False IsDate = False
2 03/11/2004 03. November 2004 03. November 2004 11. March 2004
3 11/03/2004 11. März 2004 11. March 2004 03. November 2004
4 11/03/04 11. März 2004 11. March 2004 03. November 2004
5 04/11/03 04. November 2003 04. November 2003 11. April 2003
6 2004/11/03 03. November 2004 03. November 2004 03. November 2004
7 2004-11-03 03. November 2004 03. November 2004 03. November 2004
  Fiktives Datum: 31. Februar 2004
8 31.02.2004 IsDate = False IsDate = False IsDate = False
9 31.02.04 04. Februar 1931 IsDate = False IsDate = False
10 31/02/2004 IsDate = False IsDate = False IsDate = False
11 31/02/04 04. Februar 1931 04. February 1931 04. February 1931
12 04/02/31 04. Februar 1931 04. February 1931 02. April 1931
13 2004/02/31 IsDate = False IsDate = False IsDate = False
14 2004-02-31 IsDate = False IsDate = False IsDate = False
Der Versuch, die Logik der Datumsrückgaben für die Daten mit zweistelliger Jahreszahl nachzuvollziehen, könnte wie folgt aussehen:
Kann ein Ausdruck als Datum interpretiert werden (Rückgabe der IsDate-Funktion ist True), wird bei der Typ-Umwandlung in ein Datum (CDate) scheinbar zunächst davon ausgegangen, dass der Datumsausdruck je nach Einstellung im Gebietsschema z. B. in der Form Tag/Monat/Jahr (D und UK) oder Monat/Tag/Jahr (USA), übergeben wurde. Handelt es sich nicht um ein "gültiges" Datum, so wird intern zunächst Tag und Monat vertauscht, und wenn das auch zu keinem vernünftigen Ergebnis führt, Jahr/Monat/Tag angenommen.
Fazit
1. Die IsDate-Funktion alleine reicht nicht aus, um eine vom Anwender eingegebenes Datum auf "Gültigkeit" prüfen zu können.
2. Wie aus obiger Tabelle ersichtlich, gibt es zwei Datumsformate, die, egal welches Gebietsschema verwendet wurde, das beabsichtige Datum 3. November 2004 zurückgegeben haben und das fiktive Datum als nicht korrekten Ausdruck.
 
  2004/11/03    (Jahr/Monat/Tag) 
  2004-11-03    (Jahr-Monat-Tag)  (ISO 8601)
  Ein gültiger (!) Datumsausdruck im Format yyyy-mm-dd (ISO 8601), der mit der Typ-Umwandlungsfunktion CDate in ein Datum umgewandelt wird, gibt immer das gleiche Datum zurück, unabhängig vom eingestellten Gebietsschema. Auch ggf. manuell gemachte Änderungen in den Regions- und Sprachoptionen haben darauf keinen negativen Einfluss.
Ersatzfunktionen für die IsDate-Funktion
Die beiden folgenden Funktionen bieten einen Lösungsansatz, um die von einem Anwender in TextBoxen eingegebenen Datumswerte für Jahr, Monat und Tag auf ein "gültiges" Datum hin überprüfen zu können. Sie werden wie die IsDate-Funktion verwendet (Rückgabe True/False), jedoch mit dem Unterschied, dass statt einem Datumsausdruck oder Zeichenfolgenausdruck die Jahres-, Monats- und Tageszahl als Argumente übergeben werden. Zusätzlich kann als optionales Argument das Datum in einer Variablen vom Datentyp Date zurückgegeben werden.
Anmerkung: Eine Variable vom Typ Date hat standardmäßig den Wert 0. Dieser wird aber als 30. Dezember 1899 00:00:00 Uhr interpretiert! Daher sollte das von der Funktion zurückgegebene Datum (optionales Argument) nur dann weiter verwendet werden, wenn der Rückgabewert der Funktion True ist.
Datumseingabe mit der IsDate-Funktion prüfen
Um mit der IsDate-Funktion eine "sichere" Rückgabe zu erhalten, muss bei der Auswertung der Eingabe zunächst die Jahreszahl geprüft werden, ob diese im Bereich von 100 bis 9999 liegt, damit das Jahr eindeutig bestimmt werden kann, und nicht aus 20/11/03 (Jahr/Monat/Tag) z. B. der 20. November 2003 wird, statt wie ggf. beabsichtigt, der 3. November 2020.
Anschließend wird der Monat auf Gültigkeit geprüft, dieser muss im Bereich von 1 bis 12 liegen.
Um Abfragen bzgl. der Anzahl der Tage im Monat, und im Fall Februar, ob es sich um ein Schaltjahr gehandelt hat, zu umgehen, wird der eingegebene Wert für den Tag nicht gesondert geprüft, da der Datumsausdruck nach der Überprüfung der Jahreszahl und des Monats und nach dem Zusammensetzen des Datums nach ISO 8601 mit der IsDate-Funktion auf ein gültiges Datum hin überprüft werden kann. Gibt es den Tag im angegebenen Monat nicht, so gibt die IsDate-Funktion False zurück.
Die folgende Funktion ist in der Ausführung sehr langsam und dient lediglich zur Veranschaulichung, wie die IsDate-/CDate-Funktionen doch zum Einsatz kommen könnten.
 
Public Function IsDateValid_1(ByVal varYear As Variant, _
    ByVal varMonth As Variant, ByVal varDay As Variant, _
    Optional ByRef dtmDate As Date) As Boolean

  Dim strDate As String

  If IsNumeric(varYear) Then
      If (varYear >= 100) And (varYear <= 9999) Then
          If IsNumeric(varMonth) Then
              If (varMonth >= 1) And (varMonth <= 12) Then
                  If IsNumeric(varDay) Then
                      strDate = Format$(varYear, "0000") & "-" & _
                                Format$(varMonth, "00") & "-" & _
                                Format$(varDay, "00")

                      If IsDate(strDate) Then
                          dtmDate = CDate(strDate)
                          IsDateValid_1 = True
                      End If
                  End If
              End If
          End If
      End If
  End If
End Function
 
Datumseingabe mit der DateSerial-Funktion prüfen
Schneller und effektiver lässt sich eine Datumseingabe mit der DateSerial-Funktion prüfen. Die DateSerial-Funktion gibt einen Wert vom Typ Variant (Date) zurück, der die angegebene Jahres-, Monats- und Tageszahl enthält.
Die der Funktion übergebenen Werte werden zunächst in Integer-Werte umgewandelt. Tritt hier kein Fehler auf, wird mit der DateSerial-Funktion ein Datum erzeugt und anschließend mit den VBA-Funktionen Year, Month und Day verglichen, ob die Werte mit den Integer-Werten übereinstimmen. Wenn dem so ist, dann handelt es sich um das Datum, das bei der Eingabe als Datum beabsichtigt war, andernfalls wird davon ausgegangen, dass dem Anwender ein Fehler unterlaufen ist.
 
Public Function IsDateValid_2(ByVal varYear As Variant, _
    ByVal varMonth As Variant, ByVal varDay As Variant, _
    Optional ByRef dtmDate As Date) As Boolean

  Dim intYear   As Integer
  Dim intMonth  As Integer
  Dim intDay    As Integer

  Dim dtmDateSerial   As Date

  On Error Resume Next

  intYear = CInt(varYear)
  intMonth = CInt(varMonth)
  intDay = CInt(varDay)

  If Err.Number = 0 Then
      dtmDateSerial = DateSerial(intYear, intMonth, intDay)
      If Err.Number = 0 Then
          If Year(dtmDateSerial) = intYear Then
              If Month(dtmDateSerial) = intMonth Then
                  If Day(dtmDateSerial) = intDay Then
                      dtmDate = dtmDateSerial
                      IsDateValid_2 = True
                  End If
              End If
          End If
      End If
  End If
  On Error GoTo 0
End Function
 
Beispiel-Aufrufe
 
Dim dtmDate As Date

If IsDateValid_2(txtYear.Text, txtMonth.Text, _
            txtDay.Text, dtmDate) = True Then
    MsgBox Format$(dtmDate, "Long Date")
End If

If IsDateValid_2(2004, 11, 3, dtmDate) = True Then
    MsgBox Format$(dtmDate, "Long Date")
End If
 
Weitere Links zum Thema
ISO 8601 in der Wikipedia - die freie Enzyklopädie
Numeric representation of Dates and Time
Description of Date and Time Format Standards
A Summary of the International Standard Date and Time Notation
Hinweis für VBA-Anwender
Im Download befindet sich auch ein Excel/VBA-Beispiel.

Windows-Version
95
98
ME
NT
2000
XP
Vista
Win 7
VB-Version
VBA 5
VBA 6
VB 4/16
VB 4/32
VB 5
VB 6


Download  (28,1 kB) Downloads bisher: [ 1450 ]

Vorheriger Tipp Zum Seitenanfang Nächster Tipp

Startseite | Projekte | Tutorials | API-Referenz | VB-/VBA-Tipps | Komponenten | Bücherecke | VB/VBA-Forum | VB.Net-Forum | DirectX-Forum | Foren-Archiv | DirectX | VB.Net-Tipps | Chat | Spielplatz | Links | Suchen | Stichwortverzeichnis | Feedback | Impressum

Seite empfehlen Bug-Report
Letzte Aktualisierung: Montag, 16. Mai 2011