Ruby on rails RubyonRails时区奇怪的行为

Ruby on rails RubyonRails时区奇怪的行为,ruby-on-rails,ruby,timezone,Ruby On Rails,Ruby,Timezone,当和持续时间时,存在属性为的任务模型 create_table "tasks", force: true do |t| ... t.datetime "when" t.integer "duration" ... end 我编写了检查任务是否处于活动状态的方法,以便在第页上显示它。 这是一种主动方法: def active? if (self.when + self.duration) > Time.now true end end

当和持续时间时,存在属性为
的任务模型

create_table "tasks", force: true do |t|
  ...
  t.datetime "when"
  t.integer  "duration"
  ...
end
我编写了检查任务是否处于活动状态的方法,以便在第页上显示它。 这是一种主动方法:

  def active?
    if (self.when + self.duration) > Time.now
      true
    end
  end
我尝试在控制台中检查对象:

t.when + t.duration
=> Sun, 08 Sep 2013 01:01:00 UTC +00:00
DateTime.now
=> Sun, 08 Sep 2013 01:57:13 +0200
t.active?
=> true
这是
真的
,但我输入了
持续时间的1:00和1分钟
,我希望它不应该是真的


当数据库中的
列未保存在正确的时区时,
似乎会给出错误的结果。如何解决此问题?

ActiveRecord默认情况下以UTC格式存储时间戳。有关更改默认时区的信息,请参见

您也可以使用Time#in_Time_zone将t转换为您的时区,请参阅

当数据库中的列未保存在正确的时区时

1)Rails在将时间插入数据库之前自动将时间转换为UTC时间(这是一件好事),这意味着时间的偏移量为“+0000”。这意味着,如果您在数据库中保存了8pm的时间,并且您的服务器位于偏移量为“+0600”的时区,那么等效的UTC时间是2pm,因此2pm会保存在数据库中。换句话说,本地服务器的时间比UTC时间早6小时,这意味着当服务器时区为晚上8点时,UTC时区为下午2点

2)在比较日期时,ruby会考虑时区偏移量——换句话说,ruby会将所有时间转换为同一时区,然后比较时间。以下是一个例子:

2.0.0p247 :086 > x = DateTime.strptime('28-01-2013 08:00:00 PM +6', '%d-%m-%Y %I:%M:%S %p %z')
 => Mon, 28 Jan 2013 20:00:00 +0600 
2.0.0p247 :087 > y = DateTime.strptime('28-01-2013 08:20:00 PM +7', '%d-%m-%Y %I:%M:%S %p %z')
 => Mon, 28 Jan 2013 20:20:00 +0700 
2.0.0p247 :088 > x < y
 => false 
这就是ruby在比较Time.now和
task.when+task.duration之前所做的

4)您可能会发现使用以下方法创建具有所需时间的DateTime对象更方便:

DateTime.strptime('28-01-2013 08:00:00 PM +0', '%d-%m-%Y %I:%M:%S %p %z'
由于可以将偏移量指定为零,因此不必创建预期转换为UTC时间的时间

或者可以使用change()方法,该方法会导致偏移量()发生更改,而不转换时间:

2.0.0p247 :011 > x = DateTime.now
 => Sun, 08 Sep 2013 00:34:08 +0600 
2.0.0p247 :012 > x.change offset: "+0000"
 => Sun, 08 Sep 2013 00:34:08 +0000 

您如何在
持续时间中存储“1分钟”
?我展示了与这些字段相关的迁移,它存储在整数中,所以是60。当您将
60
添加到
DateTime
时会发生什么?只需在控制台中快速执行
DateTime.now+60
,查看
+60
对时间戳的影响。
[7]pry(main)>DateTime.now=>Sun,2013年9月8日19:52:51+0200[8]pry(main)>DateTime.now+60=>2013年11月7日星期四19:52:59+0200
,但这与我的问题无关。查看:
[9]pry(main)>Task.last.when=>Sun,2013年9月8日16:58:00 UTC+00:00[10]pry(main)>Task.last.when+Task.last.duration=>Sun,2013年9月8日16:59:00 UTC+00:00
这是因为
列为
TimeWithZone
,请查看:
[11]pry(main)>Task.last.when.class=>ActiveSupport::TimeWithZone
您推荐什么解决方案?我想知道为什么默认情况下它不工作。我想知道,当我迁移到另一台服务器(例如Heroku)时,是否会更改默认时区,以确定它是否能正常工作。一切都正常,只是你不了解它是如何工作的。例如,您是否将1/8+1/4相加,得到2/8。不,在加法之前,必须将1/8和1/4转换为公分母,所以要做的是1/8+2/8=3/8。时代也是如此。在比较时间之前,rails将所有时间转换为一个公共分母UTC。但你在精神上并没有这样做。你有一个任务,比如说,开始时间为8pm,偏移量为“+0000”,你认为晚上9点偏移量为“+0200”的时间应该大于晚上8点的时间,但是你忽略了偏移量。要正确比较时间,你必须考虑偏移量,rails就是这样做的。写
Time.now.utc
真的有那么难吗?还是一样的。我不明白的是,当我试图保存21:00UTC+2时,它应该像19:00UTC+0一样保存在数据库中,但它不是这样保存的。它的值在数据库中为21:00UTC+0。
2.0.0p247 :011 > x = DateTime.now
 => Sun, 08 Sep 2013 00:34:08 +0600 
2.0.0p247 :012 > x.change offset: "+0000"
 => Sun, 08 Sep 2013 00:34:08 +0000