Rails times oddness: "x days from now"
когда пользователи регистрируются на одном из моих сайтов для бесплатной пробной версии, я устанавливаю, что их срок действия будет "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
Итак, теперь я снова запутался. Как теперь плюс плюс два дня в секундах, минус сейчас, минус две дня в секундах, составляющая час в секундах? Где находится лишний час?
Ответы
Ответ 1
Как 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, в день истечения срока действия независимо от фактического времени регистрации.
Ответ 2
Вы можете использовать класс 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
Ответ 3
Используйте since
, например:
14.days.since.to_date