Форматы дат Excel VBA

У меня есть таблица, содержащая несколько дат. Они обычно появляются либо в mm/dd/yyyy, либо mm/dd/yyyy hh:mm.

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

Моя первоначальная мысль заключалась в том, чтобы использовать IsDate для проверки или CDate, но это, похоже, не работало: оно все равно возвращало строки вместо дат.

С тех пор я создал небольшой эксперимент, который показывает, что эти функции не работают так, как я их ожидаю. Методология:

  • В ячейке A1 введите формулу =DATE(2013,10,28)
  • Формула ячейки B1 =A1*1, которая должна быть равна числу (41575)
  • Запустите этот маленький script

    Sub test()
    
    MsgBox ("Start:" & TypeName(ActiveCell.Value) & " " & IsDate(ActiveCell.Value))
    
    ActiveCell.Value = Format(ActiveCell.Value, "mm/dd/yyyy")
    MsgBox ("After format: " & TypeName(ActiveCell.Value) & " " & IsDate(ActiveCell.Value))
    
    ActiveCell.Value = CDate(ActiveCell.Value)
    MsgBox ("After Cdate: " & TypeName(ActiveCell.Value) & " " & IsDate(ActiveCell.Value))
    
    End Sub
    

Когда начинается script, ячейка имеет дату типа и IsDate возвращает true. После того, как он выполняется через Format, он имеет строку типа, но IsDate все еще возвращает true. CDate также преобразует ячейку в строку. Ячейка B1 также вернет 0 (так как ее строка * 1).

Итак, я собираюсь суммировать вопросы:

  • Почему Format и CDate меняют мои ячейки на строки?
  • Как я могу гарантировать, что ячейка вернет значение даты, а не только строку, которая выглядит как дата?

Ответ 1

Важно различать содержимое ячеек, их формат отображения, тип данных, считываемых из ячеек с помощью VBA, и тип данных, записанный в ячейки из VBA, и то, как Excel автоматически интерпретирует это. (См. Например, этот предыдущий ответ). Связь между ними может быть немного сложной, поскольку Excel будет делать такие вещи, как интерпретация значений одного типа (например, строки), как некоторые другие типы данных (например, дата), а затем автоматически изменяют формат отображения на основе этого. Ваша самая безопасная ставка делает все явно и не полагается на этот автоматический материал.

Я провел ваш эксперимент, и я не получаю те же результаты, что и вы. Моя ячейка A1 остается Датой все время, а B1 остается 41575. Поэтому я не могу ответить на ваш вопрос №1. Результаты, вероятно, зависят от того, как ваша версия/параметры Excel выбирают автоматическое обнаружение/изменение формата номера ячейки на основе ее содержимого.

Вопрос № 2 "Как я могу гарантировать, что ячейка вернет значение даты": ну, не уверен, что вы подразумеваете под "возвратом" значения даты, но если вы хотите, чтобы оно содержало числовое значение, которое отображается в качестве даты, основываясь на том, что вы пишете на нее из VBA, вы можете либо:

  • Напишите ячейке строковое значение, которое, как вы надеетесь, Excel будет автоматически интерпретировать как дату и формат как таковой. Скрестить пальцы. Очевидно, это не очень здорово. Или,

  • Напишите числовое значение для ячейки из VBA (очевидно, тип Date - это назначенный тип, но могут также делать Integer, Long, Single или Double) и явно задавать формат номеров ячеек для вашего желаемый формат даты с использованием свойства .NumberFormat (или вручную в Excel). Это намного надежнее.

Если вы хотите проверить, что существующее содержимое ячейки может отображаться как дата, то здесь функция, которая поможет:

Function CellContentCanBeInterpretedAsADate(cell As Range) As Boolean
    Dim d As Date
    On Error Resume Next
    d = CDate(cell.Value)
    If Err.Number <> 0 Then
        CellContentCanBeInterpretedAsADate = False
    Else
        CellContentCanBeInterpretedAsADate = True
    End If
    On Error GoTo 0
End Function

Пример использования:

Dim cell As Range
Set cell = Range("A1")

If CellContentCanBeInterpretedAsADate(cell) Then
    cell.NumberFormat = "mm/dd/yyyy hh:mm"
Else
    cell.NumberFormat = "General"
End If

Ответ 2

Format преобразует значения в строки. IsDate по-прежнему возвращает true, потому что он может анализировать эту строку и получать правильную дату.

Если вы не хотите менять ячейки на строку, не используйте Format. (IOW, не конвертируйте их в строки в первую очередь.) Используйте Cell.NumberFormat и установите его в формат даты, который вы хотите отобразить.

ActiveCell.NumberFormat = "mm/dd/yy"   ' Outputs 10/28/13
ActiveCell.NumberFormat = "dd/mm/yyyy" ' Outputs 28/10/2013

Ответ 3

Спасибо за ввод. Я, очевидно, вижу некоторые проблемы, которые не реплицируются на других машинах. Основываясь на ответе Жана, я придумал менее элегантное решение, которое, похоже, работает.

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

Function CellContentCanBeInterpretedAsADate(cell As Range) As Boolean
    Dim d As Date
    On Error Resume Next
    d = CDate(cell.Value)
    If Err.Number <> 0 Then
        CellContentCanBeInterpretedAsADate = False
    Else
        CellContentCanBeInterpretedAsADate = True
    End If
    On Error GoTo 0
End Function

Пример использования:

Dim cell As Range
dim cvalue as double
Set cell = Range("A1")

If CellContentCanBeInterpretedAsADate(cell) Then
    cvalue = cdate(cell.value)
    cell.value = cvalue
    cell.NumberFormat = "mm/dd/yyyy hh:mm"
Else
    cell.NumberFormat = "General"
End If

Ответ 4

Используйте value(cellref) для оценки ячеек. Строки выдают ошибку "#Value", но даты решаются на число (например, 43173).