Может ли Ruby легко распечатать временную разницу (продолжительность)?
Может ли Ruby сделать что-то вроде этого?
irb(main):001:0> start = Time.now
=> Thu Nov 05 01:02:54 -0800 2009
irb(main):002:0> Time.now - start
=> 25.239
irb(main):003:0> (Time.now - start).duration
=> "25 seconds"
(метод продолжительности не существует)... и аналогично, отчет
23 minutes and 35 seconds
1 hour and 33 minutes
2 days and 3 hours
(либо сообщать всю продолжительность, сколько секунд или сообщать до двух цифр и единиц (если сообщается день и час, то не нужно указывать, сколько минут))
Ответы
Ответ 1
Вот быстрый и простой способ реализовать это. Установите предопределенные измерения в секундах, минутах, часах и днях. Затем, в зависимости от размера номера, выведите соответствующую строку с этими единицами. Мы расширим Numeric
, чтобы вы могли вызывать метод для любого числового класса (Fixnum
, Bignum
или в вашем случае Float
).
class Numeric
def duration
secs = self.to_int
mins = secs / 60
hours = mins / 60
days = hours / 24
if days > 0
"#{days} days and #{hours % 24} hours"
elsif hours > 0
"#{hours} hours and #{mins % 60} minutes"
elsif mins > 0
"#{mins} minutes and #{secs % 60} seconds"
elsif secs >= 0
"#{secs} seconds"
end
end
end
Ответ 2
Посмотрите на метод Rails DateHelper.distance_of_time_in_words. Это даст вам отличное стартовое место. Несмотря на то, что он загружен магическими числами, подход должен работать для вас.
Ответ 3
Существует драгоценный камень https://rubygems.org/gems/time_diff
Что дает разницу в хеше
Ответ 4
Попробуйте рубиновый камень для этого https://rubygems.org/gems/time_difference - драгоценный камень разницы по времени для Ruby
Документация на https://github.com/tmlee/time_difference
start_time = Time.new(2013,1)
end_time = Time.new(2014,1)
TimeDifference.between(start_time, end_time).in_years
=> 1.0
Ответ 5
Разница во времени довольно печатная строка:
class Numeric
def duration
rest, secs = self.divmod( 60 ) # self is the time difference t2 - t1
rest, mins = rest.divmod( 60 )
days, hours = rest.divmod( 24 )
# the above can be factored out as:
# days, hours, mins, secs = self.duration_as_arr
#
# this is not so great, because it could include zero values:
# self.duration_as_arr.zip ['Days','Hours','Minutes','Seconds']).flatten.join ' '
result = []
result << "#{days} Days" if days > 0
result << "#{hours} Hours" if hours > 0
result << "#{mins} Minutes" if mins > 0
result << "#{secs} Seconds" if secs > 0
return result.join(' ')
end
end
Разница во времени как массив:
class Numeric
def duration_as_arr
rest, secs = self.divmod( 60 )
rest, mins = rest.divmod( 60 )
days, hours = rest.divmod( 24 )
[days, hours, mins, secs]
end
end
Пример:
x = 1209801.079257
x.duration
=> "14 Days 3 Minutes 21.079257000004873 Seconds"
x.duration_as_arr
=> [14, 0, 3, 21.079257000004873]
Ответ 6
Основываясь на Майкл Ричард ответьте, замените блок if
, который правильно владеет английским плюрализацией, и не будет говорить такие вещи, как "14 дней и 0 часы":
if days > 0
hour_remainder = hours % 24
if hour_remainder > 0
hour_str = hour_remainder == 1 ? 'hour' : 'hours'
"#{days} days and #{hour_remainder} #{hour_str}"
elsif days == 1
"#{days} day"
else
"#{days} days"
end
elsif hours > 0
min_remainder = mins % 60
if min_remainder > 0
min_str = min_remainder == 1 ? 'minute' : 'minutes'
"#{hours} hours and #{min_remainder} #{min_str}"
elsif hours == 1
"#{hours} hour"
else
"#{hours} hours"
end
elsif mins > 0
sec_remainder = secs % 60
if sec_remainder > 0
sec_str = sec_remainder == 1 ? 'second' : 'seconds'
"#{mins} minutes and #{sec_remainder} #{sec_str}"
elsif minutes == 1
"#{mins} minute"
else
"#{mins} minutes"
end
elsif secs == 1
"#{secs} second"
elsif secs >= 0
"#{secs} seconds"
end
Ответ 7
Я не мог выдержать здесь общее решение - хотя: есть год 365 дней?
Дополнительно я добавляю abs
при преобразовании self.to_int
class Numeric
def duration
steps=[60, 60, 24, 365,0]
names=[:seconds, :minutes, :hours, :days, :years]
results=[]
stepper = self.to_int.abs
steps.each { |div|
if stepper>0
if div>0
results<<stepper % div
stepper/=div
else
results << stepper
end
end
}
e= results.empty? ? 0 : results.count-1
mt= e>0 ? results[e-1] : 0
et=results[e] || 0
et.to_s+" "+names[e].to_s + (mt>0 ? " "+mt.to_s+" "+names[e-1].to_s : '')
end
end
и с переводом
class Numeric
def i18n_duration
steps=[60, 60, 24, 365,0]
names=[:seconds, :minutes, :hours, :days, :years]
results=[]
stepper = self.to_int.abs
steps.each { |div|
if stepper>0
if div>0
results<<stepper % div
stepper/=div
else
results << stepper
end
end
}
e= results.empty? ? 0 : results.count-1
mt= e>0 ? results[e-1] : 0
et=results[e] || 0
I18n.t("datetime.distance_in_words.x_#{names[e]}", count: et) +
(mt>0 ? " "+I18n.t("datetime.distance_in_words.x_#{names[e-1]}", count: mt):'')
end
end
Ответ 8
В качестве альтернативы вы можете сделать это:
start = DateTime.now.strftime('%Q').to_i / 1000.0
#=> 1409163050.088
sleep 3.5
#=> 3
now_ms = DateTime.now.strftime('%Q').to_i / 1000.0
#=> 1409163053.606
'%.3f' % (now_ms - start)
#=> "3.518"
Ответ 9
Я дал альтернативную реализацию для этого при использовании в журналах script здесь [скопировано оттуда]:
Как создать человеческий читаемый диапазон времени с использованием рубинов на рельсах
Если вы хотите показать значительную продолжительность в диапазоне от секунд до нескольких дней, альтернативой будет (поскольку она не должна выполнять лучшее):
def human_duration(secs, significant_only = true)
n = secs.round
parts = [60, 60, 24, 0].map{|d| next n if d.zero?; n, r = n.divmod d; r}.
reverse.zip(%w(d h m s)).drop_while{|n, u| n.zero? }
if significant_only
parts = parts[0..1] # no rounding, sorry
parts << '0' if parts.empty?
end
parts.flatten.join
end
start = Time.now
# perform job
puts "Elapsed time: #{human_duration(Time.now - start)}"
human_duration(0.3) == '0'
human_duration(0.5) == '1s'
human_duration(60) == '1m0s'
human_duration(4200) == '1h10m'
human_duration(3600*24) == '1d0h'
human_duration(3600*24 + 3*60*60) == '1d3h'
human_duration(3600*24 + 3*60*60 + 59*60) == '1d3h' # simple code, doesn't round
human_duration(3600*24 + 3*60*60 + 59*60, false) == '1d3h59m0s'
В качестве альтернативы вам может быть интересно только снять часть секунд, когда это не имеет значения (также демонстрирует другой подход):
def human_duration(duration_in_seconds)
n = duration_in_seconds.round
parts = []
[60, 60, 24].each{|d| n, r = n.divmod d; parts << r; break if n.zero?}
parts << n unless n.zero?
pairs = parts.reverse.zip(%w(d h m s)[-parts.size..-1])
pairs.pop if pairs.size > 2 # do not report seconds when irrelevant
pairs.flatten.join
end
Надеюсь, что это поможет.
Ответ 10
Я пришел с этим решением
def formate_duration(started, ended)
differences = ["secs", "mins", "hours", "days"].reduce([[nil, [(ended - started).round, nil]]]) do |acc, unit|
mod = unit == "hours" ? 24 : 60
# will return [ over, unit_value ]
# over is than used for calculation in next step
# accumulator is in format [["unit" [ over, value]]] and we are interesting in latest over in each step
# => This is why main diff is in this place in accumulator
entry = acc[acc.size-1][1][0].divmod(mod)
acc << [ unit, entry ]
end
# this will do string conversion and reverse in one step
str = differences.drop(1).reduce("") do |acc, current|
if (current[1][1] > 0)
"#{current[1][1]} #{current[0]} #{acc}"
else acc end
end
str.empty? ? "now" : str
end
Помните, что это не будет работать для различий, превышающих 60 дней (в этом случае вам нужно будет добавить условие)
Ответ 11
time_difference = current_time - old_time
def seconds_fraction_to_time(time_difference)
days = hours = mins = 0
mins = (seconds / 60).to_i
seconds = (seconds % 60 ).to_i
hours = (mins / 60).to_i
mins = (mins % 60).to_i
days = (hours / 24).to_i
hours = (hours % 24).to_i
return [days,hours,mins,seconds]
end
тогда вы можете распечатать его, какой бы вы ни хотели,
i.e
if(days > 0)
return "#{days} Days #{hours} Hours"
else
return "#{hours} Hours #{mins} Minutes"
end