parse - strptime ruby
ResoluciĆ³n en milisegundos de DateTime en Ruby (4)
ActiveRecord debe preservar la precisión total de la base de datos, simplemente no la está mirando correctamente. Use strftime
y el formato %N
para ver los segundos fraccionarios. Por ejemplo, psql
dice esto:
=> select created_at from models where id = 1;
created_at
----------------------------
2012-02-07 07:36:20.949641
(1 row)
y ActiveRecord dice esto:
> Model.find(1).created_at.strftime(''%Y-%m-%d %H:%M:%S.%N'')
=> "2012-02-07 07:36:20.949641000"
Entonces, todo está allí, solo necesitas saber cómo verlo.
También tenga en cuenta que ActiveRecord probablemente le ActiveSupport::TimeWithZone
objetos ActiveSupport::TimeWithZone
lugar de objetos DateTime
, pero DateTime
conserva todo:
> ''2012-12-31T01:01:01.232323+3''.to_datetime.strftime(''%Y-%m-%d %H:%M:%S.%N'')
=> "2012-12-31 01:01:01.232323000"
Eche un vistazo a connection_adapters/column.rb
en la fuente ActiveRecord y verifique qué hace el método string_to_time
. Su cadena iría por la ruta de fallback_string_to_time
y eso conservará los segundos fraccionarios tan cerca como pueda ver. Algo extraño podría estar sucediendo en otra parte, no me sorprendería si se tuvieran en cuenta las cosas extrañas que he visto en la fuente de Rails, especialmente el lado de la base de datos. Intentaría convertir las cadenas en objetos a mano para que ActiveRecord los mantenga alejados de ellos.
Tengo una cadena como 2012-01-01T01:02:03.456
que estoy almacenando en una base de datos de Postgres TIMESTAMP usando ActiveRecord.
Desafortunadamente, Ruby parece cortar los milisegundos:
ruby-1.9.3-rc1 :078 > ''2012-12-31T01:01:01.232323+3''.to_datetime
=> Mon, 31 Dec 2012 01:01:01 +0300
Postgrs admite una resolución de microsegundos. ¿Cómo puedo hacer que mi marca de tiempo se guarde en consecuencia? Necesito al menos una resolución de milisegundos.
(PD: Sí, podría piratear una columna de enteros de milisegundos en postgres, que de alguna forma frustra el propósito de ActiveRecord).
ACTUALIZAR:
Las respuestas muy útiles mostraron que DateTime
de Ruby no está cortando milisegundos; usar #to_f
muestra. Pero, haciendo:
m.happened_at = ''2012-01-01T00:00:00.32323''.to_datetime
m.save!
m.reload
m.happened_at.to_f
Hace caer los milisegundos.
Ahora, lo interesante es que created_at
muestra milisegundos, tanto en Rails como en Postgres. Pero otros campos de marcas de tiempo (como lo happened_at
superior) no. (Quizás Rails usa una función NOW()
para created_at
en lugar de pasar en DateTime).
Lo que lleva a mi última pregunta:
¿Cómo puedo obtener ActiveRecord para preservar la resolución de milisegundos en los campos de marca de tiempo?
Cambiar m.happened_at = ''2012-01-01T00:00:00.32323''.to_datetime
en el código anterior a m.happened_at = ''2012-01-01T00:00:00.32323''
resuelve el problema, aunque no tengo idea de por qué.
Terminé aquí cuando sufría el uso del RVM proporcionado Ruby 2.0.0-p247 binario en OS X (Mavericks) que estaba causando el redondeo a valores enteros de segundos al recuperar los tiempos de Postgres. La reconstrucción de Ruby ( rvm reinstall 2.0.0 --disable-binary
) resolvió el problema para mí.
Ver https://github.com/wayneeseguin/rvm/issues/2189 que encontré a través de https://github.com/rails/rails/issues/12422 .
Reconozco que esta no es LA RESPUESTA a este problema, pero espero que esta nota pueda ayudar a alguien a lidiar con ella.
to_datetime
no destruye la resolución de los datos en milisegundos, simplemente está oculta porque DateTime#to_s
no la muestra.
[1] pry(main)> ''2012-12-31T01:01:01.232323+3''.to_datetime
=> Mon, 31 Dec 2012 01:01:01 +0300
[2] pry(main)> ''2012-12-31T01:01:01.232323+3''.to_datetime.to_f
=> 1356904861.232323
Dicho esto, sospecho que ActiveRecord está ocultando por error esa información cuando persisten los datos; recuerde que es independiente de la base de datos, por lo que se necesitan enfoques que garanticen que funcionen en todos sus objetivos de base de datos. Mientras que Postgres supuso información de microsegundos en sellos de tiempo, MySQL no lo hace, entonces sospecho que AR selecciona el mínimo común denominador. No podría estar seguro sin meterme en las entrañas de AR. Es posible que necesite un monopatch específico de Postgres para habilitar este comportamiento.