2009-06-29 8 views
3

Wir haben eine VB6-Anwendung (in einer COM-Komponente), die CDate() verwendet, um eine Zeichenfolge zu einem Datum für das Speichern in einer Datenbank zu übergeben.Ein besseres CDate für VB6

Je nachdem, ob wir die Anwendung wollen in dd/MM/JJ oder MM/TT/JJ zum Beispiel zu sprechen, müssen wir die regionalen Einstellungen der Identität Benutzer für die COM-Anwendung ändern. (Im Augenblick ist die einzige Möglichkeit, die wir haben, ist a nasty hack.)

Wir haben ein Datumsformat-Zeichenfolge, die für die Formatierung alle Ausgabedaten verwendet wird, und es wird angenommen, dass das Datum

Wäre dies .NET würden wir verwenden DateTime.ParseExact und weg lachen. Zu diesem Zweck können Sie ein in .NET geschriebenes COM-Objekt aufrufen. Gibt es eine andere oder bessere Option, die etwas schwarze Magie um den Befehl Format enthält, oder eine lange wiederverwendbare Funktion, die das Datum in Abhängigkeit von der Formatzeichenfolge usw. in Token zerlegt?

+4

Ich bin sicher, dass, wenn ich diese Frage schrieb, der dritte Satz ein Ende hatte. – crb

Antwort

3

, obwohl es das Trennzeichen wie „/“ und Fenster YY Jahre bei 50 Hardcodes:

Private Function ParseDate(ByVal DateString As String, _ 
          ByVal DatePattern As String) As Date 
    'DateString: i/j/k formatting. 
    'DatePattern: i/j/k formatting, each to be: 
    '    M or MM for month position. 
    '    D or DD for day position. 
    '    YY or YYYY for year position, if YY 
    '     then century windowed at 50. 
    Dim strStringParts() As String 
    Dim strPatternParts() As String 
    Dim intPart As Integer, intScore As Integer 
    Dim intMonth As Integer, intDay As Integer, intYear As Integer 
    Const DELIM As String = "/" 
    Const YYWINDOW As Integer = 50 

    strStringParts = Split(DateString, DELIM) 
    strPatternParts = Split(UCase$(DatePattern), DELIM) 
    For intPart = 0 To UBound(strStringParts) 
     If intPart > UBound(strPatternParts) Then 
      Err.Raise 5, "ParseDate" 
     End If 
     Select Case strPatternParts(intPart) 
      Case "M", "MM" 
       intMonth = CInt(strStringParts(intPart)) 
       intScore = intScore Or &H1 
      Case "D", "DD" 
       intDay = CInt(strStringParts(intPart)) 
       intScore = intScore Or &H2 
      Case "YY" 
       intYear = CInt(strStringParts(intPart)) 
       If 0 > intYear Or intYear > 99 Then 
        Err.Raise 5, "ParseDate" 
       End If 
       intYear = intYear + IIf(intYear < YYWINDOW, 2000, 1900) 
       intScore = intScore Or &H4 
      Case "YYYY" 
       intYear = CInt(strStringParts(intPart)) 
       If 100 > intYear Or intYear > 9999 Then 
        Err.Raise 5, "ParseDate" 
       End If 
       intScore = intScore Or &H4 
      Case Else 
       Err.Raise 5, "ParseDate" 
     End Select 
    Next 
    If intScore = &H7 Then 
     ParseDate = DateSerial(intYear, intMonth, intDay) 
    Else 
     Err.Raise 5, "ParseDate" 
    End If 
End Function 

Validierung nicht perfekt sein, aber es sollte nahe sein. Es wirft "ungültigen Prozeduraufruf oder Argument (Fehler 5)" auf fehlerhafte Eingaben.

+1

+1. Ich habe es nur gelesen, nicht getestet, aber es sieht perfekt aus. Möglicherweise benötigt StackOverflow eine Möglichkeit, Komponententests zu veröffentlichen. – MarkJ

+1

@MarkJ: Ha. Ich habe manchmal dasselbe gedacht. Wer möchte mit dem Schreiben von SOUnit beginnen? ;) –

+1

Ich nehme an, dass die Datumszeichenketten auch für numerische Tests getestet werden könnten. Vielleicht wären andere Validierungen auch wichtig, wenn die Eingabe in Echtzeit von Benutzern gehandhabt wird. Erstaunlich, wie das Hinzufügen von Fehlerüberprüfungs-Ballons einen beliebigen Code enthält. – Bob77

0

Sie sollten mit einer Datumsvariablen (keine formatierten String) und dann zeigen formatierte Wert in der Form unter Verwendung der Benutzer regionalen Einstellungen oder nehmen Sie die Eingabe vom Benutzer zu einer Datumsvariablen sprechen.

+0

Wir nehmen die Eingabe vom Benutzer über das Internet. – crb

1

Ich kenne keine einfache Lösung. Sie könnten die Eingabezeichenfolge in Teilketten durch den Begrenzer Split und dann DateSerial verwenden, um die Jahres-, Monats- und Stundenzahl in einem native VB6 Date Variable rekombinieren. So etwas ist hier unten. Wenn Sie viele Gebietsschemas unterstützen müssen, könnte dies kompliziert werden (siehe Bob's answer). Wohlgemerkt, das würde auch DateTime.ParseExact verwenden.

sInput = "1/3/71" 
Dim splt() As String 
splt = Split(sInput, "/") 
dte = DateSerial(splt(2) + 1900, splt(1), splt(0)) ' dd/mm/yy' 
1

Sie können die integrierte Funktion Format verwenden, um dies für Sie zu tun.

Hier ist ein einfacher Test, dies zu bestätigen:

Public Sub TestDateParsing() 

    'On my computer, the date format is U.S. (mm/dd/yyyy)' 
    'This test creates a date string in dd/mm/yyyy format to' 
    'simulate user input in a different format' 

    Const TEST_DATE As Date = #6/1/2009# 

    Dim inputDate As String 
    inputDate = Format(TEST_DATE, "dd/mm/yyyy") 
    'inputDate is "1/6/2009" (June 1 in dd/mm/yyyy format)' 

    Debug.Print Format(inputDate, "dd/mm/yyyy") 
    'It`s magic! The above line will print 6/1/2009' 
    'which is the correct format for my Regional Settings' 

End Sub 

Es mag wie Magie erscheinen, aber es ist nicht. Es nutzt die Funktion Format in Verbindung mit den aktuellen regionalen Einstellungen.

Beispiel: Angenommen, Ihre Ländereinstellungen das "mm/dd/yyyy" Format für Daten konfiguriert sind.

Jetzt erhalten Sie ein Datum Zeichenfolge von einem Benutzer in "dd/mm/yyyy" Format. Wenn Sie Format diese Zeichenfolge Datum und Format sagen auch "dd/mm/yyy" zu verwenden, wird es den Monat und den Tag Teile des Datums wechseln, weil Sie Ihre Einstellungen sagen Daten in "mm/dd/yyyy" Format.

Mit anderen Worten nehmen Format immer die Datumszeichenfolge vom Benutzer formatiert nach Ihren aktuellen Regional-Einstellungen (in diesem Fall "mm/dd/yyyy"), so, wenn Sie ihm sagen, das Datum "dd/mm/yyyy" mit zu formatieren, wird es zwingen, tausche die Teile von Monat und Tag. Wenn Ihre Ländereinstellungen dasselbe Format wie das vom Benutzer angegebene Datum verwenden, funktioniert dieser Code weiterhin: Format gibt das Benutzerdatum einfach unverändert zurück. Verwirrt noch? ;)

Das Gleiche passiert, wenn Ihre Ländereinstellungen auf "dd/mm/yyyy" eingestellt sind und der Benutzer ein Datum im Format "mm/dd/yyyy" sendet.

Der Haken ist, dass Sie vor der Zeit zu wissen, welches Format der Benutzer Daten in senden.Sie können nicht mit dem Mischen und dem Anpassen von Datumsformaten beginnen (und sie sollten es auch nicht sein).


EDIT (von MarkJ) - nur um zu beweisen, dass Mikes Code einen String in Datum umwandeln kann. Mike, bitte rolle zurück oder ändere diese Änderung, wenn du willst.

Public Sub Test() 
    Dim dte As Date 
    For dte = #1/1/2009# To #12/31/2009# 
    Call TestDateParsing(dte) 
    Next dte 
End Sub 

Public Sub TestDateParsing(ByVal dteIn As Date) 

    'On my computer, the date format is U.S. (mm/dd/yyyy)' 
    'This test creates a date string in dd/mm/yyyy format to' 
    'simulate user input in a different format' 

    Dim sExpected As String 
    sExpected = Day(dteIn) & "/" & Month(dteIn) & "/" & Year(dteIn) 
    Dim inputDate As String 
    Dim dte As Date 
    inputDate = Format(dteIn, "dd/mm/yyyy") 
    dte = Format(inputDate, "dd/mm/yyyy") 

    Debug.Assert sExpected = Day(dte) & "/" & Month(dte) & "/" & Year(dte) 
    Debug.Print sExpected 

End Sub 
+0

Sie haben Recht, das macht überhaupt keinen Sinn. :) Ich habe das Datum in einer Zeichenfolge und ich habe die Formatzeichenfolge in einer Zeichenfolge, und ich möchte ein DateTime-Objekt abrufen. Außerdem sieht Ihr Code VB.NET mehr als VB6 aus? – crb

+0

Ja, es ist schwer zu erklären;). Es funktioniert, aber es funktioniert nur für Ihren speziellen Anwendungsfall. Eine allgemeinere Lösung würde leider bedeuten, Ihre eigene DateParseExact-Funktion in VB6 zu rollen. Es gibt kein integriertes Äquivalent von DateParseExact in VB6. –

+0

Ich werde versuchen, besser zu erklären: wenn der empfangende Computer verwendet "mm/TT/JJ" -Format, und Sie erhalten eine Zeichenfolge in "TT/MM/JJ", dann Aufruf Format mit "TT/MM/JJ" für die Der zweite Parameter bewirkt, dass Format das Format "TT/MM/JJ" im Format "MM/TT/JJ" formatiert, da Format davon ausgeht, dass die Zeichenfolge "TT/MM/JJ" bereits im Format "MM/TT/JJ" vorliegt (Es wird mit den aktuellen Ländereinstellungen fortgesetzt. Um es in "TT/MM/JJ" umzurechnen, tauscht Format die MM- und TT-Komponenten des Datums aus, wodurch Sie ein "MM/TT/JJJ" -Format erhalten. Ja, ich bin mir nicht sicher, ob ich es besser erklären kann;) –

3

Schau, es gibt keine einfache Möglichkeit, das zu sagen - du bist geschraubt. Wenn Sie Freiformeingabe aus dem Internet akzeptieren, müssen Sie mit der Realität leben, dass Menschen auf der ganzen Welt Daten unterschiedlich formatieren. Deshalb verwenden so viele Websites Popup-Kalender und so, um Benutzereingaben zu erhalten. Keine Zweideutigkeit dort. Ganz gleich, was Sie denken, die Bibliotheksroutinen von .NET können Ihre Absichten nicht besser verstehen als jede andere Bibliothek.

Fwiw, der Code Mike geschrieben ist absolut VB6. Ich bin mir nicht sicher, wie es aussieht wie VB.NET? Sobald Sie ein Datum/eine Uhrzeit in eine Date-Variable eingegeben haben, können Sie diese mit Format() anzeigen lassen. Das ist der einfache Teil.

Ich würde Ihnen dringend empfehlen, entweder A) einen Weg zu finden, Ihre Eingaben eindeutig zu erfassen, oder B) Ihren Benutzern mitzuteilen, welches Format Sie erwarten und mit dem, was sie eingeben, leben. Alles in allem ist es möglich, dass ich die Frage missverstanden habe, und Sie wissen wirklich, in welchem ​​Format der Benutzer die Daten bereitstellt? (Wenn ja, habe ich wirklich Probleme zu verstehen, was das Problem in ClassicVB zu interpretieren ist - Entschuldigung.)

1

Sie erwähnen in den Kommentaren zu einer der anderen Antworten, dass Sie die Eingabe aus dem Web nehmen.

In diesem Fall können Sie das zu übertragende Datumsformat steuern, indem Sie die Benutzereingabe auf <, wählen Sie > Drop-Listen. Machen Sie das Monat-Feld Liste der kurzen oder langen Monatsnamen Jan/Januar Feb/Februar etc. und dann eine Datumszeichenkette im Format "1 Jan 2009"

Dann ist es egal, was Ihre Gebietsschema Einstellungen sind, Sie ' Ich werde das Datum erhalten, das der Benutzer beabsichtigt hat. Dies sollte in der Nähe seiner

1

Ein weiterer Weg zu gehen:

Public Enum abDateType 
    abMDY 
    abDMY 
End Enum 

Public Function MakeDate(ByVal dateString As String, ByVal dateType As abDateType, Optional delimiter As String = "/") As Date 
    Dim strVals() As String 
    Dim dtRtnVal As Date 
    strVals = Split(dateString, delimiter) 
    Select Case dateType 
    Case abMDY 
     dtRtnVal = DateSerial(strVals(2), strVals(0), strVals(1)) 
    Case abDMY 
     dtRtnVal = DateSerial(strVals(2), strVals(1), strVals(0)) 
    Case Else 
     Err.Raise vbObjectError, , "Unexpected date format." 
    End Select 
    MakeDate = dtRtnVal 
End Function 
2

DateAdd kann eine Vielzahl von Ein- und Ausgängen im richtigen Format.

ThisLine = "Tuesday, September 04, 2012 2:02 PM" 

i = InStr(ThisLine, ",") ' get rid of the leading day 

If i > 0 Then 
    TempResult = Trim(Right$(ThisLine, Len(ThisLine) - i)) 
end if 

TempResult = DateAdd("s", 0, TempResult)