Ruby on rails 有人知道如何在Rails2.3中正确处理用户时区吗?

Ruby on rails 有人知道如何在Rails2.3中正确处理用户时区吗?,ruby-on-rails,ruby,timezone,Ruby On Rails,Ruby,Timezone,我们正在构建一个rails应用程序,它需要在多个时区中显示日期(更重要的是,计算日期) 有人能告诉我如何在Rails2.3(.5或.8)中使用用户时区吗 我所看到的详细介绍用户时区应该如何工作的最全面的文章在这里:。。。虽然还不清楚这是什么时候写的,或者是针对什么版本的rails。具体而言,它指出: “Time.zone—实际用于显示目的的时区。可以手动设置此时区,以根据每个请求覆盖config.Time_zone。” 关键术语为“显示目的”和“根据请求” 在我的机器上,这是真的。然而,在生产方

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

有人能告诉我如何在Rails2.3(.5或.8)中使用用户时区吗

我所看到的详细介绍用户时区应该如何工作的最全面的文章在这里:。。。虽然还不清楚这是什么时候写的,或者是针对什么版本的rails。具体而言,它指出:

“Time.zone—实际用于显示目的的时区。可以手动设置此时区,以根据每个请求覆盖config.Time_zone。”

关键术语为“显示目的”和“根据请求”

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

我们在生产中使用passenger运行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
它们产生以下结果:
处理应用程序控制器测试(适用于2010-12-24 22:15:50的98.202.196.203)[GET]
时区#
无
2010年12月24日星期五22:15:50 UTC
2010年12月24日星期五22:00:00 UTC
2010年12月24日星期五22:00:00 UTC
2010年12月24日星期五22:00:00 UTC+00:00
2010年12月24日星期五22:00:00 UTC
2010年12月24日星期五22:00:00 UTC+00:00
在21毫秒内完成(视图:0,分贝:4)| 200正常[http://www.dealsthatmatter.com/test]
处理应用程序控制器测试2(适用于2010-12-24 22:15:53的98.202.196.203)[GET]
时区#
无
143ms内完成(视图:1,分贝:3)| 200正常[http://www.dealsthatmatter.com/test2]
处理应用程序控制器测试(适用于2010-12-24 22:15:59的98.202.196.203)[GET]
时区#
无
2010年12月24日星期五22:15:59 UTC
2010年12月24日星期五22:00:00 UTC
2010年12月24日星期五22:00:00 UTC
2010年12月24日星期五15:00:00 MST-07:00
2010年12月24日星期五22:00:00 UTC
2010年12月24日星期五15:00:00 MST-07:00
在20毫秒内完成(视图:0,分贝:4)| 200正常[http://www.dealsthatmatter.com/test]
处理应用程序控制器测试3(适用于2010-12-24 22:16:03的98.202.196.203)[GET]
时区#
无
在17毫秒内完成(视图:0,分贝:2)| 200正常[http://www.dealsthatmatter.com/test3]
处理应用程序控制器测试(适用于2010-12-24 22:16:04时的98.202.196.203)[GET]
时区#
无
2010年12月24日星期五22:16:05 UTC
2010年12月24日星期五22:00:00 UTC
2010年12月24日星期五22:00:00 UTC
2010年12月24日星期五22:00:00 UTC+00:00
2010年12月24日星期五22:00:00 UTC
2010年12月24日星期五22:00:00 UTC+00:00
在151ms内完成(视图:0,分贝:4)| 200正常[http://www.dealsthatmatter.com/test]

上面应该很清楚,第二次呼叫/测试显示时区设置为Mountain,尽管它不应该


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

我在构建支持票务系统时遇到了这个问题。我的解决办法如下

将Datetime SQL和ActiveRecord时区设置为UTC

在应用程序的配置中设置所需的时间格式

制作一个CSS类来描述你想要的日期CSS的外观。使名称唯一。例如UTCDate

编写javascript在页面中搜索命名日期DOM类(上面的UTCDate)。由于Javascript由最终用户执行,因此它将用本地计算机设置的值替换该值。如果出现解析错误,请将时间保留为UTC。看


此解决方案的优点是,你的应用程序不像许多论坛那样有愚蠢的时区选择器。它还减少了服务器负载和代码库大小(因为您不必存储或处理有关客户端时区的任何信息),以处理一项重要但困难的任务。

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

在Rails解决这个问题之前,我们选择手动处理时区,这可以通过未记录的默认方法访问:

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

您是正确的,Rails不会根据请求自动重置时区。wiki作者给出了一个例子,其中时区是在before过滤器中设置的,因此他从未遇到请求之间时区“泄漏”的问题(因为时区是在请求之前正确设置的)。RDoc文档中使用的
TimeZone.zone=
示例类似。所以我认为这是一个文档问题

更改时区不是本地请求,而是本地线程。Rails将所选时区存储在当前线程中(请参见
thread.current.[:time\u zone]
),而不是当前请求中。由于同一线程处理多个请求,因此对Time.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

我还没有时间查看您的ActiveRecord问题。当我完成更新后,将返回更新。

同意-rails 2.3*中的时区被打破*已经在ar上咬了我一口
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