如何在Ruby中将MS excel日期从float格式转换为日期格式?

时间:2012-05-11 23:28:47

标签: ruby-on-rails ruby excel date activesupport

尝试使用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上运行ruby 1.9.3-mri +最新的active_support gem(3.2.1)

修改

我正在使用错误的数据查看Excel中的旧文件 - 我的脚本/控制台正在提取正确的数据 - 因此我的困惑 - 我做的一切正常,除了使用正确的文件!该死的全明星!

感谢大家的回复,我会在这里提出问题,以防有人需要有关如何使用ruby从excel转换日期的信息。

对于遇到此问题的其他人 - 电子表格宝石不支持此时正确读取XLSX文件(v 0.7.1) - 因此我使用roo进行阅读,使用axlsx进行编写。

3 个答案:

答案 0 :(得分:26)

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

最初,第1天的目的是在1900年1月1日(正如你所说,这将使第0天等于1899年12月31日)。但莲花错误地认为1900年是闰年,所以如果你现在使用莲花数并向后计数,正确地使1900年成为常年,那么1900年3月1日之前所有东西的数字都太高了。第1天变为1899年12月31日,第0天变回第30天。因此,基于Lotus的电子表格中日期算术的时代实际上是1899年12月30日星期六。(现代Excel和其他一些电子表格扩展了Lotus bug兼容性足以显示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

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

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

答案 1 :(得分:4)

“Excel将日期和时间存储为表示自1900年1月1日以来的天数的数字,加上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秒。对我来说,我导入的日期“足够接近”,但要记住一些事情。更好的解决方案可能会使用小数秒的舍入来解决此问题。

答案 2 :(得分: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结果中占一天的差异;我不确定第二天差异的原因。