受到Rails中时区的困扰

时间:2017-05-15 19:01:00

标签: ruby-on-rails datetime

我有一个使用以下配置的应用程序:

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.
  config.time_zone = 'Amsterdam'
  config.active_record.default_timezone = :utc
end

我向用户提供一个表单,要求提供单独的日期(datepicker)和时间字段(下拉列表)。在之前的方法中,我将日期与&时间进入单个日期时间字段。看来,Time对象在某种程度上是UTC,而Date对象并不关心它。

将此存储到数据库中时,时区会以某种方式得到纠正,但时间为2小时。这是因为时间仍然是UTC吗?我怎样才能在某种程度上对日光节约进行补偿呢?

代码示例

表格:

= simple_form_for @report do |f|
  .row
    .col.s6
      = f.association :report_category
    .col.s6
      = f.association :account
  .row
    .col.s6.l4
      = f.input :day, as: :string, input_html: {class: 'datepicker current-date'}
    .col.s6.l4
      = f.input :time, as: :string, input_html: {class: 'timepicker current-date'}

模型中的回调

  def create_timestamp
    self.date = day.to_datetime + time.seconds_since_midnight.seconds
  end

保存后,我在表单中选择的时间差异为2小时。

这就是创建一条新记录后的样子,其中时间与创建记录的时间恰好相同,以查看差异:

 date: Sat, 20 May 2017 16:10:00 CEST +02:00,
 created_at: Sat, 20 May 2017 14:10:33 CEST +02:00,
 updated_at: Sat, 20 May 2017 14:10:33 CEST +02:00,
 deleted_at: nil,
 time: 2000-01-01 14:10:00 UTC,
 day: Sat, 20 May 2017,

如您所见,时间以UTC格式存储,而实际上是14:10 CET +0200,我创建了记录。所以这可能导致差异。您可以在日期中看到,其中时间距离created_at为+2小时。

由于

3 个答案:

答案 0 :(得分:3)

将所有内容保留在UTC中并退出差异。

当表单提交字段并连接DateTime对象时,它将被解释为UTC ... ,因此您必须在提交信息后进行转换。

Model.rb

    # Set the Proper DateTie
    def create_timestamp
        # Create the DateTime
        date = day.to_datetime + time.seconds_since_midnight.seconds
        # Set the timezone in which you want to interpret the time
        zone = 'Amsterdam'
        # Find the offset from UTC taking into account daylight savings
        offset = DateTime.now.in_time_zone(zone).utc_offset / 3600
        # Back out the difference to find the adjusted UTC time and save it as UTC with the correct time
        self.date = date - offset.hours
    end

答案 1 :(得分:1)

第1部分

默认情况下,时间戳以UTC格式存储,这可能是最好的方法。如果您从一个服务器环境移动到另一个服务器环境,您不希望所有时间都因为您切换时区而转移。

如果您想知道当地时区的时间戳,您只需要这样询问:

obj.updated_at.localtime

第2部分:

Ruby不会考虑Rails的时区配置。这意味着如果您的系统使用America / Sao_Paulo时区并且您的应用程序使用America / Los_Angeles,Ruby仍将考虑以前的配置。

Time.now.zone
#=> "BRST"

Time.zone = "America/Los_Angeles"
#=> "America/Los_Angeles"

Time.now.zone
#=> "BRST"

最好将应用程序范围的时区保留为UTC,而是允许每个用户设置自己的时区。

为用户添加时区属性

create_table :users do |t|
  t.string :time_zone, default: "UTC"
  ...
end

从表单中获取时区

<%= f.input :time_zone %>

然后我们可以使用around_action来设置用户的首选时区:

# app/controllers/application_controller.rb
around_action :set_time_zone, if: :current_user

private

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

我们将当前用户的时区传递给Time类的use_zone方法(由ActiveSupport添加的方法)。此方法需要将块传递给它并为该块的持续时间设置时区,以便在请求完成时,将原始时区设置回来。

<强> DOS:

获取当前时间:Time.zone.now

获取日期:Time.zone.today

时间戳的时间:Time.zone.at(timestamp)

解析时间:Time.zone.parse(str)

<强> DONT'S:

Time.now

DateTime.now

Date.today

Time.at(timestamp)

Time.parse(str)

使用Time.current代替Time.now

使用Date.current代替Date.today

查看此帖子,了解有关Rails时区的更多详细信息:

working-with-time-zones-in-ruby-on-rails

答案 2 :(得分:0)

据我所知,to_datetime方法正在使用系统时间,因此最终日期是使用您的系统时区创建的。

self.date = day.to_datetime + time.seconds_since_midnight.seconds

生成Sat, 20 May 2017 14:10:00 +0000。因为它已经在UTC中,所以它按原样存储在数据库中,稍后在Rails上添加2小时以在阿姆斯特丹时区显示它。

您可以通过避免to_datetime转化

来解决此问题
self.date = day + time.seconds_since_midnight.seconds

如果day是一个字符串,您可以

self.date = Time.zone.parse(day) + time.seconds_since_midnight.seconds

我还建议this article处理不同时区相关的问题。