【问题标题】:Convert duration to hours:minutes:seconds (or similar) in Rails 3 or Ruby在 Rails 3 或 Ruby 中将持续时间转换为小时:分钟:秒(或类似)
【发布时间】:2011-05-09 17:04:06
【问题描述】:

我觉得有一种简单/内置的方法可以做到这一点,但我找不到。

我有一个整数形式的持续时间(以秒为单位),我想以友好的格式显示它。

例如3600 会显示为“01:00:00”或“1 小时”等。

我可以用time_ago_in_words(Time.zone.now+3600) 做到这一点,但这感觉有点像黑客,没有理由为了格式化这个值而从当前时间添加/减去。有duration_in_words()之类的吗?

谢谢

【问题讨论】:

    标签: ruby-on-rails ruby datetime ruby-on-rails-3


    【解决方案1】:

    总结:

    假设total_seconds = 3600

    选项 1:

    distance_of_time_in_words(total_seconds) #=> "about 1 hour"
    

    选项 2:

    Time.at(total_seconds).utc.strftime("%H:%M:%S") #=> "01:00:00"
    

    注意:它会溢出,例如。对于total_seconds = 25.hours.to_i,它也会返回"01:00:00"

    选项 3:

    seconds = total_seconds % 60
    minutes = (total_seconds / 60) % 60
    hours = total_seconds / (60 * 60)
    
    format("%02d:%02d:%02d", hours, minutes, seconds) #=> "01:00:00"
    

    选项 4:

    ActiveSupport::Duration.build(total_seconds).inspect #=> "1 hour"
    
    # OR
    
    parts = ActiveSupport::Duration.build(total_seconds).parts
    "%02d:%02d:%02d" % [parts.fetch(:hours, 0),
                        parts.fetch(:minutes, 0),
                        parts.fetch(:seconds, 0)] #=> "01:00:00"
    

    【讨论】:

    • distance_of_time_in_words 方法是一个 ActionView Helper,因此需要从 View(而不是控制器)调用。 api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
    • 请注意,strftime 选项将在 24 小时后溢出。如果您的持续时间是 25 小时,它将显示 01:00:00
    • 这不适用于负时间,例如 -1800 返回 -1h 30m 而不是 -30m
    • 请注意,选项 4 仅适用于 Duration.build;如果你尝试使用3600.seconds,你会得到"00:00:3600"
    【解决方案2】:

    见:http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html

    distance_of_time_in_words(3600)
     => "about 1 hour"
    

    【讨论】:

    • 谢谢我看了,但我觉得我必须提供两次......真是个小问题!
    • 是的,那些例子很愚蠢
    • distance_of_time_in_words(from_time, to_time, ...)
    • 另外,如果您希望它非常具体而不是“四舍五入”持续时间,请查看 Radar 的 gem:github.com/radar/distance_of_time_in_words。直接替换distance_of_time_in_words,您可以通过传递vague: true 作为选项来获得四舍五入的数字。
    【解决方案3】:

    Ruby 的字符串% 运算符太不受欢迎,经常被遗忘。

    "%02d:%02d:%02d:%02d" % [t/86400, t/3600%24, t/60%60, t%60]
    

    鉴于 t 是以秒为单位的持续时间,这会发出一个以零填充的冒号分隔的字符串,包括天数。示例:

    t = 123456
    "%02d:%02d:%02d:%02d" % [t/86400, t/3600%24, t/60%60, t%60]
    => "01:10:17:36"
    

    可爱的。

    【讨论】:

    • “可爱”的定义很奇怪。
    • % 说:“从我的右边取出参数数组并将它们插入到我左边的格式字符串中。”在这方面,它类似于 C\C++ 中的printf,只是更简洁,可以在赋值中使用。是的,我称之为可爱。并且强大。有一些例子可以更好地证明这种口才,但他们不会回答这个问题。顺便说一句,你的蛇怪不受欢迎。
    【解决方案4】:

    我猜你也可以这样做:

    (Time.mktime(0)+3600).strftime("%H:%M:%S")
    

    根据需要格式化。

    顺便说一句,最初我想使用 Time.at() 但似乎我的 Ubuntu 上的 EPOCH 时间是 Thu Jan 01 01:00:00 +0100 1970 而不是我预期的 00:00:00 小时,因此如果我愿意:

    Time.at(3600).strftime("%H:%M:%S")
    

    比想要的多给我 1 小时。

    【讨论】:

    • 啊,因为是夏天,可能有什么事情要做吗?
    • 唯一的问题是如果您处理的时间超过 24 小时。在这种情况下,小时数将回滚到一个较低的数字。
    • 对于大于 24 小时的时间,%j 会给你天数(只要它小于 365)
    【解决方案5】:

    我用它来显示我的 Rails 项目中的持续时间:

    1. Integer 类添加自定义方法。您可以在initializers 文件夹中创建一个名为pretty_duration.rb 的新文件:

      class Integer
          def pretty_duration
              parse_string = 
                  if self < 3600
                      '%M:%S'
                  else
                      '%H:%M:%S'
                  end
      
              Time.at(self).utc.strftime(parse_string)
          end
      end
      
    2. 在您项目的任何地方致电seconds.pretty_duration

      275.pretty_duration     # => "04:35"
      9823.pretty_duration    # => "02:43:43"
      

    这个答案建立在Lev Lukomsky's Code

    【讨论】:

    • 效果很好。谢谢你。
    【解决方案6】:

    这个使用了不起眼的divmod方法同时除法和取模,所以它可以正确处理Float秒:

    def duration(seconds)
      minutes, seconds = seconds.divmod(60)
      hours, minutes = minutes.divmod(60)
      days, hours = hours.divmod(24)
    
      "#{days.to_s.rjust(3)}d #{hours.to_s.rjust(2)}h #{minutes.to_s.rjust(2)}m #{seconds}s"
    end
    

    【讨论】:

      【解决方案7】:

      ActiveSupport::Duration.build + inspect 为您提供有效结果

       >> ActiveSupport::Duration.build(125557).inspect
       => "1 day, 10 hours, 52 minutes, and 37 seconds"
      

      【讨论】:

      • 优雅!很适合我。值75的结果:1 minute and 15 seconds
      • 它甚至可以用于提取单个部分以构建自定义字符串。例如ActiveSupport::Duration.build(125557).parts 会给{ days: 1, hours: 10, minutes: 52, seconds: 37 }
      【解决方案8】:

      使用Time.utc.strftime 仅适用于总小时数小于24的值:

      2.2.2 :004 > Time.at(60 * 60).utc.strftime('%H h %M m')
      => "01 h 00 m"
      

      对于更大的值,它会返回不正确的结果:

      2.2.2 :006 > Time.at(60 * 60 * 24).utc.strftime('%H h %M m')
       => "00 h 00 m"
      

      我建议使用我为这个问题找到的最简单的方法:

        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
      

      您可以随时根据需要调整返回值。

      【讨论】:

        【解决方案9】:

        请注意超过一天的持续时间。

        (timing/3600).to_i.to_s.rjust(2,'0') + ":"+Time.at(timing).utc.strftime("%M:%S")
        

        【讨论】:

        • 举一个输入/输出的例子,强调这是如何解决极端情况的
        【解决方案10】:

        从 Lev Lukomsky 的利用 ActiveSupport::Duration 和处理毫秒(对基准代码很有用)得到启发的答案

        # duration in ms modulus number of ms in one second
        milliseconds = duration.in_milliseconds % 1.second.in_milliseconds
        
        # duration in seconds modulus number of seconds in one minute
        seconds = (duration / 1.second) % (1.minute / 1.second)
        
        # duration in minutes modulus number of minutes in one hour
        minutes = (duration / 1.minute) % (1.hour / 1.minute)
        
        # duration in hours modulus number of hours in one day
        hours = (duration / 1.hour) % (1.day / 1.hour)
        
        format("%02d:%02d:%02d:%03d", hours, minutes, seconds, milliseconds) #=> "12:05:00:001"
        

        当然,您可以使用相关的 ActiveSupport 方法并重复相同的结构,轻松地将其扩展为天、月、年等。

        请记住,对于太长的持续时间,这可能不准确,因为 1 个月的持续时间在天数上是不固定的,我不确定 AS:Duration 是如何处理这个问题的。

        【讨论】:

          【解决方案11】:

          向给出最佳答案的@joshuapinter 大声喊叫(以a comment 的形式)。

          使用插入式替换 dotiw gem 来更好地控制输出的准确性以适应不同的需求:

          https://github.com/radar/distance_of_time_in_words

          示例查看代码:

          %label
            Logoff after:
            - expire_in = distance_of_time_in_words(Time.now, Time.now + user.custom_timeout.minutes, :only => [:minutes, :hours, :days])
            = expire_in
          

          结果如下:

          Logoff after: 1 day, 13 hours, and 20 minutes
          

          【讨论】:

          • 请注意,您还可以使用dotiw 生成其他格式,例如:DOTIW::TimeHash.new(1.days + 2.hours + 3.minutes + 4.seconds, nil).to_hash.each_value.map { |v| format('%02d', v) }.join(':') 生成01:02:03:04。 (不过,这种特殊的格式在几天之后变得有点难以阅读。)
          【解决方案12】:

          只需投入我的 2 美分:

          Time.at(i).utc.strftime((i < 3600) ? '%-M minutes and %-S seconds' : '%-H hours, %-M minutes, and %-S seconds')
          

          根据小斌的答案构建。

          【讨论】:

            【解决方案13】:

            这里有一个使用 divmod 和 map 的简单解决方案:

                hours = 3.5456
                value = (hours*60).divmod(60).map{ |a| "%02d"%[a.floor] }.join(":")
                => "03:32"
            

            【讨论】:

            • 请在答案中添加一些上下文。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-12-26
            • 1970-01-01
            • 2011-09-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-05-18
            相关资源
            最近更新 更多