【问题标题】:Range.Find not making a difference between January and November (February and December) in VBA ExcelRange.Find 在 VBA Excel 中的 1 月和 11 月(2 月和 12 月)之间没有区别
【发布时间】:2018-08-02 14:03:27
【问题描述】:

假设我有以下琐碎的任务:

  • 连续写下从 2016 年 1 月到 2018 年 6 月的月份中的第一个日期
  • 找到 01-January-2016 并将其涂成红色
  • 使用Range.Find()

因此,我创建了一个代码,从 1 循环到 30 并写入每个月的第一个日期。然后我使用Rows(1).Find(CDate("01.01.2016"))Rows(1).Find(DateSerial(2016,1,1)),我认为我的任务几乎准备好了。

我运行代码,我在 Excel 2010 和 Excel 2016 中都看到了这一点:

问题: 这背后有什么原因吗?或者Range.Find() 函数是否记录为这样?

Public Sub TestMe()

    Cells.Clear

    Dim cnt             As Long
    For cnt = 1 To 30
        Cells(1, cnt) = DateAdd("M", cnt - 1, DateSerial(2016, 1, 1))
        Cells(1, cnt).NumberFormat = "MMM-YY"
    Next cnt

    Dim foundRange      As Range
    Set foundRange = Rows(1).Find(CDate("01.01.2016"))
    'Set foundRange = Rows(1).Find(DateSerial(2016, 1, 1))  'the same false result
    'Set foundRange = Rows(1).Find("01.01.2016")             'does not find anything
    If Not foundRange Is Nothing Then
        foundRange.Interior.Color = vbRed
    End If

End Sub

一般来说,Range.Find() 有一个可选的After 参数,它是Range 的第一个单元格。在我们的例子中,After 参数被省略,因此它被认为是A1 并且最后被检查。如果您在第一次循环后停止代码并从 Excel 中手动删除 Nov 16,然后继续执行代码,它将以红色返回单元格 Jan 16

只要 11 月被认为已找到,它就会将其归还,并且不会更进一步。问题更像是 - 2016 年 11 月 1 日2016 年 1 月 1 日 相同,甚至部分相同的逻辑是什么?

【问题讨论】:

  • Set foundRange = Rows(1).Find(DateSerial(2016, 1, 1), [A1], , xlWhole)
  • 这是一个完整的猜测。美国日期格式1/1/2016 可以在11/1/2016 中找到。
  • @FlorentB。 - LookAt:=xlWhole 与搜索开始无关。尝试写Set foundRange = Rows(1).Find(DateSerial(2016, 1, 1), after:=Range("AD1")),它会给出预期的结果。
  • @CallumDA - 这很有意义,但是然后1/1/2016 也可以在21/1/2016 中找到。如果您只更改第一个循环中的代码,如Cells(1, cnt) = DateAdd("M", cnt - 1, DateSerial(2016, 1, 21)),那么它应该会找到一些东西。它什么也没找到。
  • @Vityata 再次猜测,请记住,在 vba 中,日期将以 EN-US 格式查看。我相信您在查看 2016 年 1 月 11 日时也会返回 2016 年 11 月 11 日。如在 EN-US 中,分别为 11/11/20161/11/2016

标签: vba excel date datetime


【解决方案1】:

每当.Find(LookAt:=xlPart) 用于一系列日期时,它会使用不带.Value2 的日期,但它会按照美国日期格式-MM/DD/YY 默默地将它们转换为String 并查看此字符串。 Excel中日期的显示格式完全不相关,只要将单元格格式化为日期即可。

因此,可以在 11 月找到 1 月的每一天,在 12 月可以找到 2 月的每一天作为子字符串,从而在一个日历年内可能出现 58 个(或闰年为 59 个)不同的错误:


为了避免这个错误,最好的解决方案是显式查看xlWhole。如果没有被引用,Range.Find() 会查找部分字符串。

另一个问题是Range.Find 的起点。根据The Documentation,它会启动AFTER提供的或默认的单元格,并且只会在循环返回后查看起始单元格。

您希望在其后开始搜索的单元格。这对应于从用户界面进行搜索时活动单元格的位置。请注意,After 必须是范围内的单个单元格。 请记住,搜索在此单元格之后开始;在方法返回到该单元格之前,不会搜索指定的单元格。如果不指定此参数,则搜索从范围左上角的单元格之后开始。

因此,通过不定义 XlWhole 和起始单元格,搜索的第一个单元格是 B1 而不是 A1,它会在循环查找正确日期之前部分找到日期。

所以将起始单元格设置在范围的末尾就可以了:

Set foundRange = Rows(1).Find(DateSerial(2016, 1, 1), [XFD1])

【讨论】:

  • 感谢您的回答和 cmets。 After 是众所周知的事情。我已经在问题的代码下方写了它,试图准确地解释你的回答。还写了一篇关于它的文章:vitoshacademy.com/…。我在找答案,说1/11/2016是在我们找1/1/2016的时候找到的,因为1/1/2016部分在11/1/2016里面,因为excel在找的时候把它当成字符串。
  • 这不是我在引用后的段落中所说的。如果你愿意,我可以多加一点。我个人会使用 match。
  • 在您的回答中,重要的是“它找到了部分日期”。正是这种偏分的方式,这很有趣,因此我正在考虑一个具有所有可能性的答案(它们在 1 年内并不多),例如- 1/11/201611/11/2016 中。 2/11/201612/11/2016 中。 2/12/201612/12/2016 中。其余的(AfterxlWhole)与“部分发现”并不真正相关。而且使用.Find() 确实不是一个好主意。
  • 我一直在寻找任何声明 Find 使用 .Value 而不是 .Value2 的内容,它显然正在这样做,但我找不到任何具体的内容。
  • 做到了。 @Vityata
【解决方案2】:

不确定为什么会出现 11 月 1 日,但您可以通过为 Find() 方法的可选 LookAt 参数指定一个值来修复它:

Set foundRange = Rows(1).Find(CDate("01.01.2016"), Lookat:=xlWhole)

【讨论】:

  • 感谢您的回答。通常,它给出它找到的第一个值。关于可选的After 参数为空,最后检查 Range("A1"),因为它是After 的默认值。就认为已找到 11 月而言,它会将其归还,并且不会更进一步。问题更像是 - 11 月与 1 月相同的逻辑是什么?
  • 微软逻辑 :)
  • 其实 11 月和 1 月的结果背后有一些逻辑。请参阅 wiki 答案,我已尝试解释。
【解决方案3】:

我发现 CDate 不时行为不端。此代码似乎首先将您的日期转换为字符串,而不是日期的数字表示。该转换发生在您找到字符串之后。既然你提到它跳过第一个单元格,那么包含该字符串的第一个日期是 11/1/16。

debug.print cdate("01/01/16")
1/1/16

Set foundRange = Rows(1).Find(CDate(#1/1/2016#))
Debug.Print foundRange.Value2
42675

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多