Лучший CDate для VB6 - PullRequest
       19

Лучший CDate для VB6

4 голосов
/ 29 июня 2009

У нас есть приложение VB6 (в компоненте COM), которое использует CDate (), чтобы взять строку и привести ее к дате для хранения в базе данных.

В зависимости от того, хотим ли мы, чтобы приложение говорило, например, в дд / мм / гг или мм / дд / гг , мы должны изменить региональные настройки удостоверения пользователя для приложения COM. (Прямо сейчас единственный вариант, который у нас есть, это мерзкий взлом .)

У нас есть строка формата даты, которая используется для форматирования всех выходных дат, и предполагается, что дата

Если бы это был .NET, мы бы использовали DateTime.ParseExact и смеялись бы далеко. Вызов COM-объекта, написанного на .NET для этой единственной цели, является опцией. Есть ли другой или лучший вариант, включающий некоторую чёрную магию вокруг команды Формат или длинную многократно используемую функцию, которая токенизирует дату в зависимости от строки формата и т. Д.?

Ответы [ 8 ]

3 голосов
/ 30 июня 2009

Это должно быть близко, хотя он жестко кодирует разделитель как "/" и Windows YY years при 50:

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

Валидация может быть не идеальной, но она должна быть близкой. Он выдает «Недопустимый вызов процедуры или аргумент (Ошибка 5)» для неверных входных данных.

3 голосов
/ 30 июня 2009

Слушай, нет простого способа сказать это - ты облажался. Если вы принимаете информацию в произвольной форме из Интернета, вы должны жить с реальностью того, что люди во всем мире форматируют даты по-разному. Вот почему так много веб-сайтов используют всплывающие календари и тому подобное, чтобы получить пользовательский ввод. Никакой двусмысленности там нет. Независимо от того, что вы думаете, подпрограммы библиотеки .NET не могут понять намерения вашего пользователя лучше, чем любая другая библиотека.

Fwiw, код, который выложил Майк, абсолютно VB6. Я не уверен, что это похоже на VB.NET? Получив дату / время в переменную Date, вы можете отобразить ее так, как вам нужно, с помощью Format (). Это легкая часть.

Я бы настоятельно рекомендовал вам либо А) найти способ однозначно собрать ваши входные данные, либо Б) сообщить своим пользователям, какой формат вы ожидаете, и жить с тем, что они вводят. Все это говорит, возможно ли, что я неправильно понял вопрос, и вы действительно знаете, в каком формате пользователь предоставляет данные? (Потому что, если это так, у меня действительно возникают проблемы с пониманием, в чем проблема, интерпретируя его в ClassicVB - извините.)

2 голосов
/ 28 сентября 2012

DateAdd принимает широкий спектр входов и выходов в правильном формате.

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)
1 голос
/ 30 июня 2009

Другой путь:

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
1 голос
/ 30 июня 2009

Вы упоминаете в комментариях к одному из других ответов, которые вы принимаете от сети.

В этом случае вы можете управлять форматом отправляемой даты, ограничивая ввод данных пользователем

Тогда не имеет значения, какие у вас настройки локали, вы получите дату, на которую рассчитывал пользователь.

1 голос
/ 30 июня 2009

Вы можете использовать встроенную функцию Format, чтобы сделать это для вас.

Вот простой тест для подтверждения этого:

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

Это может показаться магией, но это не так. Он использует преимущества функции Format в сочетании с текущими региональными настройками.

Например, предположим, что ваши региональные настройки настроены на использование формата "mm/dd/yyyy" для дат.

Теперь вы получаете строку даты от пользователя в формате "dd/mm/yyyy". Если вы Format эту строку даты и скажете Format также использовать "dd/mm/yyy", она поменяет местами часть даты месяца и дня, потому что ваши настройки говорят, что даты в формате "mm/dd/yyyy".

Другими словами, Format всегда предполагает, что строка даты от пользователя отформатирована в соответствии с вашими текущими региональными настройками (в данном случае "mm/dd/yyyy"), поэтому, когда вы говорите ей форматировать дату с помощью "dd/mm/yyyy", это заставит его поменять местами месяц и день. Если ваши региональные настройки используют тот же формат, что и предоставленная пользователем дата, этот код все равно будет работать: Format просто вернет дату пользователя без изменений. Смущены еще? ;)

То же самое произойдет, если ваши региональные настройки установлены на "dd/mm/yyyy", и пользователь отправляет дату в формате "mm/dd/yyyy".

Суть в том, что вам нужно заранее знать, в каком формате пользователь отправляет даты. Они не могут начинать смешивать и сопоставлять форматы дат (и они не должны быть в любом случае).


EDIT ( от MarkJ ) - просто чтобы доказать, что код Майка может преобразовать строку в Date. Майк, пожалуйста, откатись или измени эту правку, если хочешь.

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
1 голос
/ 30 июня 2009

Я не знаю простого решения. Вы можете Разделить входную строку на подстроки с помощью разделителя, а затем использовать DateSerial , чтобы объединить числа года, месяца и часа в собственный VB6 Дата переменная. Примерно так ниже. Если вам нужно поддерживать много локалей, это может быть сложно (см. ответ Боба ). Имейте в виду, так же будет использовать DateTime.ParseExact.

sInput = "1/3/71"
Dim splt() As String
splt = Split(sInput, "/")
dte = DateSerial(splt(2) + 1900, splt(1), splt(0))  ' dd/mm/yy'
0 голосов
/ 29 июня 2009

Вы должны поговорить с переменной даты (не отформатированной строкой), а затем отобразить форматированное значение в форме, используя региональные настройки пользователя, или перенести ввод от пользователя в переменную даты.

...