web-dev-qa-db-fra.com

comment convertir 270921sec en jours + heures + minutes + s? (Rubis)

J'ai quelques secondes. Disons 270921. Comment puis-je afficher ce nombre en indiquant qu'il s'agit de xx jours, aa heures, zz minutes, secondes?

60
Radek

Cela peut être fait de manière assez concise en utilisant 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

Vous pourriez probablement DRY continuer plus loin en faisant preuve de créativité avec collect, ou peut-être inject, mais lorsque la logique de base est composée de trois lignes, il peut être excessif.

146
Mike Woodhouse

J'espérais qu'il y aurait un moyen plus facile que d'utiliser divmod, mais c'est le moyen le plus DRY et le plus réutilisable que j'ai trouvé pour le faire:

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

La méthode est facilement ajustée en modifiant la chaîne de formatage et le premier tableau en ligne (c'est-à-dire le [24,60,60]).

Version améliorée

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

Exemples:

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"

Notez que format_string ne vous laissera pas changer l'ordre des pièces (c'est toujours la valeur la plus importante au moins). Pour un contrôle plus fin, vous pouvez utiliser split et manipuler les valeurs vous-même.

13
Kelvin

Besoin d'une pause. Golfé cette place:

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

Rails a un assistant qui convertit la distance de temps en mots . Vous pouvez regarder son implémentation: distance_du_temps_en_mots

9
Simone Carletti

Si vous utilisez Rails, il existe un moyen simple de ne pas avoir besoin de précision:

time_ago_in_words 270921.seconds.from_now
# => 3 days
9
adamlamar

Vous pouvez utiliser la méthode la plus simple que j'ai trouvée pour résoudre ce problème:

  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

Vous pouvez toujours ajuster la valeur renvoyée à vos besoins.

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

Je commence juste à écrire Ruby. Je suppose que ce n'est que pour 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
2
patai

J'ai modifié la réponse donnée par @Mike pour ajouter un formatage dynamique basé sur la taille du résultat. 

      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
SMAG