将现有时间迁移到 Rails 中的 DateTime,同时保持数据完整
Migrating existing Time to DateTime in Rails while keeping data intact
请原谅我问了一个愚蠢的问题,但我 运行 在尝试将 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
这是 运行dom 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: []>
最终,我想迁移所有 ShopHour
表上的属性 from_hours
和 to_hours
,以便它们的类型为 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_...
如果我能提供更多信息,请告诉我。提前致谢!
您实际上无法自动将时间列转换为时间戳,因为时间没有日期部分。 Postgres 实际上正确地阻止了你这样做,因为结果会模棱两可——它应该真正将 12:45
转换为:
的日期
- 公元前 0 年?
- epoc时间的开始?
- 今天几号?
Ruby 实际上没有 class 来表示没有日期部分的时间。主要区别在于 Time 是用 C 编写的简单包装器,它包装了 UNIX 时间戳,而 DateTime 在历史时间上更好。 Rails 只是将 time
数据库列转换为从 2000-01-01 开始的时间这一事实实际上只是一个奇怪但实用的问题解决方案,而不是创建类似 TimeWithoutDate
的东西class.
如果您想将数据库列从 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
因此是一个非常糟糕的名字。
请原谅我问了一个愚蠢的问题,但我 运行 在尝试将 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
这是 运行dom 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: []>
最终,我想迁移所有 ShopHour
表上的属性 from_hours
和 to_hours
,以便它们的类型为 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_...
如果我能提供更多信息,请告诉我。提前致谢!
您实际上无法自动将时间列转换为时间戳,因为时间没有日期部分。 Postgres 实际上正确地阻止了你这样做,因为结果会模棱两可——它应该真正将 12:45
转换为:
- 公元前 0 年?
- epoc时间的开始?
- 今天几号?
Ruby 实际上没有 class 来表示没有日期部分的时间。主要区别在于 Time 是用 C 编写的简单包装器,它包装了 UNIX 时间戳,而 DateTime 在历史时间上更好。 Rails 只是将 time
数据库列转换为从 2000-01-01 开始的时间这一事实实际上只是一个奇怪但实用的问题解决方案,而不是创建类似 TimeWithoutDate
的东西class.
如果您想将数据库列从 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
因此是一个非常糟糕的名字。