10

尝试在 ruby​​ 脚本中使用 roo gem 解析和 XLSX 文件。

在 Excel 中,日期以 DDDDD.ttttt 格式存储为浮点数或整数,从1900-01-00 (00 no 01). 因此,为了转换诸如 40396 之类的日期 - 你会得到1900-01-00 + 40396,你应该得到 2010-10-15,但我得到的是 2010-08-08。

我正在使用 active_support/time 进行计算,如下所示:

Time.new("1900-01-01") + 40396.days

我的计算是错误的还是主动支持中存在错误?

我在 Windows 7 + 最新的 active_support gem (3.2.1) 上运行 ruby​​ 1.9.3-mri

编辑

我在 Excel 中查看带有错误数据的旧文件 - 我的脚本/控制台正在提取正确的数据 - 因此我感到困惑 - 除了使用正确的文件之外,我做的一切都是正确的!!!!该死的通宵达旦!

感谢大家的回复,如果有人需要有关如何使用 ruby​​ 从 excel 转换日期的信息,我会将问题保留在这里。

同样对于遇到此问题的其他任何人-电子表格gem目前不支持正确读取XLSX文件(v 0.7.1)-因此我使用 roo 进行阅读,并使用 axlsx 进行写作。

4

3 回答 3

31

您的日期编号有一个错误 - 由于 Lotus 1-2-3 中的一个错误,Excel 和其他电子表格程序已经小心地保持了 30 多年的兼容性。

最初,第 1 天打算是 1900 年 1 月 1 日(如您所说,这将使第 0 天等于 1899 年 12 月 31 日)。但是 Lotus 错误地认为 1900 年是闰年,所以如果你使用现在的 Lotus 数字并倒数,正确地将 1900 年设为普通年,那么 1900 年 3 月 1 日之前的所有日期数字都太高了。第 1 天变为 1899 年 12 月 31 日,第 0 天移回 30 日。因此,基于 Lotus 的电子表格中日期算术的纪元实际上是 1899 年 12 月 30 日星期六。(现代 Excel 和其他一些电子表格扩展了 Lotus 错误兼容性,足以显示 1900 年 2 月实际上有第 29 天,因此它们将标记日期0 “12 月 31 日”,同时同意这是星期六!但其他基于 Lotus 的电子表格不这样做,Ruby 当然也不这样做。)

但是,即使允许出现此错误,您所说的示例也不正确:Lotus day number 40,396 是 2010 年 8 月 6 日,而不是 10 月 15 日。我已经在 Excel、LibreOffice 和 Google 表格中确认了此通信,所有这些都同意。您一定在某处遇到过示例。

这是进行转换的一种方法:

Time.utc(1899,12,30) + 40396.days #=> 2010-08-06 00:00:00 UTC

或者,您可以利用另一个已知的通信。Ruby(以及一般的 POSIX 系统)的时间为零是 1970 年 1 月 1 日,格林威治标准时间午夜。1970 年 1 月 1 日是莲花日 25,569。只要您记得使用 UTC 进行计算,您也可以这样做:

Time.at( (40396 - 25569).days ).utc # => 2010-08-06 00:00:00 UTC

在任何一种情况下,您可能都希望为纪元日期声明一个符号常量(Time表示 1899-12-30 的对象或 POSIX“第 0 天”值 25,569)。

.days如果您不需要active_support/core_ext/integer/time其他任何内容并且不想为此加载它,则可以将这些调用替换为乘以 86400(每天秒数)。

于 2012-05-12T01:12:46.793 回答
6

“Excel 将日期和时间存储为一个数字,表示自 1900 年 1 月 0 日以来的天数,加上 24 小时日的小数部分:ddddd.tttttt。这称为序列日期或序列日期时间。” ( http://www.cpearson.com/excel/datetime.htm )

如果您的列包含日期时间,而不仅仅是日期,则以下代码很有用:

 dt = DateTime.new(1899, 12, 30) + excel_value.to_f

另请记住,Excel 工作表中有 2 种日期模式,基于 1900 和基于 1904,通常默认情况下会为在 Mac 上创建的电子表格启用。如果您始终发现日期相差 4 年,则应使用不同的基准日期:

 dt = DateTime.new(1904, 1, 1) + excel_value.to_f

您可以为任何电子表格启用/禁用 1904 日期模式,但如果您在添加数据后更改设置,则日期将在电子表格中显示 4 年。一般来说,您应该始终使用 1900 日期模式,因为大多数 excel 用户都是基于 Windows 的。

注意:这种方法的一个问题是舍入可能会发生 +/- 1 秒。对我来说,我导入的日期“足够接近”,但需要牢记。更好的解决方案可能会使用小数秒的舍入来解决此问题。

于 2013-03-06T16:18:02.040 回答
3

你计算错了。您如何得出 2010-10-15 的预期结果?

在 Excel 中,403962010-08-06(当然不使用 1904 年的日历)。为了证明这一点,在 Excel 单元格中键入 40396 并将格式设置为yyyy-mm-dd.

或者:

40396 / 365.2422 = 110.6 (years -- 1900 + 110 = 2010)
0.6 * 12 = 7.2 (months -- January = 1; 1 + 7 = 8; 8 = August)
0.2 * 30 = 6 (days)

Excel 的日历错误地包含 1900-02-29;这说明了您 2010-08-08 结果之间的一天差异;我不确定第二天差异的原因。

于 2012-05-12T01:12:07.783 回答