13

I've got a spreadsheet that contains a number of dates. These generally appear in either mm/dd/yyyy or mm/dd/yyyy hh:mm.

The problem is that the dates aren't always put in correctly and I want to have checks to make sure they are dates in the code.

My original thought was to use IsDate to check or CDate but this didn't seem to work: it was still returning strings instead of dates.

I've since set up a small experiment which shows that these functions don't work the way I expect them to. Methodology is:

  1. In a cell A1 I enter the formula =DATE(2013,10,28)
  2. Cell B1 formula =A1*1 which should equal a number (41575)
  3. Run this little 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
    

When the script starts the cell is a of type date and IsDate returns true. After it is run through Format it is of type string but IsDate still returns true. CDate will also convert the cell to a string. Cell B1 will also now return 0 (since its a string*1).

So I guess to summarize the questions:

  1. Why are Format and CDate changing my cells to strings?
  2. How can I ensure that a cell will return a date value and not just a string that looks like a date?
Jean-François Corbett
  • 37,420
  • 30
  • 139
  • 188
gtwebb
  • 2,981
  • 3
  • 13
  • 22

5 Answers5

17

It's important to distinguish between the content of cells, their display format, the data type read from cells by VBA, and the data type written to cells from VBA and how Excel automatically interprets this. (See e.g. this previous answer.) The relationship between these can be a bit complicated, because Excel will do things like interpret values of one type (e.g. string) as being a certain other data type (e.g. date) and then automatically change the display format based on this. Your safest bet it do everything explicitly and not to rely on this automatic stuff.

I ran your experiment and I don't get the same results as you do. My cell A1 stays a Date the whole time, and B1 stays 41575. So I can't answer your question #1. Results probably depend on how your Excel version/settings choose to automatically detect/change a cell's number format based on its content.

Question #2, "How can I ensure that a cell will return a date value": well, not sure what you mean by "return" a date value, but if you want it to contain a numerical value that is displayed as a date, based on what you write to it from VBA, then you can either:

  • Write to the cell a string value that you hope Excel will automatically interpret as a date and format as such. Cross fingers. Obviously this is not very robust. Or,

  • Write a numerical value to the cell from VBA (obviously a Date type is the intended type, but an Integer, Long, Single, or Double could do as well) and explicitly set the cells' number format to your desired date format using the .NumberFormat property (or manually in Excel). This is much more robust.

If you want to check that existing cell contents can be displayed as a date, then here's a function that will help:

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

Example usage:

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
Community
  • 1
  • 1
Jean-François Corbett
  • 37,420
  • 30
  • 139
  • 188
  • Minor comment - it is a good practice that you revert an error handling as soon as possible in your code, so I would move "On Error GoTo 0" one line higher (before if statement) – Juliusz Nov 06 '13 at 11:47
  • 1
    @Juliusz: No, that wouldn't work. `On Error Goto` resets the `Err` object to zero, so you wouldn't be able to tell whether an error occurred, and the function would always return True. For more, see [this question](http://stackoverflow.com/questions/19681030/pattern-to-handle-expected-errors-locally-rethrow-unexpected-errors) and let me know if you have any brilliant ideas! – Jean-François Corbett Nov 06 '13 at 11:54
  • @JFC You are absolutely right! Just checked that. In that case you still will be better off with assigning the error number and switch the error handling on. In this example it would not make much difference, but it would be different story with the more complex "if" statement. – Juliusz Nov 06 '13 at 13:03
  • The answer looks good, it unfortunate that the code isn't doing the same thing on your computer. With your code it still won't convert a cell to a number even if it can be interpreted as a date, not sure why. It changes the format to "mm/dd/yyyy hh:mm" but it still only shows the string in the cell and it doesn't have a numeric value. – gtwebb Nov 06 '13 at 16:49
2

Format converts the values to strings. IsDate still returns true because it can parse that string and get a valid date.

If you don't want to change the cells to string, don't use Format. (IOW, don't convert them to strings in the first place.) Use the Cell.NumberFormat, and set it to the date format you want displayed.

ActiveCell.NumberFormat = "mm/dd/yy"   ' Outputs 10/28/13
ActiveCell.NumberFormat = "dd/mm/yyyy" ' Outputs 28/10/2013
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • 1
    Well, no, [`CDate` converts to Date type](http://msdn.microsoft.com/en-us/library/2dt118h2%28v=vs.84%29.aspx), not String. So using `CDate` is actually a good idea. It won't change the content of cells to strings. – Jean-François Corbett Nov 06 '13 at 09:21
  • @Jean-FrançoisCorbett: Thanks for the correction. It's been a while since I did any VBA, and I don't recall using `CDate` for any of it. I've edited to correct. – Ken White Nov 06 '13 at 12:00
1

Thanks for the input. I'm obviously seeing some issues that aren't being replicated on others machines. Based on Jean's answer I have come up with less elegant solution that seems to work.

Since if I pass the cell a value directly from cdate, or just format it as a number it leaves the cell value as a string I've had to pass the date value into a numerical variable before passing that number back to the cell.

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

Example usage:

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
gtwebb
  • 2,981
  • 3
  • 13
  • 22
0

Use value(cellref) on the side to evaluate the cells. Strings will produce the "#Value" error, but dates resolve to a number (e.g. 43173).

ryanyuyu
  • 6,366
  • 10
  • 48
  • 53
Craig
  • 1
0

To ensure that a cell will return a date value and not just a string that looks like a date, first you must set the NumberFormat property to a Date format, then put a real date into the cell's content.

Sub test_date_or_String()
 Set c = ActiveCell
 c.NumberFormat = "@"
 c.Value = CDate("03/04/2014")
   Debug.Print c.Value & " is a " & TypeName(c.Value) 'C is a String
 c.NumberFormat = "m/d/yyyy"
   Debug.Print c.Value & " is a " & TypeName(c.Value) 'C is still a String
 c.Value = CDate("03/04/2014")
   Debug.Print c.Value & " is a " & TypeName(c.Value) 'C is a date    
End Sub
Docmarti
  • 376
  • 2
  • 6