处理时区多问题:Rails配置、PostgreSQL时间存储与查询
问题
某应用配置了非UTC时区,设置如下:
config.timezone = 'Bogota' config.active_record.default_timezone = :local
原本目的是让系统时间戳采用应用的主时区,但该应用需要收集事件发生的datetime及对应时区,日志传入的参数示例如下:
"event"=>{"name_last"=>"tz event", "date(3i)"=>"1", "date(2i)"=>"1", "date(1i)"=>"1987", "date(4i)"=>"01", "date(5i)"=>"01", "tz"=>"Hawaii"}
数据库表结构包含两个独立属性:
t.timestamptz "date" t.string "tz"
需求是让用户既能以事件提供的tz时区查看timestamptz属性,也能以UTC时区查看。当前遇到的问题是输入数据会被按应用配置的Bogota时区处理。
请问:
- 是否移除时区配置、全程使用UTC更合理?
- 如何处理这些参数,在PostgreSQL中结合夏令时正确保存带输入时区的记录?
- 如何实现UTC与事件时区两种时间展示?
回答
关于是否全程用UTC更合理
非常建议全程使用UTC作为应用和数据库的基准时区,理由很直接:
- 多时区场景下,UTC是无歧义的基准,能彻底避免时间转换混乱,尤其适合你这种需要存储不同事件时区的场景
- 把Rails配置改成UTC后,ActiveRecord默认以UTC处理时间,减少不必要的时区转换错误
- 后续扩展其他时区需求时,UTC的兼容性更强,不会因为应用主时区变更影响历史数据
对应的配置修改为:
config.timezone = 'UTC' config.active_record.default_timezone = :utc
正确保存带输入时区的记录
要结合夏令时正确保存,核心是把用户输入的日期时间+指定时区,转换成UTC时间后存入timestamptz字段,同时保留原始时区字符串:
- 拼接时间参数:把
date(1i)、date(2i)等参数组合成标准日期时间字符串,比如"1987-01-01 01:01" - 按指定时区解析并转UTC:临时切换到用户指定的时区解析时间,再转成UTC存入数据库,同时保存原始时区
示例代码:
这样PostgreSQL的# 拼接日期时间字符串 date_str = "#{params[:event]['date(1i)']}-#{params[:event]['date(2i)']}-#{params[:event]['date(3i)']} #{params[:event]['date(4i)']}:#{params[:event]['date(5i)']}" # 用用户指定的时区解析时间 event_time = ActiveSupport::TimeZone.new(params[:event]['tz']).parse(date_str) # 转UTC后存入,同时保留原始时区 Event.create!(date: event_time.utc, tz: params[:event]['tz'])timestamptz会存储UTC时间,后续转换时会根据时区规则自动处理夏令时问题。
实现两种时区的时间展示
- 以事件时区展示:取出数据库中的UTC时间,结合保存的
tz字段转换为对应时区的时间即可:# 假设event是取出的记录 event_time_in_tz = event.date.in_time_zone(event.tz) # 格式化展示,比如"1987-01-01 01:01 HST" event_time_in_tz.strftime("%Y-%m-%d %H:%M %Z") - 以UTC时区展示:直接取出
date字段格式化即可,因为数据库存的是UTC,ActiveRecord会默认返回UTC时间:event.date.strftime("%Y-%m-%d %H:%M UTC")
内容的提问来源于stack exchange,提问作者Jerome
相关产品推荐
相关产品推荐

