【发布时间】:2021-12-03 03:36:34
【问题描述】:
我有一个 Rails 应用程序正在创建日历事件的视图,其中一些存储为带有 starts_at 列的日历事件,其中一些具有从重复计划创建的生成的 starts_at 列。
视图是一个联合,看起来像这样(简化):
(
SELECT
'appointment' AS event_type,
starts_at
FROM
appointments
)
UNION
(
SELECT
'schedule' AS event_type,
(
to_timestamp(
CONCAT(
start_date,
' ',
lpad(start_hour :: text, 2, '0'),
':',
lpad(start_minute :: text, 2, '0'),
':00.000000'
),
'YYYY-MM-DD hh24:mi:ss:us'
) at time zone 'UTC'
) :: timestamp without time zone AS starts_at
FROM
schedule_items
)
这很好用,当我在 postgres 中查询视图时,我得到:
event_type | ends_at
-------------+----------------------------
schedule | 2021-10-18 08:00:00
schedule | 2021-11-08 09:00:00
appointment | 2021-10-14 17:44:15.122543
这些都是正确的 UTC 时间,而不是本地时区。
我在这个视图周围包裹了一个 ActiveRecord 模型(使用 scenic gem 生成视图)但是当我查询模型时,它为 appointment 记录提供了正确的时间,但为 schedule 提供了不正确的时间(生成的)记录。
约会显示在上面的 UTC 时间(当前比英国当地时区晚 1 小时)。
计划时间显示在上述时间加上 1 小时(以 UTC 表示),因此当 Active Record 投射时比 UTC 提前 2 小时。
如果我为属性构建自定义cast_type,我可以看到它在上面第一次读取为2021-10-18 08:00:00 UTC(有效地将其转换为当地时间但标记为UTC)但对于约会记录,它正在正确读取它作为2021-10-14 17:44:15.122566 UTC。
如果我在 Active Record 中使用基本 SQL 查询,我会得到以下结果:
irb(main):045:0> r = ActiveRecord::Base.connection.execute('select starts_at from calendar_events')
irb(main):045:0> r[0]
=> {"starts_at"=>2021-10-18 09:00:00.000000 UTC}
irb(main):046:0> r[2]
=> {"starts_at"=>2021-10-14 17:44:15.122543 UTC}
这表明第一条记录的时间解析错误,最后一条记录正确。
如果我在本地使用 pg gem,我会得到与使用 sql 查询相同的结果:
irb(main):001:0> conn = PG.connect( dbname: 'my_db' )
=> #<PG::Connection:0x00000001222d3a48>
irb(main):002:1* conn.exec('select * from calendar_events') do |result|
irb(main):003:2* result.each do |row|
irb(main):004:2* puts row['starts_at']
irb(main):005:1* end
irb(main):006:0> end
2021-10-14 17:44:15.122543
2021-11-08 09:00:00
2021-10-18 08:00:00
这显示了我期望的结果。
日历表中的日期时间列类型与用于在视图中创建日期时间的类型相同,例如
starts_at timestamp without time zone
rails 应用程序和 Postgres 数据库都设置为 Europe/London 时区,所有时间戳都以 timestamp without time zone 类型写入数据库,其值在 UTC 中。
我尝试了多种方法来解决它(例如,为模型上的属性创建自定义 cast_type,添加 self.skip_time_zone_conversion_for_attributes、attribute_before_type_cast['starts_at'])这些都没有解决 ActiveRecord 似乎正在转换某些日期的问题UTC 到本地时间,但仍将它们标记为 UTC。
所以我有点茫然,所以任何人的任何建议都将不胜感激!
【问题讨论】:
-
如果我在数据库中将时间戳格式化为 ISO8601 字符串而不是实际时间戳,则更令人困惑的是:
2021-10-18T08:00:00.000000Z当它被加载到 ActiveRecord 中时,它会在转换之前转换为2021-10-18T09:00:00.000000Z。使用pggem 直接进行查询,通过 ActiveRecord 进行查询会奇怪地转换字符串(与转换日期的方式相同)。就像它正在对它认为是日期时间的字符串进行某种本地时间转换...... -
另外,如果您认为这是一个错误,您可以填写此模板并在 Rails github 问题跟踪器上提交错误github.com/rails/rails/blob/main/guides/bug_report_templates/…
-
@Eyeslandic 如果我加上时区,它有趣地相似 - 数据库有
2021-11-08 09:00:00+00(这实际上是错误的,因为我们希望它是 08:00)并且当 ActiveRecord 解析它时它至少有相同的值:2021-10-18 09:00:00 +0000并呈现为Mon, 18 Oct 2021 10:00:00.000000000 BST +01:00。我真的不知道这是一个错误,还是与我在 Postgres 中的演员表和时区有关,所以不确定它是否属于错误,因为我确定我一定错过了一些重要的东西! -
@Eyeslandic 我做了更多的测试,我更确信它毕竟是 ActiveRecord 中的一个错误。我创建了一个测试脚本并在任何情况下都将其作为错误提交:github.com/rails/rails/issues/43467。我怀疑它与 psql 中的时区设置有关。在这种情况下,带有生成时间戳的奇怪视图似乎是唯一的问题。
标签: ruby-on-rails postgresql datetime view rails-activerecord