Rails / MySQL:不同的用户时区

时间:2013-07-25 12:40:03

标签: ruby-on-rails cron timezone

问题1 - 我在德克萨斯州达拉斯的共享主机上有一个应用...所以我的数据库/网络服务器目前设置为美国中部时间...我无法更改此设置,因此UTC已关闭桌子。

我的应用程序记录并向用户显示日期和时间数据,但每个用户可能位于不同的时区。

如果我配置我的应用(config.time_zone = 'Central Time (US & Canada)'),但我允许用户选择他们的时区,

def user_time_zone(&block)
  Time.use_zone(current_user.time_zone, &block)
end

RoR会在用户选择的时区中显示任何返回的数据吗?或者我需要评估数据并在显示之前对其进行相应修改?

问题1.1 - 我有相关邮件,每天/每周/每月向用户发送报告。如果我使用CRON运行这些,我是否需要在不同的时间间隔安排我的工作以允许时区?换句话说,如果一个作业包含

when 'daily'
   @dates = params[:f]['Date Range'] = "#{Date.current} - #{Date.current}"

Date.current将在东海岸提前1小时,在西方提前3小时,依此类推。那么(仍然使用Central的服务器时区),我会在晚上11点(东部)和凌晨2点(西部)运行一个吗?

非常感谢任何建议/协助。谢谢!

2 个答案:

答案 0 :(得分:2)

为了获得最佳效果,应编写应用程序,使其运行的计算机的时区不会影响任何数据或行为。

这通常意味着在您的代码中使用UTC。例如,Time.now.utc而非Time.now。有关RoR的详细信息,请参阅this post

最佳做法是尽可能将服务器时区设置为UTC,但这并不意味着您应该依赖代码中的那个。

关于你的第二个问题,请记住,每个时区都有自己的“日”概念。有关可视化,请参阅this site

您应该在您将数据对齐的每个时区的午夜运行单独的作业。某些应用程序(例如StackExchange网站)对所有用户使用单个时区(通常为UTC)。其他应用程序根据自己的时区调整每个用户的数据。您需要确定适合您的应用程序的内容。

在安排作业时,确定每个时区的午夜对齐的UTC时间。然后安排一段时间的工作。确保您的调度程序了解它是UTC。不要尝试将其调整到服务器的本地时区,否则当本地时区经历夏令时转换时可能会引入错误。

确保计划作业不仅在正确的时间运行,而且报告查询使用正确的时区来确定应该运行的UTC范围。

请注意,这确实意味着来自不同时区的两位用户报告相同的数据可能最终会产生不同的结果 - 因为他们的每个“天”的时间段略有不同。

答案 1 :(得分:2)

Matt非常明确的回答。您肯定希望将您的时间数据保存在UTC TZ中,然后将其转换为您的用户TZ(或客户用户的服务器TZ)

您使用MySQL全局设置为非UTC TZ,您可以使用以下查询覆盖此设置

SET time_zone = 'UTC';

问题是:此命令在MySQL中是会话范围的,每次初始化与数据库的新连接时,都必须再次设置它。 Rails ActiveRecord :: ConnectionAdapters :: ConnectionPool类可以帮助您。请参阅此答案:How do I execute queries upon DB connection in Rails?

接下来,您需要在application.rb中设置服务器默认TZ(您这样做了)并设置每个用户拥有自己的TZ(并且您也这样做了)

最后,每次访问时间数据时都必须遵循Rails约定。我找到了这篇文章,其中包含了非常有用的信息,它将回答您的所有问题:http://www.elabs.se/blog/36-working-with-time-zones-in-ruby-on-rails

摘自该文章:

<强>下放

2.hours.ago # => Fri, 02 Mar 2012 20:04:47 JST +09:00
1.day.from_now # => Fri, 03 Mar 2012 22:04:47 JST +09:00
Date.today.to_time_in_current_zone # => Fri, 02 Mar 2012 22:04:47 JST +09:00
Date.current # => Fri, 02 Mar
Time.zone.parse("2012-03-02 16:05:37") # => Fri, 02 Mar 2012 16:05:37 JST +09:00
Time.zone.now # => Fri, 02 Mar 2012 22:04:47 JST +09:00
Time.current # Same thing but shorter. (Thank you Lukas Sarnacki pointing this out.)
Time.zone.today # If you really can't have a Time or DateTime for some reason
Time.zone.now.utc.iso8601 # When supliyng an API (you can actually skip .zone here, but I find it better to always use it, than miss it when it's needed)
Time.strptime(time_string, '%Y-%m-%dT%H:%M:%S%z').in_time_zone(Time.zone) # If you can't use Time#parse

<强>不该做什么

Time.now # => Returns system time and ignores your configured time zone.
Time.parse("2012-03-02 16:05:37") # => Will assume time string given is in the system's time zone.
Time.strptime(time_string, '%Y-%m-%dT%H:%M:%S%z') # Same problem as with Time#parse.
Date.today # This could be yesterday or tomorrow depending on the machine's time zone.
Date.today.to_time # => # Still not the configured time zone.