Rails 5.2:阻止where子句前将时间转换为UTC的方法
解决Rails 5.2中时区不匹配的查询问题
这个问题我之前做项目时也踩过坑——核心就是Rails默认的UTC时区处理逻辑,和你数据库里存储的无时区本地时间不兼容,导致查询时时间被转成UTC后和实际存储的本地时间对不上。下面给你几个实用的解决办法:
方法一:全局配置应用时区(推荐)
直接让Rails整个应用使用你的本地时区,这样ActiveRecord在处理时间时就不会自动转UTC了。
在config/application.rb里修改或添加以下配置:
# 设置应用使用的时区,替换成你对应的时区,比如Asia/Shanghai config.time_zone = 'Asia/Shanghai' # 让ActiveRecord以本地时区存储和查询时间,而非UTC config.active_record.default_timezone = :local
配置完成后,你原来的查询代码就能正常工作了——Time.now会以本地时区的时间和数据库里的starts_at、ends_at直接比较,不会再转成UTC。
方法二:查询时手动指定本地时间
如果不想改动全局配置,你可以在查询时明确使用本地时间,避免Rails自动转UTC:
# 获取当前本地时间,不转UTC local_current_time = Time.now.localtime # 直接用本地时间进行查询 Location.opening_hours.where("starts_at <= ? AND ends_at >= ?", local_current_time, local_current_time).any?
如果你用的是PostgreSQL数据库,还可以通过::time语法确保时间被转换成无时区类型,和数据库字段匹配:
local_current_time = Time.now.in_time_zone('Asia/Shanghai') Location.opening_hours.where("starts_at <= ?::time AND ends_at >= ?::time", local_current_time, local_current_time).any?
方法三:单独配置模型的时间字段
针对OpeningHour模型的starts_at和ends_at字段,告诉Rails不要进行时区转换:
在app/models/opening_hour.rb中添加:
# 标记这两个时间字段不使用时区转换 attribute :starts_at, :time, timezone: false attribute :ends_at, :time, timezone: false
这样Rails在处理这两个字段时,会直接使用本地时间和数据库的无时区时间进行比较,不需要额外转换。
为什么原来的查询会出错?
Rails默认会把所有时间对象转换成UTC来和数据库交互,但你的starts_at、ends_at是无时区的时间类型,存储的是本地时间。比如当前本地时间是上午10点(+8时区),Rails会把它转成UTC的凌晨2点,再去和数据库里的10点比较,自然会得到错误的结果。
内容的提问来源于stack exchange,提问作者simonlehmann
相关产品推荐
相关产品推荐

