【问题标题】:How to generate a human readable time range using ruby on rails如何使用 ruby​​ on rails 生成人类可读的时间范围
【发布时间】:2011-05-07 09:07:53
【问题描述】:

我正在尝试找到生成以下输出的最佳方法

<name> job took 30 seconds
<name> job took 1 minute and 20 seconds
<name> job took 30 minutes and 1 second
<name> job took 3 hours and 2 minutes

我开始这段代码

def time_range_details
  time = (self.created_at..self.updated_at).count
  sync_time = case time 
    when 0..60 then "#{time} secs"       
    else "#{time/60} minunte(s) and #{time-min*60} seconds"
  end
end

有没有更有效的方法来做到这一点。对于一些超级简单的事情,似乎有很多冗余代码。

另一个用途是:

<title> was posted 20 seconds ago
<title> was posted 2 hours ago

此代码类似,但我使用 Time.now:

def time_since_posted
  time = (self.created_at..Time.now).count
  ...
  ...
end

【问题讨论】:

    标签: ruby-on-rails ruby time


    【解决方案1】:

    如果您需要比distance_of_time_in_words 更“精确”的内容,您可以按照以下方式编写内容:

    def humanize(secs)
      [[60, :seconds], [60, :minutes], [24, :hours], [Float::INFINITY, :days]].map{ |count, name|
        if secs > 0
          secs, n = secs.divmod(count)
    
          "#{n.to_i} #{name}" unless n.to_i==0
        end
      }.compact.reverse.join(' ')
    end
    
    p humanize 1234
    #=>"20 minutes 34 seconds"
    p humanize 12345
    #=>"3 hours 25 minutes 45 seconds"
    p humanize 123456
    #=>"1 days 10 hours 17 minutes 36 seconds"
    p humanize(Time.now - Time.local(2010,11,5))
    #=>"4 days 18 hours 24 minutes 7 seconds"
    

    哦,对你的代码有一点评论:

    (self.created_at..self.updated_at).count
    

    真的是获得差异的糟糕方法。简单使用:

    self.updated_at - self.created_at
    

    【讨论】:

    • 只是好奇,为什么不好? (self.created_at..self.updated_at).count 感谢您的干净回答!
    • @csanz: .count 在这种情况下会遍历两个时间戳之间的每秒数组并计算它们。好像你会通过实际计算 50 到 100 之间的所有数字来计算表达式 100-50 的结果。
    • 我更喜欢 DateHelper 方法。如果您要费心将其转换为英语,那么您可能不想结合天数和秒数。这是一种人为的精确度。
    • 正确,但主要取决于上下文。例如,我需要类似的东西来显示表格中每个项目的持续时间。我首先尝试了 DateHelper,但后来将其替换为自定义方法,该方法打印类似 5:30:25,rigt 对齐的内容。表格数据比 DH 中的“模糊”表达式更具可读性。
    • 这段代码很棒,但如果超过 1,000 天(86_400_000 秒),它就不起作用。 humanize(86_400_001) =&gt; "0 days 0 hours 0 minutes 1 seconds" 是错误的。可以通过将数组的最后一个元素更新为 [Float::INFINITY, :days] 来修复此错误。
    【解决方案2】:

    DateHelper 中有两种方法可以满足您的需求:

    1. time_ago_in_words

      time_ago_in_words( 1234.seconds.from_now ) #=> "21 minutes"
      
      time_ago_in_words( 12345.seconds.ago )     #=> "about 3 hours"
      
    2. distance_of_time_in_words

      distance_of_time_in_words( Time.now, 1234.seconds.from_now ) #=> "21 minutes"
      
      distance_of_time_in_words( Time.now, 12345.seconds.ago )     #=> "about 3 hours"
      

    【讨论】:

    • 请注意,distance_of_time_in_words 支持 i18n 并具有双重接口:它可以直接用于持续时间:distance_of_time_in_words(6270.873) #=&gt; environ 2 heures(法语)
    【解决方案3】:

    chronic_duration 将数字时间解析为可读,反之亦然

    【讨论】:

      【解决方案4】:

      如果您想在秒到天的范围内显示重要的持续时间,另一种选择是(因为它不必表现最好):

      def human_duration(secs, significant_only = true)
        n = secs.round
        parts = [60, 60, 24, 0].map{|d| next n if d.zero?; n, r = n.divmod d; r}.
          reverse.zip(%w(d h m s)).drop_while{|n, u| n.zero? }
        if significant_only
          parts = parts[0..1] # no rounding, sorry
          parts << '0' if parts.empty?
        end
        parts.flatten.join
      end
      start = Time.now
      # perform job
      puts "Elapsed time: #{human_duration(Time.now - start)}"
      
      human_duration(0.3) == '0'
      human_duration(0.5) == '1s'
      human_duration(60) == '1m0s'
      human_duration(4200) == '1h10m'
      human_duration(3600*24) == '1d0h'
      human_duration(3600*24 + 3*60*60) == '1d3h'
      human_duration(3600*24 + 3*60*60 + 59*60) == '1d3h' # simple code, doesn't round
      human_duration(3600*24 + 3*60*60 + 59*60, false) == '1d3h59m0s'
      

      或者,您可能只对在无关紧要的情况下剥离秒部分感兴趣(也演示了另一种方法):

      def human_duration(duration_in_seconds)
        n = duration_in_seconds.round
        parts = []
        [60, 60, 24].each{|d| n, r = n.divmod d; parts << r; break if n.zero?}
        parts << n unless n.zero?
        pairs = parts.reverse.zip(%w(d h m s)[-parts.size..-1])
        pairs.pop if pairs.size > 2 # do not report seconds when irrelevant
        pairs.flatten.join
      end
      

      希望对您有所帮助。

      【讨论】:

        【解决方案5】:

        distance_of_time_in_words 有问题,如果您通过那里1 小时 30 分钟它将返回 大约 2 小时

        只需添加助手:

         PERIODS = {
           'day' => 86400,
           'hour' => 3600,
           'minute' => 60
           }
        
        
        def formatted_time(total)
          return 'now' if total.zero?
        
          PERIODS.map do |name, span|
            next if span > total
            amount, total = total.divmod(span)
            pluralize(amount, name)
          end.compact.to_sentence
        end
        

        基本上只需几秒钟即可传递您的数据。

        【讨论】:

          【解决方案6】:

          Rails 有一个DateHelper 用于查看。如果这不是您想要的,您可能需要自己编写。

          @Mladen Jablanović 有一个很好的示例代码的答案。但是,如果您不介意继续自定义人性化示例方法,这可能是一个很好的起点。

          def humanized_array_secs(sec)
            [[60, 'minutes '], [60, 'hours '], [24, 'days ']].inject([[sec, 'seconds']]) do |ary, (count, next_name)|
              div, prev_name = ary.pop
          
              quot, remain = div.divmod(count)
              ary.push([remain, prev_name])
              ary.push([quot, next_name])
              ary
            end.reverse
          end
          

          这为您提供了一个可以操作的值和单位名称数组。

          如果第一个元素不为零,则为天数。您可能希望编写代码来处理多天,例如显示周、月和年。否则,剪掉前面的 0 值,并取接下来的两个。

          def humanized_secs(sec)
            return 'now' if 1 > sec
          
            humanized_array = humanized_array_secs(sec.to_i)
            days = humanized_array[-1][0]
            case
              when 366 <= days
                "#{days / 365} years"
              when 31 <= days
                "#{days / 31} months"
              when 7 <= days
                "#{days / 7} weeks"
              else
                while humanized_array.any? && (0 == humanized_array[-1][0])
                  humanized_array.pop
                end
                humanized_array.reverse[0..1].flatten.join
            end
          end
          

          该代码甚至可以用于 ruby​​ while 语句。

          【讨论】:

            猜你喜欢
            • 2011-05-15
            • 2012-09-03
            • 2012-12-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多