Can Ruby print out time difference (duration) read

2019-01-09 09:02发布

Can Ruby do something like this?

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"

(the duration method doesn't exist now)... and similarly, report

23 minutes and 35 seconds
1 hour and 33 minutes
2 days and 3 hours

(either report the whole duration, up to how many seconds, or report up to 2 numbers and units (if day and hour is reported, then no need to tell how many minutes))

11条回答
等我变得足够好
2楼-- · 2019-01-09 09:20
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

then you can print it out what ever way you wish,

i.e

if(days > 0)
return "#{days} Days #{hours} Hours"
else
return "#{hours} Hours #{mins} Minutes"
end
查看更多
干净又极端
3楼-- · 2019-01-09 09:21

Try a ruby gem for that https://rubygems.org/gems/time_difference - Time Difference gem for Ruby Documentation at 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
查看更多
淡お忘
4楼-- · 2019-01-09 09:21

Building on Michael Richard's answer, here's a replacement for the if block that gets English pluralization right, and won't say things like "14 days and 0 hours":

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
查看更多
爷的心禁止访问
5楼-- · 2019-01-09 09:22

I've given an alternative implementation for this when used in script logs here [copied from there]:

How to generate a human readable time range using ruby on rails

If you want to show significant durations in the seconds to days range, an alternative would be (as it doesn't have to perform the best):

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'

Alternatively you may be only interested in stripping the seconds part when it doesn't matter (also demonstrating another approach):

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

Hope that helps.

查看更多
孤傲高冷的网名
6楼-- · 2019-01-09 09:23

As an alternative you can do this:

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"
查看更多
smile是对你的礼貌
7楼-- · 2019-01-09 09:24

I could not withstand to put a generic solution here - although: has a year 365 days?

Additional I put an abs when converting 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

and with translation

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
查看更多
登录 后发表回答