Rubrik: Verschiedenes / Sonstiges | VB-Versionen: VB.NET | 04.04.07 |
Verbalisierte Datumsangaben in Benutzer-Dialogen Ersetzung von Datumsangaben durch relative Begriffe wie "gestern", "in drei Tagen" usw. | ||
Autor: Manfred Bohn | Bewertung: | Views: 11.668 |
ohne Homepage | System: WinNT, Win2k, WinXP, Win7, Win8, Win10, Win11 | Beispielprojekt auf CD |
Problemstellung:
Wenn man in einem Dialog dem Benutzer eine Liste von Datumsangaben zur Bearbeitung vorlegt - z.B. für die Terminplanung oder zur Ereignisüberwachung - ist es oftmals günstiger, die reinen Datumswerte im Format Tag/Monat/Jahr - soweit sie in der Nähe des aktuellen Datums liegen - zu ersetzen oder zu ergänzen durch aussagekräftigere Bezeichnungen wie "heute", "vorgestern" oder "kommenden Dienstag".
Auch bei der Anzeige von Datums-Spalten in Datenbanktabellen kann eine Option zur Darstellung der Einträge in Form von solchen Begriffen nützlich sein.
In Dialogen, bei deren Bearbeitung es vor allem auf die Zeitspanne ankommt, kann man statt dessen Benennungen wie "vor drei Tagen" oder "in 14 Tagen" ausgeben.
Da Visual Basic einen Kalender, den speziellen Datentyp "DateTime" und dafür zahlreiche Datumsfunktionen bereit hält, ist die Programmierung relativ einfach. Die benötigten Methoden sind im Namespace "Microsoft.Visualbasic" enthalten. (Im Code werden die Methoden "voll qualifiziert" aufgerufen).
Zu beachten ist dabei insbesondere, dass Instanzen der Datenstruktur "System.DateTime" IMMER einen "tick"-genauen Zeitpunkt enthalten, also ein Datum UND eine Uhrzeit umfassen. Das gilt auch für die aktuelle Version des "klassischen" VB-Datentyps "Date", der in VB 2005 als ein Ableger von "DateTime" enthalten ist und dessen Instanzen ebenfalls die entsprechenden Methoden bereit stellen (vgl. unten).
Details zu den VB-Methoden:
Den aktuellen Zeitpunkt, dessen Datum den Bezugspunkt bei Bildung der strukturierten Angaben darstellt, liefert die Eigenschaft "System.DateTime.Now".
Sie ruft ein DateTime-Objekt ab, das auf das aktuelle Datum und die aktuelle Zeit auf dem lokalen Rechner als lokale Zeit festgelegt ist (VB-Doku). Allerdings wird die Kind-Eigenschaft dabei nicht auf "DateTimeKind:Local“ gesetzt, sondern bleibt unspezifiziert.
Die Now-Eigenschaft ist nicht zu verwechseln mit der auf Modulebene verfügbaren Now-Funktion (Microsoft.Visualbasic), die das Systemdatum und die Systemzeit abfragt.
Für jedes im Dialog angezeigte Datum ist die Zahl der Tageswechsel zu bestimmen, durch die sie vom Now-Datum abweichen. Liegt kein Tageswechsel vor, ist das Datum durch "Heute" verbal zu etikettieren.
Die Funktion "DateDiff" berechnet das Intervall zwischen zwei Zeitpunkten in einem vorzugebenden Maßstab - in unserem Fall sind das Tage.
"DateDiff" berücksichtigt dabei den genauen Zeitpunkt der übergebenen Parameter. Das bedeutet, zwischen dem 1.1.2007 23:00:00 und dem 2.1.2007 01:00:00 liegen zwei Stunden, also weniger als ein Tag - d.h. die Methode errechnet 0 Tage, obwohl ein Tageswechsel stattgefunden hat.
Als Übergabeparameter muss deshalb die Rückgabe der "DateTime“-Eigenschaft "Date" verwendet werden. Diese Eigenschaft liefert den Zeitwert stets als 00:00:00. Der obige Vergleich gestaltet sich als Differenz der Zeitpunkte 1.1.2007 00:00:00 und 2.1.2007 00:00:00 - und liefert somit korrekt den benötigten einen Tageswechsel.
Bei Bestimmung der Tageswechsel ist zu beachten, ob der übergebene "DateTime"-Wert in lokaler Zeit (einschließlich Sommerzeit) oder in Weltzeit angegeben worden ist (Abfrage der Kind-Eigenschaft). Die Routine „SetToLocalTime“ stellt ggf. die lokale Zeit her – ignoriert aber die unspezifizierte Kind-Eigenschaft!.
Beträgt die Zeitdistanz zwischen einem Dialog-Datum und dem aktuellen Datum mehr als zwei, aber weniger als acht Tage, kann man in Dialogen den zugehörigen Wochentag ausgeben, z.B. "vergangener Freitag" bzw. "kommender Freitag".
Die VB-Funktion "WeekDay" liefert zu einem Datum eine Integer-Kennung für den Wochentag, bei der gemäß Voreinstellung 1 = Sonntag und 7 = Samstag codiert ist.
Man könnte über die VB-Funktion "WeekdayName" die Bezeichnung des Wochentags entsprechend der Windows-Spracheinstellung auslesen. Da im vorliegenden Fall die Wochentage mit eigenen Bezeichnungen kombiniert werden sollen, ist das nicht unbedingt zu empfehlen. Bei der Windows-Einstellung ENGLISCH ergäbe sich dann z.B. "kommender Wednesday".
Der Einfachheit halber wird deshalb eine Funktion verwendet, die die deutschen Wochentags-Bezeichnungen sicher stellt.
Die Übersetzung deutscher Relativzeit-Bezeichnungen in andere Sprachen ist z.T. nicht unbedingt sinnvoll: Z.B. wird der Begriff "vorgestern" ins Englische normalerweise als "the day before yesterday" übersetzt. In dieser Sprache sollte man statt dessen den entsprechenden Wochentag angeben.
Vorschlag:
Die Funktion "VerbalisiertesDatum" wertet die Datumsangabe in einer Instanz der „DateTime“-Struktur aus. Zurückgegeben wird entweder das gegebene Datum als deutscher Standard-Datums-String (Funktion: "NurDatum") oder dessen Ersetzung durch einen aussagekräftigen Begriff .
Der zweite optionale Parameter entscheidet, ob bei einem Datum, das weiter als eine Woche vom gegenwärtigen Datum entfernt liegt, dieses Datum als String (bei: false) oder ein Leerstring (bei: true) zurückgegeben wird. Letzteres ist dann zweckmäßig, wenn das verbale Etikett des Datums nur zusätzlich im Dialog angezeigt werden soll.
Die Funktion "VerbalisiertesDatum_PlusUhrzeit" hängt die Zeitangabe an die Rückgabe des Datums als Standard-Zeitstring (Funktion: "NurUhrzeit") an. Von Bedeutung ist das z.B. bei der Anzeige von Dateilisten. Es kommt häufig vor, dass mehrere Versionen einer Datei am gleichen Tag erstellt worden sind und deshalb erst durch die ergänzende Anzeige der Uhrzeit ('letzte Änderung') vom Benutzer sicher identifiziert werden können (vgl. Funktion „VerbalisierteDateizeit“)
Die Funktion "´VerbalisiertesDatum_Intervall" ersetzt das übergebene Datum durch eine Intervallangabe in Tagen, falls die absolute Differenz zum aktuellen Datum maximal 30 Tageswechsel beträgt.
Solche Angaben sind sinnvoll z.B. bei der Bearbeitung von Bestellungen. Dabei ist es wichtig zu wissen, wie viele Tage seit Auftragseingang bereits vergangen, bzw. wie viele Tage bis zum Erreichen eines vereinbarten Liefertermins noch übrig sind.
Die Routine "Demo_VerbalisiertesDatum" demonstriert Aufruf und Rückgabe dieser Funktionen.
Es versteht sich, dass es kaum Sinn macht, solche Angaben in einer Datei oder Datenbank zu speichern oder sie vom Benutzer im Dialog direkt manipulieren zu lassen.
' ==================================================================== ' Start Quellcode ' ====================================================================
Public Sub Demo_VerbalisiertesDatum() Dim i As Integer, dt As System.DateTime ' Hier angeben, ob bei nicht durchgeführter Verbalisierung ' die Funktionen das Datum zurückgeben oder einen Leerstring Dim NurVerbalisierung As Boolean = False ' Demo-Ausgabe von verbalisierten Datumsangaben ' 10 Tage vor bis 10 Tage nach dem aktuellen Datum Debug.Print("") Debug.Print("Verbalisiertes Datum: ") For i = -10 To 10 ' laufenden Schleifenindex-Wert als Tagesanzahl ' zum aktuellen Datum addieren dt = Now.Add(New System.TimeSpan(i, 0, 0, 0)) ' Ausgabe ins Debug-Fenster Debug.Print(dt.ToString + " ---> " + _ VerbalisiertesDatum(dt, NurVerbalisierung)) Next i Debug.Print("") Debug.Print("Verbalisiertes Datum plus Uhrzeit: ") For i = -10 To 10 ' laufenden Schleifenindex-Wert als Tagesanzahl ' zum aktuellen Datum addieren dt = Now.Add(New System.TimeSpan(i, 0, 0, 0)) ' Ausgabe ins Debug-Fenster Debug.Print(dt.ToString + " ---> " + _ VerbalisiertesDatum_PlusUhrzeit(dt, NurVerbalisierung)) Next i ' Demo-Ausgabe von verbalisierten Datumsangaben (vor x Tagen) ' 32 Tage vor bis 32 Tage nach dem aktuellen Datum Debug.Print("") Debug.Print("Verbalisiertes Datum als Tagesdifferenz: ") For i = -32 To 32 ' laufenden Schleifenindex-Wert als Tagesanzahl ' zum aktuellen Datum addieren dt = Now.Add(New System.TimeSpan(i, 0, 0, 0)) Debug.Print(dt.ToString + " ---> " + _ VerbalisiertesDatum_Intervall(dt, NurVerbalisierung)) Next i End Sub
Public Function VerbalisiertesDatum( _ ByVal dt As System.DateTime, _ Optional ByVal NurVerbalisierung As Boolean = False) As String ' Ein Datum wird umgewandelt in Angaben wie Heute, Morgen oder ' den Wochentag falls es maximal eine Woche vom aktuellen Datum ' entfernt ist ' Bestimmung der Zahl der Tageswechsel zwischen dem aktuellem ' Datum und dem Inhalt des übergebenen Parameters Dim DatumsDifferenz As Long = _ TagesWechsel(System.DateTime.Now, dt) ' ggf. Verbalisierung des Datums: Fallunterscheidungen Select Case DatumsDifferenz Case Is > 7 ' zu weit in der Zukunft If NurVerbalisierung Then Return ("") Return NurDatum(dt) Case Is < -7 ' zu weit in der Vergangenheit If NurVerbalisierung Then Return ("") Return NurDatum(dt) Case 0 Return "Heute" Case -1 Return "Gestern" Case -2 Return "Vorgestern" Case 1 Return "Morgen" Case 2 Return "Übermorgen" Case Is > 2 ' Wochentag (kommend) Return "kommender " + Wochentag(dt) Case Else ' Wochentag (vergangen) Return "vergangener " + Wochentag(dt) End Select End Function
Public Function VerbalisiertesDatum_PlusUhrzeit( _ ByVal dt As System.DateTime, _ Optional ByVal NurVerbalisierung As Boolean = False) As String ' Bei dieser Funktion wird ggf. die Uhrzeit an den ' Rückgabestring angehängt Dim datum As String datum = VerbalisiertesDatum(dt, NurVerbalisierung) If datum <> "" Then datum += (" " + NurUhrzeit(dt)) Return datum End Function
Public Function VerbalisiertesDatum_Intervall( _ ByVal dt As System.DateTime, _ Optional ByVal NurVerbalisierung As Boolean = False) As String ' Ein Datum wird umgewandelt in Angaben zur Tagesdifferenz ' z.b. 'vor drei Tagen' oder 'in 14 Tagen' ' falls es maximal 30 Tage vom aktuellen Datum entfernt ist ' Zahlen bis 12 werden als Zahlworte zurückgegeben Dim Zahlworte As String() = New String(12) _ {"", "einem", "zwei", "drei", "vier", _ "fünf", "sechs", "sieben", "acht", _ "neun", "zehn", "elf", "zwölf"} ' Bestimmung der Zahl der Tageswechsel zwischen dem ' aktuellen Datum und dem Inhalt des übergebenen Parameters Dim DatumsDifferenz As Integer = _ TagesWechsel(System.DateTime.Now, dt) ' Fallunterscheidungen Select Case DatumsDifferenz Case Is > 30 ' zu weit in der Zukunft If NurVerbalisierung Then Return ("") Return NurDatum(dt) Case Is < -30 ' zu weit in der Vergangenheit If NurVerbalisierung Then Return ("") Return NurDatum(dt) Case Is > 12 ' Anzahl der Tage als Zahl zurückgeben Return "in " + DatumsDifferenz.ToString + " Tagen" Case Is < -12 ' Anzahl der Tage als Zahl zurückgeben Return "vor " + (System.Math.Abs(DatumsDifferenz)).ToString + " Tagen" Case 0 Return ("Heute") Case 1 Return ("in einem Tag") Case Is > 1 ' Anzahl der Tage als Zahlwort zurückgeben Return "in " + Zahlworte(DatumsDifferenz) + " Tagen" Case -1 Return ("vor einem Tag") Case Else ' Anzahl der Tage als Zahlwort zurückgeben Return "vor " + Zahlworte(System.Math.Abs(DatumsDifferenz)) + " Tagen" End Select End Function
Public Function TagesWechsel(ByVal dt1 As System.DateTime, _ ByVal dt2 As System.DateTime) As Long ' Zeitdifferenz, die die Zahl der Tageswechsel zwischen den ' beiden Datumsangaben dt1, dt2 berechnet (Basis: lokale Zeit) dt1 = SetToLocalTime(dt1) dt2 = SetToLocalTime(dt2) TagesWechsel = Microsoft.VisualBasic.DateAndTime.DateDiff( _ Microsoft.VisualBasic.DateInterval.Day, dt1.Date, dt2.Date) End Function
Public Function SetToLocalTime(ByVal dt As Date) As Date ' falls im Parameter dt eine UTC-Zeit vorliegt, ' wird in die lokale Zeit umgewandelt ' falls Kind unbestimmt ist, erfolgt keine Änderung If dt.Kind = DateTimeKind.Utc Then dt = dt.ToLocalTime End If Return dt End Function
Public Function Wochentag(ByVal dt As System.DateTime) As String ' Ermittlung des Wochentags zu einem Datum ' ohne Beachtung der Windows-Systemeinstellungen (Sprache) ' für die Bezeichnung der Wochentage Dim Wochentage As String() = New String(7) _ {"", "Sonntag", "Montag", "Dienstag", "Mittwoch", _ "Donnerstag", "Freitag", "Samstag"} ' Der Parameter-Wert 'Sunday' entspricht der Voreinstellung Return (Wochentage(Microsoft.VisualBasic.Weekday( _ dt, Microsoft.VisualBasic.FirstDayOfWeek.Sunday))) End Function
Public Function NurDatum(ByVal dt As System.DateTime) As String ' Das in einer DateTime-Instanz enthaltene Datum ' wird als Standardstring z.b. 24.3.2007 zurückgeben ' Windows-Einstellungen werden ignoriert Return (dt.Day.ToString + "." + dt.Month.ToString + "." _ + dt.Year.ToString) End Function
Public Function NurUhrzeit(ByVal dt As System.DateTime) As String ' Die in einer DateTime-Instanz enthaltene Uhrzeit ' wird als Standardstring 00:00:00 zurückgeben ' Windows-Einstellungen werden ignoriert Return (dt.Hour.ToString + ":" + dt.Minute.ToString + ":" _ + dt.Second.ToString) End Function
Public Function VerbalisierteDateizeit( _ ByVal PfadDatei As String) As String ' Die Funktion gibt das Datum der letzten Änderung einer ' Datei, deren voller Pfad im Parameter "PfadDatei" gegeben ' worden ist, zurück Err.Clear() Try With My.Computer.FileSystem ' Warnhinweis: Die Methode GetFileInfo löst bei ' Zugriffsversuch auf eine fehlende Datei ' keinen Fehler aus - 'FileExists' ist erforderlich If Not .FileExists(PfadDatei) Then Return "Fehler: Datei nicht gefunden" Else Return VerbalisiertesDatum( _ .GetFileInfo(PfadDatei).LastWriteTime) End If End With Catch Return "Fehler: " + Err.Description End Try End Function
' ==================================================================== ' Ende Quellcode ' ====================================================================