变体数组中的日期粘贴到 Excel 有时会丢失一天

Date in Variant Array pasted into Excel sometimes loses a day

以下 Excel VBA 代码比较了在具有 Date 成员的 Variant 数组和 Date 数组中累积小时数(一天的 1/24)的效果。我将 C、E、G 列格式化为日期,将 D、F、H 列格式化为带有很多 d.p.s 的数字。在 A 列和 B 列中,我使用工作表函数复制了计算。在您尝试之前您可能不会相信这一点,但是 Date 的 Variant 数组的最后一个输出提前 1 天。 VBA 报告为 39814.9999999999 的数值在 Excel.

中转换为 39814

编辑:当我说日期格式的 C、E、G 列时,我指的是自定义 "dd/mm/yyyy hh:mm:ss.00"。

有人以前看过这个吗?但更重要的是,我如何知道在使用变体时我可以信任什么?我有数万行代码需要维护,并且在我们需要对 Excel 数据进行密集处理的所有地方都使用 Variant 数组。

Sub FillDatesBug()
Dim dtHours() As Date
Dim vHours As Variant
Dim vHours2 As Variant
Dim dtHour As Date
Dim dHour As Double
Dim i As Long

ReDim dtHours(1 To 25, 1 To 1)
ReDim vHours(1 To 25, 1 To 1)
ReDim vHours2(1 To 25, 1 To 1)

dtHour = CDate(1 / 24)
dHour = 1 / 24
dtHours(1, 1) = CDate(39814)
vHours(1, 1) = CDate(39814)
vHours2(1, 1) = 39814#

For i = 2 To 25
    dtHours(i, 1) = dtHours(i - 1, 1) + dtHour
    vHours(i, 1) = vHours(i - 1, 1) + dtHour
    vHours2(i, 1) = vHours2(i - 1, 1) + dHour
Next i

Range("C2:C26").Value = dtHours
Range("D2:D26").Value = dtHours
Range("E2:E26").Value = vHours
Range("F2:F26").Value = vHours
Range("G2:G26").Value = vHours2
Range("H2:H26").Value = vHours2

Range("C28").Value = "dtHours(25,1) = " & dtHours(25, 1) & " or " & CDbl(dtHours(25, 1))
Range("E28").Value = "vHours(25,1) = " & vHours(25, 1) & " or " & CDbl(vHours(25, 1))
Range("G28").Value = "vHours2(25,1) = " & Format(vHours2(25, 1), "dd/mm/yyyy hh:mm:ss") & " or " & vHours2(25, 1)
End Sub

这里有一个 screenshot - 显然我没有显示它的代表。

如您所见,具有 Double 成员的 Date 数组和 Variant 数组得到了正确的答案(包括完全正常和可理解的数值错误)。具有 Date 成员的 Variant 数组中的某些值被错误地翻译成 Excel。

第一个真正看到我在这里说的话的人赢得了我永恒的感激。

EDIT(更好的解释)。以前见过类似的东西,但不记得细节了。在这种特殊情况下,它似乎至少部分与使用 Variant 数据类型时进行的某些隐式数据转换有关,并将结果写入工作 sheet.

如果您必须使用 Variant 数组,一个解决方法是四舍五入到您想要的精度级别。因此,您将进行以下更改,以四舍五入,例如以毫秒为单位:

For i = 2 To 25
    vHours(i, 1) = Round((vHours(i - 1, 1) + dtHour) * 86400000, 0) / 86400000
    dtHours(i, 1) = Round((dtHours(i - 1, 1) + dtHour) * 86400000, 0) / 86400000
Next I

编辑 补充观察

如果使用Range.Value2属性而不是Range.Value属性将变体数组写回sheet,日期显示在worksheet 单元格将是正确的。但是,公式栏中显示的日期将不正确(比预期早一天)。这似乎是一个 Excel 作为值的问题,例如直接输入单元格的 39814.99999999999 也会在公式栏中显示与工作 sheet 单元格不同的日期。

处理这个(和其他问题)的最安全、最快的方法似乎是在变体数组和工作之间使用 Value2 属性 when reading/writing sheet。有趣的讨论 here,以及 Williams 在他的回复中引用的 link。

如果您希望公式栏中的日期与工作sheet 单元格中的日期一致,除了四舍五入外别无选择。但这可能不是必需的。

我的结论是 - 使用 range.Value = 数组将包含 Date 元素的 Variant 数组复制到 Excel 中是不安全的。有一个错误只发生在这种情况下,显然只有当值非常接近并且可能小于整数时才会发生。

BUG存在于Excel2003,2007,2010,2013...我还没有安装2016

Date 数组可以,包含 Doubles 的 Variant 数组也可以。包含 Dates 的 Variant 数组可以一次转换为 Double 一个元素

array(i,j) = CDbl(array(i,j))

然后又安全了。 VBA 中的算术在所有三种情况下都是相同的 - 日期在 VBA 中是双精度的,您可以在算术语句中互换使用它们。除了格式化之外,Excel 的日期也是双倍的。它只是以这种特定的方式从一个传递到另一个会触发一段糟糕的内部转换代码,将小数部分向上舍入,将整数部分向下舍入。

编辑:Ron 关于使用 .Value2 而不是 .Value 的建议有效,并且比转换为 Double 更简洁(也更快)。谢谢,罗恩。全局搜索和替换,我们开始...