Rails 6.1.4.1 Docker环境下ActiveRecord连接PG时区返回UTC问题
问题原因说明
你观察到的ActiveRecord::Base.connection.execute("show timezone;")返回UTC的现象属于Rails ActiveRecord的常规行为,不属于配置错误,也不是测试方法有误,核心原因如下:
config.active_record.default_timezone配置的作用仅为控制ActiveRecord将数据库返回的时间值转换为应用层时间对象时的解析规则,不会主动修改PostgreSQL连接的会话级时区参数- 你直接通过psql进入PG实例查询到的是PG全局时区配置,而Rails建立的每个数据库连接默认使用UTC作为会话时区,会话级配置优先级高于全局配置,因此直接在Rails连接中执行
show timezone会返回UTC - 之前的校验结果全部正常,是因为ActiveRecord已经按照
config.time_zone的配置,自动将数据库返回的UTC时间转换为了柏林时区的时间对象,所以应用层感知不到差异。如果你的业务代码中没有直接在SQL中调用PostgreSQL的时间函数(如now()、current_timestamp等),当前配置不会产生业务问题。
修正方案
如果你需要让Rails建立的所有数据库连接会话时区都为柏林时区,可选择以下任意一种方案实现:
方案1(更推荐,配置更简洁)
在你的config/database.yml对应环境的配置块中,添加variables参数设置时区:
development: adapter: postgresql # 原有配置项:database、username、password等保持不变 variables: timezone: 'Europe/Berlin' # 生产/测试等其他环境配置块也同步添加即可 production: adapter: postgresql # 原有配置项保持不变 variables: timezone: 'Europe/Berlin'
重启Rails服务后即可生效。
方案2(动态配置场景适用)
如果你的时区配置需要从环境变量等动态来源读取,可以通过初始化器实现,新建config/initializers/database_timezone.rb文件,写入如下内容:
Rails.application.config.after_initialize do ActiveRecord::Base.connection_pool.on_connection_add do |conn| conn.execute("SET TIME ZONE 'Europe/Berlin'") # 如需从环境变量动态读取,可替换为:conn.execute("SET TIME ZONE '#{ENV['TZ']}'") end end
内容的提问来源于stack exchange,提问作者Montells
相关产品推荐
相关产品推荐

