Основываясь на ваших комментариях, я думаю, что основная проблема заключается в преобразовании точного времени в микросекунды в Float. Float (хотя в ruby внутренне Double) не обладает достаточной точностью для полного представления всех дат / времени с точностью до микросекунд, как это задокументировано в классе Time (хотя они говорят о «наносекундах», что интересно). Такое преобразование только тогда пытается найти ближайшее возможное представление в Float. Округление полученного числа с плавающей запятой до 6 цифр может сработать, но я не уверен, что это всегда будет работать…
Предположим, что реальное время, хранящееся в БД, равно 2020-08-07 11:30:28.593491
. Как вы заметили, это преобразуется в Float неточно:
>> Time.parse('2020-08-07 11:30:28.593491').to_f
=> 1596792628.5934908
Гарантированный метод заключался бы в использовании вместо этого рационального числа , т.е. to_r
:
>> Time.parse('2020-08-07 11:30:28.593491').to_r
=> (1596792628593491/1000000)
Чтобы восстановить время обратно из рационального числа, вы можете использовать Time.at
:
>> Time.at(Rational(1596792628593491, 1000000)).usec
=> 593491
Обратите внимание, что микросекунды здесь полностью сохранены.
Таким образом, точное сохранение времени created_at
и его последующее использование для поиска записи предполагает использование числовой переменной Rational вместо 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
Альтернативным подходом может быть сохранение обоих целых секунд с начала эпохи. (User.first.created_at.to_i
) и долю наносекунд (User.first.created_at.usec
) отдельно в двух переменных. Их также можно использовать в Time.at
, чтобы восстановить время назад.
В качестве примечания, это также обсуждалось в выпуске Rails с аналогичный вывод.