Ruby-on-rails Rails 时间奇怪:“x 天后”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4050991/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:30:24  来源:igfitidea点击:

Rails times oddness : "x days from now"

ruby-on-railsrubytimezoneactivesupport

提问by Max Williams

when users sign up to one of my sites for a free trial, i set their account expiry to be "14.days.from_now". Then on the home page i show how many days they have remaining, which i get with:

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

(user.trial_expires - Time.now)/86400 

(because there are 86400 seconds in a day, ie 60 * 60 * 24)

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

The funny thing is, this comes out as more than 14, so gets rounded up to 15. On closer investigation in the console this happens for just two days in the future (if you know what i mean). eg

有趣的是,结果超过 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?

I thought maybe it was to do with timezones - i noticed that the time from 1.day from now was in BST and the time 2 days from now was in GMT. So, i tried using localtime and got the same results!

我想也许这与时区有关 - 我注意到从现在起 1.day 的时间是 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

I then wondered how big the difference is, and it turns out that it isexactly an hour out. So it looks like some time zone weirdness, or at least something to do with time zones that i don't understand. Currently my time zone is BST (british summer time) which is one hour later than UTC at the moment (till this sunday at which point it reverts to the same as UTC).

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

The extra hour seems to be introduced when i add two days to Time.now: check this out. I start with Time.now, add two days to it, subtract Time.now, then subtract two days of seconds from the result, and am left with an hour.

当我将两天添加到 Time.now 时,似乎引入了额外的一小时:看看这个。我从 Time.now 开始,加上两天,减去 Time.now,然后从结果中减去两天的秒数,剩下一个小时。

It just occurred to me, in a head slapping moment, that this is occurring BECAUSE the clocks go back on sunday morning: ie at 11.20 on sunday morning it willbe two days AND an extra hour from now. I was about to delete all of this post, but then i noticed this: i thought 'ah, i can fix this by using (24*daynum).hours instead of daynum.days, but i still get the same result: even when i use seconds!

我突然想到,在一个令人头疼的时刻,这是因为时钟在星期天早上倒退:即在星期天早上 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

So now i'm confused again. How can now plus two days worth of seconds, minus now, minus two days worth of seconds be an hour worth of seconds? Where does the extra hour sneak in?

所以现在我又糊涂了。现在加上两天的秒数,减去现在,减去两天的秒数怎么会是一小时的秒数?额外的时间从哪里偷偷溜进来的?

回答by mikej

As willcodejavaforfoodhas commented, this is due to daylight saving time which ends this weekend.

正如willcodejavaforfood所评论的那样,这是由于本周末结束的夏令时。

When adding a duration ActiveSupport has some code in it to compensate for if the starting time is in DST and the resulting time isn't (or vice versa).

当添加持续时间时,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

If you have 11:09:27 and add a number of days you will still get 11:09:27 on the resulting day even if the DST has changed. This results in an extra hour when you come to do calculations in seconds.

如果您有 11:09:27 并添加天数,即使 DST 已更改,您仍然会在结果日获得 11:09:27。当您在几秒钟内进行计算时,这会导致额外的一个小时。

A couple of ideas:

几个想法:

  • Use the distance_of_time_in_wordshelper method to give the user an indication of how long is left in their trial.
  • Calculate the expiry as Time.now + (14 * 86400)instead of using 14.days.from_now- but some users might claim that they have lost an hour of their trial.
  • Set trials to expire at 23:59:59 on the expiry day regardless of the actual signup time.
  • 使用distance_of_time_in_words辅助方法向用户指示他们的试用期还剩多长时间。
  • 将到期时间计算为Time.now + (14 * 86400)而不是使用14.days.from_now- 但一些用户可能会声称他们已经失去了一个小时的试用期。
  • 无论实际注册时间如何,将试用设置为在到期日的 23:59:59 到期。

回答by Jonas Elfstr?m

You could use the Dateclass to calculate the number of days between today and the expire date.

您可以使用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

回答by Roberto Capelo

Use since, example: 14.days.since.to_date

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