2012-05-11 28 views
7

Intentando analizar y el archivo XLSX usando roo gem en un script de ruby.Cómo convertir la fecha de MS Excel del formato float to date en Ruby?

En Excel, las fechas se almacenan como flotantes o enteros en el formato DDDDD.ttttt, contando desde 1900-01-00 (00 no 01). Entonces, para convertir una fecha como 40396, tomaría 1900-01-00 + 40396 y debería obtener 2010-10-15, pero recibiré el 2010-08-08.

estoy usando active_support/hora para hacer el cálculo de este modo:

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

Estoy haciendo mi mal cálculo o hay un error en el apoyo activo?

estoy corriendo rubí 1.9.3-MRI en Windows 7 + última joya active_support (3.2.1)

EDITAR

que estaba buscando en el archivo antiguo en Excel con los datos equivocada - Mi script/consola estaba sacando los datos correctos - de ahí mi confusión - ¡Estaba haciendo todo bien, excepto por usar el archivo correcto! ¡Malditos los nighters!

Gracias a todos los que respondieron, voy a mantener la pregunta aquí en caso de que alguien necesita información sobre cómo convertir las fechas de excel utilizando ruby.

También para cualquier otra persona que se encuentre con esto: la gema de la hoja de cálculo NO es compatible con la lectura de archivos XLSX en este punto (v 0.7.1), así que estoy usando roo para leer y axlsx para escribir.

Respuesta

24

Tiene un error de uno a uno en la numeración de su día, debido a un error en Lotus 1-2-3 que Excel y otros programas de hojas de cálculo han mantenido cuidadosamente su compatibilidad durante más de 30 años.

Originalmente, el día 1 estaba destinado a ser el 1 de enero de 1900 (lo que indicaría, como usted indicó, que el día 0 sea igual al 31 de diciembre de 1899). Pero Lotus consideró incorrectamente que 1900 era un año bisiesto, por lo que los números del día para todo antes del 1 de marzo de ese año son demasiado altos. Usando esos números con un calendario que cuente correctamente 1900 como un año común, el día 1 se convierte en el 31 de diciembre y el día 0 se revierte al 30. Así que la época de la aritmética de fechas en hojas de cálculo basadas en Lotus es el sábado 30 de diciembre de 1899. (Modern Excel y algunas otras hojas de cálculo extienden la compatibilidad de errores de Lotus lo suficiente como para continuar etiquetando esa fecha "31 de diciembre" y acordaron que Sábado, pero otras hojas de cálculo basadas en Lotus no, y Ruby ciertamente tampoco).

Aun teniendo en cuenta este error, sin embargo, su ejemplo establecido es incorrecto: Lotus día número 40,396 es 6 de agosto de 2010, no 15 de octubre. He confirmado esta correspondencia en Excel, LibreOffice y hojas de Google, todos están de acuerdo.Debes haber cruzado ejemplos en alguna parte.

Aquí es una manera de hacer la conversión:

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

Como alternativa, puede tomar ventaja de otra correspondencia conocida. El tiempo cero para Ruby (y los sistemas POSIX en general) es el momento del 1 de enero de 1970, a la medianoche GMT. El 1 de enero de 1970 es día de Lotus 25,569. Como siempre que se recuerde que hacer sus cálculos en UTC, también se puede hacer esto:

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

En cualquier caso, es probable que desee declarar una constante simbólica para la fecha época (ya sea el objeto que representa Time 1899-1812 -30 o el valor POSIX "día 0" 25,569).

Puede reemplazar esas llamadas a .days con una multiplicación de 86,400 si no necesita active_support/core_ext/integer/time para cualquier otra cosa, y no desea cargarlo solo para esto.

+0

La compatibilidad se extiende hasta 1/1/1900, porque el calendario de Excel también tiene 1900 como año bisiesto. – phoog

+0

Bastante justo, @phoog, ya que la pregunta se refiere específicamente a Excel, pero mencioné otras hojas de cálculo también, y no extienden su compatibilidad tan lejos. Ruby tampoco, por supuesto. Entonces efectivamente, el día 0 todavía es 1899-12-30. –

+0

Simplemente ignore todo - estaba confundido - porque en mi script estaba sacando el archivo correcto, pero en Excel tenía un archivo más viejo abierto con la fecha incorrecta. !!!!! Aún así gracias por responder. – konung

3

Usted está haciendo su cálculo incorrecto. ¿Cómo se llega al resultado esperado de 2010-10-15?

En Excel, 40396 es 2010-08-06 (sin utilizar el calendario de 1904, por supuesto). Para demostrarlo, escriba 40396 en una celda de Excel y configure el formato en yyyy-mm-dd.

alternativa: Calendario

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) 

de Excel incluye incorrectamente 29/02/1900; eso explica la diferencia de un día entre su resultado de 2010-08-08; No estoy seguro del motivo del segundo día de diferencia.

3

"Excel almacena las fechas y horas como un número que representa el número de días desde 1900-ene-0, más una porción fraccionaria de un día de 24 horas: ddddd.tttttt. Esto se conoce como fecha de serie o fecha de serie. hora." (http://www.cpearson.com/excel/datetime.htm)

Si su columna contiene una fecha y hora, en lugar de sólo una fecha, el código siguiente es útil:

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

También hay que tener en cuenta que hay 2 modos de fechas en una hoja de cálculo Excel, Basado en 1900 y basado en 1904, que normalmente está habilitado de forma predeterminada para las hojas de cálculo creadas en el mac. Si usted constantemente encontrar sus fechas fuera por 4 años, se debe utilizar una fecha de referencia distinta:

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

Puede activar desactivar el modo/1904 fecha para cualquier hoja de cálculo, pero las fechas aparecerán entonces fuera por 4 años en el hoja de cálculo si cambia la configuración después de agregar datos. En general, siempre debe usar el modo de fecha 1900, ya que la mayoría de los usuarios de Excel en la naturaleza están basados ​​en Windows.

Nota: Un problema con este método es que el redondeo puede ocurrir +/- 1 segundo. Para mí, las fechas que importo son "lo suficientemente cercanas", pero solo hay que tenerlas en cuenta. Una mejor solución puede usar el redondeo en segundos fraccionarios para resolver este problema.

+0

Te hundió mucho! –