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

前端 未结 9 458
不知归路
不知归路 2020-12-02 06:09

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?

相关标签:
9条回答
  • 2020-12-02 06:15

    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.

    0 讨论(0)
  • 2020-12-02 06:15

    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]
    
    0 讨论(0)
  • 2020-12-02 06:15

    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
    
    0 讨论(0)
  • 2020-12-02 06:18

    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.

    0 讨论(0)
  • 2020-12-02 06:23

    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
    
    0 讨论(0)
  • 2020-12-02 06:24

    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

    0 讨论(0)
提交回复
热议问题