Ответ 1
Изменение m.happened_at = '2012-01-01T00:00:00.32323'.to_datetime
в коде выше до m.happened_at = '2012-01-01T00:00:00.32323'
решает проблему, хотя я понятия не имею, почему.
У меня есть строка типа 2012-01-01T01:02:03.456
, которую я храню в базе данных Postgres TIMESTAMP с помощью ActiveRecord.
К сожалению, Ruby, похоже, отрубает миллисекунды:
ruby-1.9.3-rc1 :078 > '2012-12-31T01:01:01.232323+3'.to_datetime
=> Mon, 31 Dec 2012 01:01:01 +0300
Postgrs поддерживает разрешение микросекунды. Как я могу получить соответствующую метку времени? Мне нужно разрешение не менее миллисекунды.
(PS Да, я мог бы взломать целочисленный столбец в миллисекундах в postgres, этот вид побеждает всю цель ActiveRecord.)
UPDATE:
Очень полезные ответы показали, что Ruby DateTime
не прерывает миллисекунды; используя #to_f
показывает это. Но, делая:
m.happened_at = '2012-01-01T00:00:00.32323'.to_datetime
m.save!
m.reload
m.happened_at.to_f
Отбрасывает миллисекунды.
Теперь интересно, что created_at
показывает миллисекунды, как в Rails, так и в Postgres. Но другие поля временных меток (например, happened_at
выше) этого не делают. (Возможно, Rails использует функцию NOW()
для created_at
, а не для передачи в DateTime).
Что приводит к моему окончательному вопросу:
Как я могу получить ActiveRecord для сохранения миллисекундного разрешения в полях timestamp?
Изменение m.happened_at = '2012-01-01T00:00:00.32323'.to_datetime
в коде выше до m.happened_at = '2012-01-01T00:00:00.32323'
решает проблему, хотя я понятия не имею, почему.
ActiveRecord должен сохранять полную точность из базы данных, вы просто не смотрите на нее должным образом. Используйте формат strftime
и %N
, чтобы увидеть дробные секунды. Например, psql
говорит следующее:
=> select created_at from models where id = 1;
created_at
----------------------------
2012-02-07 07:36:20.949641
(1 row)
и ActiveRecord говорит следующее:
> Model.find(1).created_at.strftime('%Y-%m-%d %H:%M:%S.%N')
=> "2012-02-07 07:36:20.949641000"
Итак, все есть, вам просто нужно знать, как его увидеть.
Также обратите внимание, что ActiveRecord, вероятно, даст вам объекты ActiveSupport::TimeWithZone
, а не DateTime
, но DateTime
тоже сохранит все:
> '2012-12-31T01:01:01.232323+3'.to_datetime.strftime('%Y-%m-%d %H:%M:%S.%N')
=> "2012-12-31 01:01:01.232323000"
Посмотрите connection_adapters/column.rb
в источнике ActiveRecord и проверьте, что делает метод string_to_time
. Ваша строка пойдет вниз по пути fallback_string_to_time
и сохранит дробные секунды как можно ближе. Что-то странное могло происходить в другом месте, я бы не удивился, учитывая странные вещи, которые я видел в источнике Rails, особенно на стороне базы данных. Я бы попытался преобразовать строки в объекты вручную, чтобы ActiveRecord оставил свои руки.
Я оказался здесь, когда я страдал от использования RVM, предоставленного двоичным Ruby 2.0.0-p247 на OS X (Mavericks), который вызывал округление до целых значений секунд при извлечении времени из Postgres. Реконструкция Ruby сама (rvm reinstall 2.0.0 --disable-binary
) решила проблему для меня.
Смотрите https://github.com/wayneeseguin/rvm/issues/2189, который я нашел через https://github.com/rails/rails/issues/12422.
Я понимаю, что это не ответ на этот вопрос, но я надеюсь, что эта заметка может помочь кому-то бороться с ней.
to_datetime
не уничтожает миллисекундное разрешение данных - он просто скрыт, потому что DateTime#to_s
не отображает его.
[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
Тем не менее, я подозреваю, что ActiveRecord ошибочно скрывает эту информацию при сохранении данных; помните, что он агрегирован для базы данных, поэтому он использует подходы, которые гарантируют работу во всех своих целевых объектах базы данных. Хотя Postgres предполагала получение информации о микросекундах во временные метки, MySQL не делает этого, поэтому я подозреваю, что AR выбирает самый низкий общий знаменатель. Я не мог быть уверен, не попав в мужество АР. Для включения этого поведения вам может понадобиться специальная опция monkeypatch для Postgres.