我有一个使用以下配置的应用程序:
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小时。
由于
答案 0 :(得分:3)
将所有内容保留在UTC中并退出差异。
当表单提交字段并连接DateTime对象时,它将被解释为UTC ... ,因此您必须在提交信息后进行转换。
# 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时区的更多详细信息:
答案 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处理不同时区相关的问题。