Функция IsDate возвращает неожиданные результаты

Предполагая, что value является double, вы можете сделать:

(double)Math.round(value * 100000d) / 100000d

Это для точности 5 цифр. Количество нулей указывает количество десятичных знаков.

28
задан mwolfe02 10 April 2012 в 16:23
поделиться

1 ответ

Недавно я был ошарашен этой маленькой «особенностью» и хотел повысить осведомленность о некоторых проблемах, связанных с функцией IsDate в VB и VBA.

Простой случай

Как и следовало ожидать, IsDate возвращает True при передаче типа данных Date и False для всех других типов данных, кроме строк. Для строк IsDate возвращает True или False в зависимости от содержимого строки:

IsDate(CDate("1/1/1980"))  --> True
IsDate(#12/31/2000#)       --> True
IsDate(12/24)              --> False  '12/24 evaluates to a Double: 0.5'
IsDate("Foo")              --> False
IsDate("12/24")            --> True

IsDateTime?

IsDate должно быть более точно названо IsDateTime потому что он возвращает True для строк, отформатированных как раз:

IsDate("10:55 AM")   --> True
IsDate("23:30")      --> True  'CDate("23:30")   --> 11:30:00 PM'
IsDate("1:30:59")    --> True  'CDate("1:30:59") --> 1:30:59 AM'
IsDate("13:55 AM")   --> True  'CDate("13:55 AM")--> 1:55:00 PM'
IsDate("13:55 PM")   --> True  'CDate("13:55 PM")--> 1:55:00 PM'

Обратите внимание из последних двух примеров выше, что IsDate не является идеальным валидатором раз.

Gotcha!

Мало того, что IsDate принимает время, оно принимает время во многих форматах. Один из которых использует точку (.) в качестве разделителя. Это приводит к некоторой путанице, потому что период может использоваться как разделитель времени, но не как разделитель даты:

IsDate("13.50")     --> True  'CDate("13.50")    --> 1:50:00 PM'
IsDate("12.25")     --> True  'CDate("12.25")    --> 12:25:00 PM'
IsDate("12.25.10")  --> True  'CDate("12.25.10") --> 12:25:10 PM'
IsDate("12.25.2010")--> False '2010 > 59 (number of seconds in a minute - 1)'
IsDate("24.12")     --> False '24 > 23 (number of hours in a day - 1)'
IsDate("0.12")      --> True  'CDate("0.12")     --> 12:12:00 AM

Это может быть проблемой, если вы анализируете строку и работаете с ней, основываясь на ее кажущемся тип. Например:

Function Bar(Var As Variant)
    If IsDate(Var) Then
        Bar = "This is a date"
    ElseIf IsNumeric(Var) Then
        Bar = "This is numeric"
    Else
        Bar = "This is something else"
    End If
End Function

?Bar("12.75")   --> This is numeric
?Bar("12.50")   --> This is a date

Обходные пути

Если вы тестируете вариант для его базового типа данных, вы должны использовать TypeName(Var) = "Date", а не IsDate(Var):

TypeName(#12/25/2010#)  --> Date
TypeName("12/25/2010")  --> String

Function Bar(Var As Variant)
    Select Case TypeName(Var)
    Case "Date"
        Bar = "This is a date type"
    Case "Long", "Double", "Single", "Integer", "Currency", "Decimal", "Byte"
        Bar = "This is a numeric type"
    Case "String"
        Bar = "This is a string type"
    Case "Boolean"
        Bar = "This is a boolean type"
    Case Else
        Bar = "This is some other type"
    End Select
End Function

?Bar("12.25")   --> This is a string type
?Bar(#12/25#)   --> This is a date type
?Bar(12.25)     --> This is a numeric type

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

Function Bar(Var As Variant)
    If IsNumeric(Var) Then
        Bar = "This is numeric"
    ElseIf IsDate(Var) Then
        Bar = "This is a date"
    Else
        Bar = "This is something else"
    End If
End Function

?Bar("12.75")   --> This is numeric
?Bar("12.50")   --> This is numeric
?Bar("12:50")   --> This is a date

Даже если все, что вас волнует, является ли это датой, вы, вероятно, должны убедиться, что это не число:

Function Bar(Var As Variant)
    If IsDate(Var) And Not IsNumeric(Var) Then
        Bar = "This is a date"
    Else
        Bar = "This is something else"
    End If
End Function

?Bar("12:50")   --> This is a date
?Bar("12.50")   --> This is something else

Особенности CDate

Как @Deanna указал в комментарии ниже, поведение CDate() также ненадежно. Его результаты варьируются в зависимости от того, передана ли строка или число:

?CDate(0.5)     -->  12:00:00 PM
?CDate("0.5")   -->  12:05:00 AM

Конечные и начальные нули значимы, если число передается в виде строки:

?CDate(".5")    -->  12:00:00 PM 
?CDate("0.5")   -->  12:05:00 AM 
?CDate("0.50")  -->  12:50:00 AM 
?CDate("0.500") -->  12:00:00 PM 

Поведение также изменяется по мере приближения десятичной части строки к 60-минутной отметке:

?CDate("0.59")  -->  12:59:00 AM 
?CDate("0.60")  -->   2:24:00 PM 

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

64
ответ дан 28 November 2019 в 02:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: