Подтвердить что ты не робот

Разрешение миллисекундной даты DateTime в Ruby

У меня есть строка типа 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?

4b9b3361

Ответ 1

Изменение m.happened_at = '2012-01-01T00:00:00.32323'.to_datetime в коде выше до m.happened_at = '2012-01-01T00:00:00.32323' решает проблему, хотя я понятия не имею, почему.

Ответ 2

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 оставил свои руки.

Ответ 3

Я оказался здесь, когда я страдал от использования 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.

Я понимаю, что это не ответ на этот вопрос, но я надеюсь, что эта заметка может помочь кому-то бороться с ней.

Ответ 4

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.