[英]Active Record transforming dates inconsistently when querying a postgresql view
我有一个 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
宝石生成视图)但是当我查询模型时,它为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 db 都设置为Europe/London
时区,并且所有时间戳都作为timestamp without time zone
写入数据库, timestamp without time zone
类型,值为UTC
。
我尝试了多种方法来解决它(例如,为模型上的属性创建自定义 cast_type,添加self.skip_time_zone_conversion_for_attributes
, attribute_before_type_cast['starts_at']
)都没有解决 ActiveRecord 似乎正在转换某些日期的问题UTC 到本地时间,但仍将它们标记为 UTC。
所以我有点不知所措,所以任何人的任何建议都将不胜感激!
好的,以防万一其他人遇到此问题,这是解决方案:
SELECT
(
to_timestamp(
'2021-11-18 09:00:00', 'YYYY-MM-DD HH24::m1:ss'
) at time zone 'UTC' at time zone 'Europe/London'
) :: timestamp without time zone as starts_at;
然后 ActiveRecord 会按照您的预期对所有内容进行排序。 显然,在这种情况下,postgres 上的时区设置似乎确实与 ActiveRecord 交互。
在psql
进行原始查询会使数据看起来相同,但使用 UTC 的详细会话时区从 ActiveRecord 读取时显然不完全相同。
时区确实很难。 如果我的本地机器像我所有的生产系统一样设置为“Etc/UTC”,那么就不会有问题。
所以教训是,如果您确实在本地安装 postgres 以进行 rails 开发并希望在视图中使用生成的日期,那么确保时区设置为“Etc/UTC”很重要。
这确实很难解决,但要感谢@eyeslandic,他提出了关于 rails 的错误报告,然后让我创建了一个测试脚本来演示这个问题。
一旦我有了正确的设置组合,就可以很容易地使用该脚本对其进行验证。 否则将很难深入了解。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.