ActiveSupport :: Duration вычисляет свое значение, используя следующие константы и алгоритм (я добавил объяснение того, что он делает ниже, но здесь есть ссылка на источник). Как вы можете видеть ниже, константа SECONDS_PER_YEAR
- это среднее число секунд в григорианском календаре (которое затем используется для определения SECONDS_PER_MONTH
). Именно из-за этого «среднего определения» SECONDS_PER_YEAR и SECONDS_PER_MONTH вы получаете неожиданные часы, минуты и секунды. Он определяется как среднее, потому что месяц и год не являются стандартным фиксированным периодом времени.
SECONDS_PER_MINUTE = 60
SECONDS_PER_HOUR = 3600
SECONDS_PER_DAY = 86400
SECONDS_PER_WEEK = 604800
SECONDS_PER_MONTH = 2629746 # This is 1/12 of a Gregorian year
SECONDS_PER_YEAR = 31556952 # The length of a Gregorian year = 365.2425 days
# You pass ActiveSupport::Duration the number of seconds (b-a) = 9255600.0 seconds
remainder_seconds = 9255600.0
# Figure out how many years fit into the seconds using integer division.
years = (remainder_seconds/SECONDS_PER_YEAR).to_i # => 0
# Subtract the amount of years from the remaining_seconds
remainder_seconds -= years * SECONDS_PER_YEAR # => 9255600.0
months = (remainder_seconds/SECONDS_PER_MONTH).to_i # => 3
remainder_seconds -= months * SECONDS_PER_MONTH # => 1366362.0
weeks = (remainder_seconds/SECONDS_PER_WEEK).to_i # => 2
remainder_seconds -= weeks * SECONDS_PER_WEEK # => 156762.0
days = (remainder_seconds/SECONDS_PER_DAY).to_i # => 1
remainder_seconds -= days * SECONDS_PER_DAY # => 70362.0
hours = (remainder_seconds/SECONDS_PER_HOUR).to_i # => 19
remainder_seconds -= hours * SECONDS_PER_HOUR # => 1962.0
minutes = (remainder_seconds/SECONDS_PER_MINUTE).to_i # => 32
remainder_seconds -= minutes * SECONDS_PER_MINUTE # => 42
seconds = remainder_seconds # => 42
puts "#{years} years, #{months} months, #{weeks} weeks, #{days} days, #{hours} hours, #{minutes} minutes, #{seconds} seconds"
# 0 years, 3 months, 2 weeks, 1 days, 19 hours, 32 minutes, 42.0 seconds
Чтобы избежать возникшей проблемы, я бы предложил просто представить время в неделе, днях, часы, минуты и секунды (в основном все, кроме месяца и года).
Количество секунд в месяце будет сложным, если вы не используете среднее значение, поскольку вам нужно будет учитывать 28, 29, 30 и 31 день для каждого отдельного месяца. Точно так же за год вам нужно будет учитывать скачок / не-скачок, если вы не используете среднее значение.
Я не уверен ни в каких драгоценных камнях вокруг, которые делают это для вас, однако я могу предоставить функция, которая может помочь вам рассчитать продолжительность в днях, часах, минутах и секундах ниже.
def duration_in_whms(seconds)
parts_and_seconds_in_part = {:weeks => 604800, :days => 86400, :hours => 3600, :minutes => 60}
result = {}
remainder = seconds
parts_and_seconds_in_part.each do |k, v|
result[k] = (remainder/v).to_i
remainder -= result[k]*v
end
result.merge(seconds: remainder)
end
duration_in_whms(9255600) => # {:weeks=>15, :days=>2, :hours=>3, :minutes=>0, :seconds=>0.0}