Перенос существующего времени в DateTime в Rails с сохранением данных

#ruby-on-rails #ruby #database #time #database-migration

#ruby-on-rails #ruby #База данных #время #база данных -миграция

Вопрос:

Простите, если я задаю глупый вопрос, но у меня возникли некоторые проблемы при попытке обновить time столбец до datetime .

Вот как schema.rb выглядит соответствующая часть моего:

   create_table "shop_hours", id: :serial, force: :cascade do |t|
    t.time "from_hours"
    t.time "to_hours"
    t.string "day"
    t.integer "repair_shop_id"
    t.boolean "is_shop_open"
    t.integer "chain_id"
    t.integer "regions", default: [], array: true
    t.index ["repair_shop_id"], name: "index_shop_hours_on_repair_shop_id"
  end
  

Вот пример случайного ShopHour объекта:

 [67] pry(main)> ShopHour.find(439)
 #<ShopHour:0x00007ff05462d3a0
 id: 439,
 from_hours: Sat, 01 Jan 2000 15:00:00 UTC  00:00,
 to_hours: Sat, 01 Jan 2000 00:00:00 UTC  00:00,
 day: "Friday",
 repair_shop_id: 468,
 is_shop_open: true,
 chain_id: nil,
 regions: []>
  

В конечном счете, я хочу перенести атрибуты from_hours и to_hours во все мои ShopHour таблицы, чтобы они были типа datetime .

Я также хотел бы обновить дату для каждого from_hours и to_hours быть текущим.

Я попробовал эту миграцию, но столкнулся с ошибкой:

 class ChangeShopHoursToDateTime < ActiveRecord::Migration[6.0]
  def change
    change_column :shop_hours, :from_hours, 'timestamp USING CAST(from_hours AS timestamp)'
    change_column :shop_hours, :to_hours, 'timestamp USING CAST(to_hours AS timestamp)'
  end
end
  

Вот ошибка, с которой я сталкиваюсь:

 == 20201021083719 ChangeShopHoursToDateTime: migrating ========================
-- change_column(:shop_hours, :from_hours, "timestamp USING CAST(from_hours AS timestamp)")
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:

PG::CannotCoerce: ERROR:  cannot cast type time without time zone to timestamp without time zone
LINE 1: ...s" ALTER COLUMN "from_hours" TYPE timestamp USING CAST(from_...
  

Пожалуйста, дайте мне знать, если я могу предоставить дополнительную информацию. Заранее спасибо!

Ответ №1:

Вы не можете автоматически преобразовать столбец времени в метку времени, поскольку у времени нет компонента даты. Postgres на самом деле правильно запрещает вам делать это, поскольку результат будет неоднозначным — на какую дату он должен быть действительно приведен 12:45 :

  • 0 BC?
  • начало времени epoc?
  • сегодняшняя дата?

На самом деле в Ruby нет класса для представления времени без компонента даты. Основное отличие заключается в том, что Time — это простая оболочка, написанная на C, которая переносит временную метку UNIX, а DateTime лучше в исторические времена. Тот факт, что Rails просто приводит столбец time базы данных ко времени, начинающемуся с 2000-01-01, на самом деле является просто странным, но прагматичным решением проблемы вместо создания чего-то вроде TimeWithoutDate класса.

Если вы хотите перенести столбец базы данных из time в timestamp / timestampz, вам нужно сообщить базе данных, на какую дату вы ожидаете, что время будет:

 class AddDatetimeColumnsToHours < ActiveRecord::Migration[6.0]
  def up
    add_column :shop_hours, :opens_at, :datetime
    add_column :shop_hours, :closes_at, :datetime
    ShopHour.update_all(
      [ "closes_at = (timestamp '2000-01-01')   to_hour, opens_at = (timestamp '2000-01-01')   from_hour" ]
    )
  end

  def down
    remove_column :shop_hours, :opens_at
    remove_column :shop_hours, :closes_at
  end
end
  

Это добавляет два новых столбца, и вам действительно следует подумать о том, чтобы просто удалить существующий столбец и использовать эту схему именования, поскольку методы, которые начинаются с to_ , являются методами приведения по соглашению в Ruby (например to_s , to_a , to_h ) — to_hour таким образом, это действительно плохое имя.

Комментарии:

1. О боже, какой фантастический ответ. Я хочу поблагодарить вас за то, что вы нашли время ответить так подробно (также, не снисходительно). Это объяснило все, что я хотел знать. Спасибо, что сделали мое утро! редактировать: вы полностью правы в отношении моего использования to_ . Это … грубо.