次の出力を生成する最良の方法を見つけようとしています
<name> job took 30 seconds
<name> job took 1 minute and 20 seconds
<name> job took 30 minutes and 1 second
<name> job took 3 hours and 2 minutes
私はこのコードを始めました
def time_range_details
time = (self.created_at..self.updated_at).count
sync_time = case time
when 0..60 then "#{time} secs"
else "#{time/60} minunte(s) and #{time-min*60} seconds"
end
end
これを行うより効率的な方法はありますか?非常にシンプルなものには、冗長なコードがたくさんあるようです。
これの別の用途は次のとおりです。
<title> was posted 20 seconds ago
<title> was posted 2 hours ago
このコードは似ていますが、代わりにTime.nowを使用しています。
def time_since_posted
time = (self.created_at..Time.now).count
...
...
end
distance_of_time_in_words
よりも「正確」なものが必要な場合は、次のように記述できます。
def humanize secs
[[60, :seconds], [60, :minutes], [24, :hours], [Float::INFINITY, :days]].map{ |count, name|
if secs > 0
secs, n = secs.divmod(count)
"#{n.to_i} #{name}" unless n.to_i==0
end
}.compact.reverse.join(' ')
end
p humanize 1234
#=>"20 minutes 34 seconds"
p humanize 12345
#=>"3 hours 25 minutes 45 seconds"
p humanize 123456
#=>"1 days 10 hours 17 minutes 36 seconds"
p humanize(Time.now - Time.local(2010,11,5))
#=>"4 days 18 hours 24 minutes 7 seconds"
ああ、あなたのコードに関する一つのコメント:
(self.created_at..self.updated_at).count
本当に違いを得るのに悪い方法です。単純に使用:
self.updated_at - self.created_at
DateHelper
には2つのメソッドがあり、必要なものが提供される可能性があります。
time_ago_in_words( 1234.seconds.from_now ) #=> "21 minutes"
time_ago_in_words( 12345.seconds.ago ) #=> "about 3 hours"
distance_of_time_in_words( Time.now, 1234.seconds.from_now ) #=> "21 minutes"
distance_of_time_in_words( Time.now, 12345.seconds.ago ) #=> "about 3 hours"
chronic_duration 数値時間を解析して読み取り可能に、またはその逆に
秒から日までの範囲で重要な期間を表示したい場合は、代わりの方法を使用できます(最高のパフォーマンスを発揮する必要がないため)。
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
お役に立てば幸いです。
Railsにはビュー用のDateHelper
があります。それがあなたが望んでいるものと完全に一致しない場合は、独自に記述する必要があるかもしれません。
@MladenJablanovićは良いサンプルコードで答えを持っています。ただし、サンプルのヒューマナイズメソッドを引き続きカスタマイズしてもかまわない場合は、これが出発点として適している可能性があります。
def humanized_array_secs(sec)
[[60, 'minutes '], [60, 'hours '], [24, 'days ']].inject([[sec, 'seconds']]) do |ary, (count, next_name)|
div, prev_name = ary.pop
quot, remain = div.divmod(count)
ary.Push([remain, prev_name])
ary.Push([quot, next_name])
ary
end.reverse
end
これにより、操作可能な値と単位名の配列が得られます。
最初の要素がゼロ以外の場合、それは日数です。週、月、年など、複数の日を処理するコードを記述したい場合があります。それ以外の場合は、先頭の0
の値を削除して、次の2つを取得します。
def humanized_secs(sec)
return 'now' if 1 > sec
humanized_array = humanized_array_secs(sec.to_i)
days = humanized_array[-1][0]
case
when 366 <= days
"#{days / 365} years"
when 31 <= days
"#{days / 31} months"
when 7 <= days
"#{days / 7} weeks"
else
while humanized_array.any? && (0 == humanized_array[-1][0])
humanized_array.pop
end
humanized_array.reverse[0..1].flatten.join
end
end
このコードは、Ruby while
ステートメントの使用も見つけます。
distance_of_time_in_words
に問題がある場合、そこを通過すると1時間30分戻ります約2時間
ヘルパーを追加するだけです:
PERIODS = {
'day' => 86400,
'hour' => 3600,
'minute' => 60
}
def formatted_time(total)
return 'now' if total.zero?
PERIODS.map do |name, span|
next if span > total
amount, total = total.divmod(span)
pluralize(amount, name)
end.compact.to_sentence
end
基本的には、数秒でデータを渡すだけです。