微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ruby-on-rails – 有谁知道如何在rails 2.3中适当处理用户时区?

我们正在构建一个需要在多个时区显示日期(更重要的是,计算它们)的rails应用程序.

谁能指出我如何在rails 2.3(.5或.8)中使用用户时区

我见过的最具包容性的文章详细介绍了用户时区应该如何工作:http://wiki.rubyonrails.org/howtos/time-zones …虽然不清楚这是什么时候编写的,或者是什么版本的rails.具体而言,它指出:

“Time.zone – 实际用于显示目的的时区.可以手动设置,以基于每个请求覆盖config.time_zone.”

键术语是“显示目的”和“按请求基础”.

在我的机器上,这是真的.然而,在生产中,两者都不是真的.设置Time.zone持续超过请求的结束(对所有后续请求)并且还影响AR保存到DB的方式(基本上将任何日期视为已经在UTC中,即使它不是),从而保存完全不合适的值.

我们与乘客一起运行Ruby Enterprise Edition.如果这是我的问题,我们是否需要切换到JRuby或其他?

为了说明这个问题,我现在在ApplicationController中添加了以下操作:

def test
p_time = Time.Now.utc
s_time = Time.utc(p_time.year,p_time.month,p_time.day,p_time.hour)

logger.error "TIME.ZONE" + Time.zone.inspect
logger.error ENV['TZ'].inspect
logger.error p_time.inspect
logger.error s_time.inspect

jl = JunkLead.create!
jl.date_at = s_time

logger.error s_time.inspect
logger.error jl.date_at.inspect

jl.save!

logger.error s_time.inspect
logger.error jl.date_at.inspect


render :nothing => true,:status => 200
end


def test2
Time.zone = 'Mountain Time (US & Canada)'
logger.error "TIME.ZONE" + Time.zone.inspect
logger.error ENV['TZ'].inspect

render :nothing => true,:status => 200
end


def test3
Time.zone = 'UTC'
logger.error "TIME.ZONE" + Time.zone.inspect
logger.error ENV['TZ'].inspect


render :nothing => true,:status => 200
end

他们产生以下结果:

Processing ApplicationController#test (for 98.202.196.203 at 2010-12-24 22:15:50) [GET]
TIME.ZONE#<ActiveSupport::TimeZone:0x2c57a68 @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>,@name="UTC",@utc_offset=0>
nil
Fri Dec 24 22:15:50 UTC 2010
Fri Dec 24 22:00:00 UTC 2010
Fri Dec 24 22:00:00 UTC 2010
Fri,24 Dec 2010 22:00:00 UTC +00:00
Fri Dec 24 22:00:00 UTC 2010
Fri,24 Dec 2010 22:00:00 UTC +00:00
Completed in 21ms (View: 0,DB: 4) | 200 OK [http://www.dealsthatmatter.com/test]


Processing ApplicationController#test2 (for 98.202.196.203 at 2010-12-24 22:15:53) [GET]
TIME.ZONE#<ActiveSupport::TimeZone:0x2c580a8 @tzinfo=#<TZInfo::DataTimezone: America/Denver>,@name="Mountain Time (US & Canada)",@utc_offset=-25200>
nil
Completed in 143ms (View: 1,DB: 3) | 200 OK [http://www.dealsthatmatter.com/test2]


Processing ApplicationController#test (for 98.202.196.203 at 2010-12-24 22:15:59) [GET]
TIME.ZONE#<ActiveSupport::TimeZone:0x2c580a8 @tzinfo=#<TZInfo::DataTimezone: America/Denver>,@utc_offset=-25200>
nil
Fri Dec 24 22:15:59 UTC 2010
Fri Dec 24 22:00:00 UTC 2010
Fri Dec 24 22:00:00 UTC 2010
Fri,24 Dec 2010 15:00:00 MST -07:00
Fri Dec 24 22:00:00 UTC 2010
Fri,24 Dec 2010 15:00:00 MST -07:00
Completed in 20ms (View: 0,DB: 4) | 200 OK [http://www.dealsthatmatter.com/test]

Processing ApplicationController#test3 (for 98.202.196.203 at 2010-12-24 22:16:03) [GET]
TIME.ZONE#<ActiveSupport::TimeZone:0x2c57a68 @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>,@utc_offset=0>
nil
Completed in 17ms (View: 0,DB: 2) | 200 OK [http://www.dealsthatmatter.com/test3]

Processing ApplicationController#test (for 98.202.196.203 at 2010-12-24 22:16:04) [GET]
TIME.ZONE#<ActiveSupport::TimeZone:0x2c57a68 @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>,@utc_offset=0>
nil
Fri Dec 24 22:16:05 UTC 2010
Fri Dec 24 22:00:00 UTC 2010
Fri Dec 24 22:00:00 UTC 2010
Fri,24 Dec 2010 22:00:00 UTC +00:00
Completed in 151ms (View: 0,DB: 4) | 200 OK [http://www.dealsthatmatter.com/test]

上面应该清楚,对/ test的第二次调用显示Time.zone设置为Mountain,即使它不应该.

此外,检查数据库显示,在test2之后运行的测试操作保存了日期为2010-12-22 15:00:00的JunkLead记录,这显然是错误的.

解决方法

经过详尽的研究后,现在完全清楚的是,大多数Rails版本(包括2.3& 3)都会破坏Time.zone.此功能使用中心线程哈希来存储设置的值(应该是线程安全的,而不是),并最终修改所有后续请求的行为.此外,与文档相反,设置Time.zone会修改ActiveRecord行为并将日期时间保存在新区域中,而不是在config中指定的日期时间(通常为UTC).

在Rails得到修复之前,我们选择手动使用Timezones,可以通过未记录的方法访问:

ActiveSupport::TimeZone['Arizona'].Now (or .local,or .parse).

另外,我修补了Time和ActiveSupport :: TimeWithZone,以便将时间片段轻松转换为不同的区域.要清楚,我指的是不同区域的相应时刻,而不是同时的时刻.

>> Time.utc(2011)
=> Sat Jan 01 00:00:00 UTC 2011
>> Time.utc(2011).in_time_zone('Arizona')
=> Fri,31 Dec 2010 17:00:00 MST -07:00 #Simultaneous
>> Time.utc(2011).change_zone('Arizona')
=> Sat,01 Jan 2011 00:00:00 MST -07:00 #Corresponding

补丁如下:

module ActiveSupport #:nodoc:
  module CoreExtensions #:nodoc:
    module Time #:nodoc:
      module ZoneCalculations
        def self.included(base) #:nodoc:
          base.class_eval do
            alias_method_chain :change,:zone
          end
        end

        def change_zone(new_zone)
          new_zone = ::Time.__send__(:get_zone,new_zone)
          return self if new_zone.name == zone
          new_zone.local(year,month,day,hour,min,sec,usec)
        end

        def change_with_zone(options)
          result = change_without_zone(options)
          options[:zone] ? result.change_zone(options[:zone]) : result
        end

      end
    end
  end
  class TimeWithZone
    def change_zone(new_zone)
      time.change_zone(new_zone)
    end
    def change(options)
      time.change(options)
    end
  end

end

class Time
  include ActiveSupport::CoreExtensions::Time::ZoneCalculations
end

原文地址:https://www.jb51.cc/ruby/269334.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐