Рельсы времен странности: "х дней отныне" - PullRequest
17 голосов
/ 29 октября 2010

когда пользователи регистрируются на одном из моих сайтов для бесплатной пробной версии, я определяю срок действия их учетной записи как "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.day по настоящее время было в 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

Так что теперь я снова запутался.Как теперь плюс плюс два дня секунд, минус сейчас, минус два дня секунд может быть часом секунд?Куда уходит дополнительный час?

Ответы [ 3 ]

19 голосов
/ 29 октября 2010

Как прокомментировал 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 и вы добавите количество дней, вы все равно получите 11:09:27 в следующий день, даже если DST изменился. Это приводит к дополнительному часу, когда вы приходите делать вычисления в секундах.

Пара идей:

  • Используйте вспомогательный метод distance_of_time_in_words, чтобы дать пользователю представление о том, сколько времени осталось в его испытании.
  • Рассчитайте срок действия как Time.now + (14 * 86400) вместо использования 14.days.from_now - но некоторые пользователи могут утверждать, что они потеряли час своего испытания.
  • Установить срок действия проб в 23:59:59 в день истечения независимо от фактического времени регистрации.
8 голосов
/ 29 октября 2010

Вы можете использовать класс Дата для вычисления количества дней между сегодняшним днем ​​и датой истечения срока действия.

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
5 голосов
/ 18 августа 2014

Используйте since, пример: 14.days.since.to_date

...