Подтвердить что ты не робот

Может ли 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

(либо сообщать всю продолжительность, сколько секунд или сообщать до двух цифр и единиц (если сообщается день и час, то не нужно указывать, сколько минут))

4b9b3361

Ответ 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. Это даст вам отличное стартовое место. Несмотря на то, что он загружен магическими числами, подход должен работать для вас.

Ответ 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