Rails中ActiveSupport::TimeWithZone与TimeZone的DST偏移匹配问题
Rails中ActiveSupport时区夏令时偏移问题解决方法
问题背景
使用ActiveSupport::TimeZone和ActiveSupport::TimeWithZone时,遇到夏令时(DST)偏移不匹配问题:以都柏林时区为例,非夏令时时段为UTC+0,夏令时时段为UTC+1。
- 生成时区选择下拉框时,最初代码:
ActiveSupport::TimeZone.all.collect{ |tz| ["(GMT#{ActiveSupport::TimeZone.seconds_to_utc_offset(tz.utc_offset)}) #{tz.name}", tz.name] }
此时都柏林被错误显示为+1,因为tz.utc_offset返回的是时区标准偏移(非夏令时)。将tz.utc_offset改为tz.now.utc_offset后,能正确获取当前时间点的实际偏移(包含DST),显示为+0。
- 用户提交时间后,使用
ActiveSupport::TimeWithZone#change调整时区时:
> time.change(zone: 'Dublin') => Tue, 30 Aug 2022 06:00:00.000000000 IST +01:00
仍错误显示为+1,未识别当前非夏令时时段的UTC+0偏移。
解决方案
不要使用change(zone: ...)方法调整时区,改用in_time_zone方法,它会根据具体时间点自动适配目标时区的夏令时偏移:
> time.in_time_zone('Dublin')
原理说明
change方法的zone参数仅用于设置时区对象,但不会重新计算该时间在目标时区下的实际偏移——它只是保留原时间的时分秒等属性,套用到目标时区的标准偏移上,因此无法正确识别夏令时。in_time_zone方法会将原时间转换为目标时区的时间,同时根据该时间点是否处于夏令时时段,自动应用对应的偏移量,确保时区偏移的准确性。
额外说明
生成时区下拉框时,使用tz.now.utc_offset的方式是正确的,但需注意:用户在不同时段打开页面,显示的偏移会随夏令时变化。若希望展示时区的标准偏移+夏令时说明,可以改用如下方式:
ActiveSupport::TimeZone.all.collect do |tz| base_offset = ActiveSupport::TimeZone.seconds_to_utc_offset(tz.utc_offset) dst_info = tz.dst? ? " (DST: #{ActiveSupport::TimeZone.seconds_to_utc_offset(tz.utc_offset + 3600)})" : "" ["(GMT#{base_offset}) #{tz.name}#{dst_info}", tz.name] end
内容的提问来源于stack exchange,提问作者Tony Beninate
相关产品推荐
相关产品推荐

