Ama*_*Jay 7 ruby timezone ruby-on-rails
我们正在构建一个需要在多个时区显示日期(更重要的是,计算它们)的rails应用程序.
任何人都可以指出我如何在rails 2.3(.5或.8)中使用用户时区
我见过的最具包容性的文章详细介绍了用户时区应该如何工作:http://wiki.rubyonrails.org/howtos/time-zones ...虽然目前还不清楚这是什么时候写的或是什么版本铁轨 具体而言,它指出:
"Time.zone - 实际用于显示目的的时区.可以手动设置,以基于每个请求覆盖config.time_zone."
键术语是"显示目的"和"按请求基础".
在我的机器上,这是真的.但是在生产中,两者都不是真的.设置Time.zone会持续超过请求的结束(对所有后续请求),并且还会影响AR保存到数据库的方式(基本上将任何日期视为已经在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
Run Code Online (Sandbox Code Playgroud)
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>, @name="Mountain Time (US & Canada)", @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>, @name="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>, @name="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
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]
Run Code Online (Sandbox Code Playgroud)
上面应该清楚,对/ test的第二次调用显示Time.zone设置为Mountain,即使它不应该.
此外,检查数据库显示,在test2之后运行的测试操作保存了日期为2010-12-22 15:00:00的JunkLead记录,这显然是错误的.
小智 9
经过详尽的研究后,现在完全清楚的是,在大多数Rails版本(包括2.3和3)中,Time.zone都被破坏了.此功能使用中心线程哈希来存储设置的值(应该是线程安全的,而不是),并最终修改所有后续请求的行为.此外,与文档相反,设置Time.zone会修改ActiveRecord行为并将日期时间保存在新区域中,而不是在config中指定的日期时间(通常为UTC).
在Rails得到修复之前,我们选择手动使用Timezones,可以通过未记录的默认方法访问:
ActiveSupport::TimeZone['Arizona'].now (or .local, or .parse).
Run Code Online (Sandbox Code Playgroud)
另外,我修补了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
Run Code Online (Sandbox Code Playgroud)
补丁如下:
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
Run Code Online (Sandbox Code Playgroud)
你是正确的,因为Rails不会自动重置每个请求的时区.维基作者给出了一个示例,其中时区在前置过滤器中设置,因此他从未遇到时区在请求之间"泄漏"的问题(因为在请求之前正确设置了时区).RDoc文档中使用的示例TimeZone.zone=类似.所以我认为这是一个文档问题.
更改时区不是本地请求,而是本地线程.Rails将所选时区存储在当前线程中(请参阅参考资料Thread.current.[:time_zone]),而不是当前请求中.由于同一个线程处理多个请求,因此对Time.zone的更改在请求之间是持久的.
我认为在您的方案中使用时区的正确方法是使用Time.use_zone:
def my_action
Time.zone # 'UTC'
Time.use_zone('Mountain Time (US & Canada)') do
Time.zone # 'Mountain Time (US & Canada)'
end
Time.zone # 'UTC'
end
Run Code Online (Sandbox Code Playgroud)
我没有时间查看您的ActiveRecord问题.当我这样做时,将返回更新.
我在构建支持票务系统时遇到了这个问题。我的解决方案如下。
将 Datetime SQL 和 ActiveRecord 时区设置为 UTC。
在应用程序的配置中设置所需的时间格式。
根据您希望约会对象的 CSS 外观创建一个 CSS 类。让名字独一无二。EG UTC 日期
编写 javascript 在页面中搜索指定的 Date DOM 类(上面的 UTCDate)。由于 Javascript 是由最终用户执行的,因此它将用本地计算机设置的值替换该值。如果出现解析错误,请保留 UTC 时间。在这里查找相关的 javascript
此解决方案的优点是您的应用程序没有像许多论坛那样的愚蠢的时区选择器。它还减少了服务器负载和代码库大小(因为您不必存储或处理有关客户端时区的任何信息)来处理重要但困难的任务。
| 归档时间: |
|
| 查看次数: |
1878 次 |
| 最近记录: |