17

当用户注册我的一个网站进行免费试用时,我将他们的帐户到期时间设置为“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。所以,我尝试使用本地时间并得到相同的结果!

>> 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

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

4

3 回答 3

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 到期,无论实际注册时间如何。
于 2010-10-29T10:53:24.050 回答
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
于 2010-10-29T12:36:43.047 回答
5

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

于 2014-08-18T18:35:20.280 回答