Excel VBA 日期格式

Posted

技术标签:

【中文标题】Excel VBA 日期格式【英文标题】:Excel VBA date formats 【发布时间】:2013-11-17 01:42:38 【问题描述】:

我有一个包含许多日期的电子表格。这些通常出现在mm/dd/yyyymm/dd/yyyy hh:mm 中。

问题是日期并不总是正确输入,我想检查以确保它们是代码中的日期。

我最初的想法是使用 IsDate 来检查或使用 CDate 但这似乎不起作用:它仍然返回字符串而不是日期。

我已经建立了一个小实验,表明这些功能不能按我期望的方式工作。方法是:

    在单元格 A1 中,我输入公式 =DATE(2013,10,28) 单元格 B1 公式 =A1*1 应该等于一个数字 (41575)

    运行这个小脚本

    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
    

当脚本启动时,单元格是日期类型,IsDate 返回 true。通过Format 运行后,它是字符串类型,但IsDate 仍然返回true。 CDate 还将单元格转换为字符串。单元格 B1 现在也将返回 0(因为它是一个字符串 *1)。

所以我想总结一下问题:

    为什么FormatCDate 将我的单元格更改为字符串? 如何确保单元格返回日期值,而不仅仅是看起来像日期的字符串?

【问题讨论】:

【参考方案1】:

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

【讨论】:

嗯,不,CDate converts to Date type,不是字符串。所以使用CDate其实是个好主意。它不会将单元格的内容更改为字符串。 @Jean-FrançoisCorbett:感谢您的更正。自从我做任何 VBA 以来已经有一段时间了,我不记得使用 CDate 来做任何事情。我已经编辑更正了。【参考方案2】:

区分单元格的内容、它们的显示格式、VBA从单元格中读取的数据类型以及从 VBA 写入单元格的数据类型以及 Excel 如何自动解释这一点。 (参见例如this previous answer。)它们之间的关系可能有点复杂,因为 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

【讨论】:

次要评论 - 在代码中尽快恢复错误处理是一种很好的做法,因此我会将“On Error GoTo 0”移高一行(在 if 语句之前)跨度> @Juliusz:不,那行不通。 On Error GotoErr 对象重置为零,因此您将无法判断是否发生错误,并且该函数将始终返回 True。如需更多信息,请查看this question,如果您有任何绝妙的想法,请告诉我! @JFC 你完全正确!刚刚检查过。在这种情况下,您仍然会更好地分配错误编号并打开错误处理。在这个例子中,它不会有太大的不同,但如果使用更复杂的“if”语句,情况就会不同。 答案看起来不错,不幸的是代码在您的计算机上没有做同样的事情。使用您的代码,即使可以将单元格解释为日期,它仍然不会将单元格转换为数字,不知道为什么。它将格式更改为“mm/dd/yyyy hh:mm”,但它仍然只显示单元格中的字符串,并且没有数值。【参考方案3】:

感谢您的意见。我显然看到了一些没有在其他机器上复制的问题。根据 Jean 的回答,我想出了似乎可行的不太优雅的解决方案。

因为如果我直接从 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

【讨论】:

哇,这太奇怪了。我根本不需要做这个switcheroo。 +1 找出一个好的解决方法!【参考方案4】:

使用侧面的value(cellref) 来评估单元格。字符串会产生“#Value”错误,但日期会解析为数字(例如43173)。

【讨论】:

【参考方案5】:

为确保单元格将返回日期值而不仅仅是看起来像日期的字符串,首先必须将 NumberFormat 属性设置为日期格式,然后将真实日期放入单元格的内容中。

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

【讨论】:

以上是关于Excel VBA 日期格式的主要内容,如果未能解决你的问题,请参考以下文章

通过vba在excel中的“dd/mm/yyyy”日期格式

如何使用 VBA 在 Excel 注释中查找和替换日期格式

VBA批量修改excel格式

excel用vba宏处理一些日期时间

通过VBA 程序找出excel表中B列的最大日期,并且排除一个不要的年份的日期,如12/31/2099(日期格式),急

vba把一个带时间的日期转为不带时间的日期