开发者

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

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?


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.


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.


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]


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


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


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"


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


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


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


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/

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜