Quiero poder encontrar un registro de User por su marca de tiempo created_at (que en este caso es: 2020-08-07 11:30:28.5934908 ).
Pero el resultado es este:
User.where(created_at: '2020-08-07 11:30:28.5934908').first => nilLa razón por la que este registro de usuario (existente) no se encuentra parece hacerse evidente en la consulta de MySQL que generó Rails:
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`created_at` = '2020-08-07 11:30:28.593490' ORDER BY `users`.`id` ASC LIMIT 1
... donde, por alguna razón, el último dígito 8 se elimina de la marca de tiempo 2020-08-07 11:30:28.5934908 utilizada en la consulta MySQL.
¿Cuál es el problema aquí? ¿Rails acorta la marca de tiempo en la consulta? ¿O MySQL hace esto? ¿Cómo puedo solucionar esto?
Según sus comentarios, creo que el principal problema está en la conversión del tiempo preciso en microsegundos a Float. Float (aunque en Ruby internamente es un Double) no tiene suficiente precisión para representar completamente todas las fechas/horas con una precisión de microsegundos, como se documenta en la clase Time (aunque curiosamente hablan de "nanosegundos"). Tal conversión solo intenta encontrar la representación más cercana posible en Float. Redondear el número flotante resultante a 6 dígitos puede funcionar, pero no estoy seguro de que funcione siempre...
Suponga que el tiempo real almacenado en DB es 2020-08-07 11:30:28.593491 . Como habrás notado, esto se convierte en Float de forma imprecisa:
>> Time.parse('2020-08-07 11:30:28.593491').to_f => 1596792628.5934908 El método garantizado sería utilizar un número racional en su lugar, es decir, to_r :
>> Time.parse('2020-08-07 11:30:28.593491').to_r => (1596792628593491/1000000) Para reconstruir el Tiempo a partir del número racional, puede usar Time.at :
>> Time.at(Rational(1596792628593491, 1000000)).usec => 593491Tenga en cuenta que los microsegundos se conservan completamente aquí.
Por lo tanto, almacenar un created_at time con precisión y usarlo más tarde para buscar un registro implica usar una variable de número racional en lugar de Float:
>> user_created_at = User.first.created_at.to_r => (1596792628593491/1000000) >> User.where(created_at: Time.at(user_created_at)).first == User.first => true Un enfoque alternativo podría ser almacenar los segundos enteros desde Epoch ( User.first.created_at.to_i ) y la fracción de nanosegundos ( User.first.created_at.usec ) por separado en dos variables. También se pueden usar en Time.at para reconstruir el tiempo atrás.
Como nota al margen, esto también se ha discutido en una edición de Rails con una conclusión similar.
Esta no es una respuesta directa a su pregunta, sino una especie de solución. Podría consultar la columna created_at en un delta de tiempo muy pequeño como este.
User.where(created_at: '2020-08-07 11:30:28.593490'...'2020-08-07 11:30:28.593491')Según el comentario de @BoraMa (¡gracias!), MySQL created_at timestamp debe tener solo 6 dígitos. Por lo tanto, la consulta de trabajo se vería así:
User.where(created_at: '2020-08-07 11:30:28.5934908'.round(6)).first