how to convert 270921sec into days + hours + minutes + sec ? (ruby)

RubyTime

Ruby Problem Overview


I have a number of seconds. Let's say 270921. How can I display that number saying it is xx days, yy hours, zz minutes, ww seconds?

Ruby Solutions


Solution 1 - Ruby

It can be done pretty concisely using divmod:

t = 270921
mm, ss = t.divmod(60)            #=> [4515, 21]
hh, mm = mm.divmod(60)           #=> [75, 15]
dd, hh = hh.divmod(24)           #=> [3, 3]
puts "%d days, %d hours, %d minutes and %d seconds" % [dd, hh, mm, ss]
#=> 3 days, 3 hours, 15 minutes and 21 seconds

You could probably DRY it further by getting creative with collect, or maybe inject, but when the core logic is three lines it may be overkill.

Solution 2 - Ruby

I was hoping there would be an easier way than using divmod, but this is the most DRY and reusable way I found to do it:

def seconds_to_units(seconds)
  '%d days, %d hours, %d minutes, %d seconds' %
    # the .reverse lets us put the larger units first for readability
    [24,60,60].reverse.inject([seconds]) {|result, unitsize|
      result[0,0] = result.shift.divmod(unitsize)
      result
    }
end

The method is easily adjusted by changing the format string and the first inline array (ie the [24,60,60]).

Enhanced version

class TieredUnitFormatter
  # if you set this, '%d' must appear as many times as there are units
  attr_accessor :format_string

  def initialize(unit_names=%w(days hours minutes seconds), conversion_factors=[24, 60, 60])
    @unit_names = unit_names
    @factors = conversion_factors

    @format_string = unit_names.map {|name| "%d #{name}" }.join(', ')
    # the .reverse helps us iterate more effectively
    @reversed_factors = @factors.reverse
  end

  # e.g. seconds
  def format(smallest_unit_amount)
    parts = split(smallest_unit_amount)
    @format_string % parts
  end

  def split(smallest_unit_amount)
    # go from smallest to largest unit
    @reversed_factors.inject([smallest_unit_amount]) {|result, unitsize|
      # Remove the most significant item (left side), convert it, then
      # add the 2-element array to the left side of the result.
      result[0,0] = result.shift.divmod(unitsize)
      result
    }
  end
end

Examples:

fmt = TieredUnitFormatter.new
fmt.format(270921)  # => "3 days, 3 hours, 15 minutes, 21 seconds"

fmt = TieredUnitFormatter.new(%w(minutes seconds), [60])
fmt.format(5454)  # => "90 minutes, 54 seconds"
fmt.format_string = '%d:%d'
fmt.format(5454)  # => "90:54"

Note that format_string won't let you change the order of the parts (it's always the most significant value to least). For finer grained control, you can use split and manipulate the values yourself.

Solution 3 - Ruby

Rails has an helper which converts distance of time in words. You can look its implementation: distance_of_time_in_words

Solution 4 - Ruby

Needed a break. Golfed this up:

s = 270921
dhms = [60,60,24].reduce([s]) { |m,o| m.unshift(m.shift.divmod(o)).flatten }
# => [3, 3, 15, 21]

Solution 5 - Ruby

If you're using Rails, there is an easy way if you don't need the precision:

time_ago_in_words 270921.seconds.from_now
# => 3 days

Solution 6 - Ruby

You can use the simplest method I found for this problem:

  def formatted_duration total_seconds
    hours = total_seconds / (60 * 60)
    minutes = (total_seconds / 60) % 60
    seconds = total_seconds % 60
    "#{ hours } h #{ minutes } m #{ seconds } s"
  end

You can always adjust returned value to your needs.

2.2.2 :062 > formatted_duration 3661
 => "1 h 1 m 1 s"

Solution 7 - Ruby

I just start writing ruby. i guess this is only for 1.9.3

def dateBeautify(t)

    cute_date=Array.new
    tables=[ ["day", 24*60*60], ["hour", 60*60], ["minute", 60], ["sec", 1] ]

    tables.each do |unit, value|
        o = t.divmod(value)
        p_unit = o[0] > 1 ? unit.pluralize : unit
        cute_date.push("#{o[0]} #{unit}") unless o[0] == 0
        t = o[1]
    end
    return cute_date.join(', ')

end

Solution 8 - Ruby

I modified the answer given by @Mike to add dynamic formatting based on the size of the result

      def formatted_duration(total_seconds)
        dhms = [60, 60, 24].reduce([total_seconds]) { |m,o| m.unshift(m.shift.divmod(o)).flatten }

        return "%d days %d hours %d minutes %d seconds" % dhms unless dhms[0].zero?
        return "%d hours %d minutes %d seconds" % dhms[1..3] unless dhms[1].zero?
        return "%d minutes %d seconds" % dhms[2..3] unless dhms[2].zero?
        "%d seconds" % dhms[3]
      end

Solution 9 - Ruby

Number of days = 270921/86400 (Number of seconds in day) = 3 days this is the absolute number

seconds remaining (t) = 270921 - 3*86400 = 11721

3.to_s + Time.at(t).utc.strftime(":%H:%M:%S")

Which will produce something like 3:03:15:21

Solution 10 - Ruby

Not a direct answer to the OP but it might help someone who lands here.

I had this string

"Sorry, you cannot change team leader in the last #{freeze_period_time} of a #{competition.kind}"

freeze_period_time resolved to 5 days inside irb, but inside the string, it resolved to time in seconds eg 47200, so the string became something ugly

"Sorry, you cannot change team leader in the last 47200 of a hackathon"

To fix it, I had to use .inspect on the freeze_period_time object.

So the following made it work

"Sorry, you cannot change team leader in the last #{freeze_period_time.inspect} of a #{competition.kind}"

Which returned the correct sentence

"Sorry, you cannot change team leader in the last 5 days of a hackathon"

TLDR

You might need time.inspect - https://www.geeksforgeeks.org/ruby-time-inspect-function/

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRadekView Question on Stackoverflow
Solution 1 - RubyMike WoodhouseView Answer on Stackoverflow
Solution 2 - RubyKelvinView Answer on Stackoverflow
Solution 3 - RubySimone CarlettiView Answer on Stackoverflow
Solution 4 - RubyMikeView Answer on Stackoverflow
Solution 5 - RubyadamlamarView Answer on Stackoverflow
Solution 6 - RubyIgor SpringerView Answer on Stackoverflow
Solution 7 - RubypataiView Answer on Stackoverflow
Solution 8 - RubySMAGView Answer on Stackoverflow
Solution 9 - RubyMaged MakledView Answer on Stackoverflow
Solution 10 - RubyKaka RutoView Answer on Stackoverflow