Rails时间奇怪:“从现在起x天”

时间:2010-10-29 10:30:03

标签: ruby-on-rails ruby timezone activesupport

当用户注册我的某个网站进行免费试用时,我将其帐户到期时间设置为“14.days.from_now”。然后在主页上显示他们剩下多少天,我得到了:

(user.trial_expires - Time.now)/86400 

(因为一天有86400秒,即60 * 60 * 24)

有趣的是,这个数字超过了14,所以最多可以调整到15个。在控制台中进行更仔细的调查,这将在未来发生两天(如果你知道我的意思)。例如

>> Time.now
=> Fri Oct 29 11:09:26 0100 2010
>> future_1_day = 1.day.from_now
=> Sat, 30 Oct 2010 11:09:27 BST 01:00
#ten past eleven tomorrow

>> (future_1_day - Time.now)/86400
=> 0.999782301526931
#less than 1, what you'd expect right?

>> future_2_day = 2.day.from_now
=> Sun, 31 Oct 2010 11:09:52 GMT 00:00
>> (future_2_day - Time.now)/86400
=> 2.04162248861183
#greater than 2 - why?

我想也许这与时区有关 - 我注意到从现在起1天的时间是在BST,从现在起2天的时间是在GMT。所以,我尝试使用localtime并得到了相同的结果!

>> future_2_day = 2.day.from_now.localtime
=> Sun Oct 31 11:11:24 0000 2010
>> (future_2_day - Time.now)/86400
=> 2.04160829127315
>> (future_2_day - Time.now.localtime)/86400
=> 2.04058651585648

然后我想知道差异有多大,而且事实证明它 正好一小时了。所以它看起来像一些时区怪异,或者至少与我不理解的时区有关。目前我所在的时区是BST(英国夏令时),此时比UTC晚一个小时(直到这个星期日,它恢复到与UTC相同的时间)。

当我向Time.now添加两天时,似乎会引入额外的小时:检查一下。我从Time.now开始,向它添加两天,减去Time.now,然后从结果中减去两天的秒数,并留下一个小时。

我突然想到,这是在发生这种情况,因为钟表会在星期天早上回归:即星期天早上11点20分两天一小时现在起。我即将删除所有这篇文章,但后来我注意到了这一点:我想'啊,我可以通过使用(24 * daynum).hours而不是daynum.days来解决这个问题,但我仍然得到相同的结果:即使是我用秒!

>> (Time.now + (2*24).hours - Time.now) - 86400*2
=> 3599.99969500001
>> (Time.now + (2*24*3600).seconds - Time.now) - 86400*2
=> 3599.999855

所以现在我又困惑了。现在怎么能加上两天的价值,减去现在,减去两天的价值是一小时的秒数?额外的小时在哪里潜入?

3 个答案:

答案 0 :(得分:19)

正如willcodejavaforfood所述,这是由于夏令时在本周末结束。

添加持续时间时,ActiveSupport中包含一些代码,以补偿开始时间是否为DST且结果时间是否为(反之亦然)。

def since(seconds)
  f = seconds.since(self)
  if ActiveSupport::Duration === seconds
    f
  else
    initial_dst = self.dst? ? 1 : 0
    final_dst   = f.dst? ? 1 : 0
    (seconds.abs >= 86400 && initial_dst != final_dst) ? f + (initial_dst - final_dst).hours : f
  end
rescue
  self.to_datetime.since(seconds)
end

如果您有11:09:27并且添加了几天,即使DST已经更改,您在结果日仍然会得到11:09:27。当你在几秒钟内完成计算时,这会导致额外的一小时。

一些想法:

  • 使用distance_of_time_in_words帮助程序方法向用户提供试用版中剩余时间的指示。
  • 将到期时间计算为Time.now + (14 * 86400)而非使用14.days.from_now - 但有些用户可能会声称他们已经失去了一小时的审判时间。
  • 将试用期限定为截止日的23:59:59,无论实际注册时间如何。

答案 1 :(得分:8)

您可以使用Date课程来计算今天和过期日期之间的天数。

expire_date = Date.new(user.trial_expires.year, user.trial_expires.month, user.trial_expires.day)
days_until_expiration = (expire_date - Date.today).to_i

答案 2 :(得分:5)

使用since,示例: 14.days.since.to_date