【问题标题】:How do I display duration in milliseconds as a properly formatted string?如何将持续时间(以毫秒为单位)显示为格式正确的字符串?
【发布时间】:2017-09-13 14:09:30
【问题描述】:

我使用的是 Ruby 2.4。我想将毫秒转换为可读的小时、分钟和秒格式。所以我有这个方法

  def time_as_str(time_in_ms)
    regex = /^(0*:?)*0*/
    Time.at(time_in_ms.to_f/1000).utc.strftime("%H:%M:%S.%1N").sub!(regex, '')
  end

问题是,如果我的毫秒值是一天或更大,则此函数不会显示正确的值。例如,如果我通过了

2.4.0 :009 > TimeFormattingHelper.time_as_str(86400000)
 => ".0"

86400000 是以毫秒为单位的一天(除非我计算错误)。所以我希望这个值是“24:00:00”。如何更正上述内容以正确显示时间格式?

【问题讨论】:

  • 你的正则表达式搞砸了。即使它消失了,这也不会达到您的预期。 Time.at(86400000.to_f/1000) => 1970-01-01 18:00:00 -0600。您正在获取日期的小时数... Time.at(86400000.to_f/1000).strftime("%H:%M:%S.%1N") => "18:00:00.0" ruby-doc.org/core-2.3.0/Time.html#method-c-at
  • 此外,当您尝试解决此类问题时,将其分解为多个步骤会有所帮助,每个步骤都足够简单,可以轻松确定成功。您的最后一行有不少于 6 个单独的操作,每一个都导致难以确定所有这些错误的确切位置......
  • 阅读 Jon Skeet 的“Writing the perfect question
  • "24:00:00" 是一个无效时间,不是吗?您的方法返回一个Time 对象,因此应该只返回有效时间,即在"00:00:00""23:59:59" 之间。除非您尝试将毫秒转换为小时、分钟和秒,这与将有效时间(以毫秒为单位)转换为 hh:mm:ss 不同。

标签: ruby string time format


【解决方案1】:

一些基本信息:

  • 一秒 = 1000 毫秒
  • 一分钟 = 1000 × 60 毫秒
  • 一小时 = 1000 × 60 × 60 毫秒

现在,你可以试试下面的方法:

def get_duration_hrs_and_mins(duration)
  duration = duration.to_i

  h = duration / (1000 * 60 * 60)
  m = duration / (1000 * 60) % 60
  s = duration / (1000.to_f) % 60

  h = zero_to_nil(h)
  m = zero_to_nil(m) if h.nil?

  arr = [h, m, s].map { |e| format_val(e) }

  arr.compact.join(':')
end

def format_val(e)
  case
  when e.nil?
    nil
  when e.zero?
    '00'
  when e.round(0) == e
    sprintf("%2d", e)
  else
    e.round(3)
  end
end

def zero_to_nil(val)
  val.zero? ? nil : val
end

rails 控制台的一些输出:

puts get_duration_hrs_and_mins(86400000)
# => 24:00:00
puts get_duration_hrs_and_mins(150400)
# => 2:30.4
puts get_duration_hrs_and_mins(150401)
# => 2:30.401
puts get_duration_hrs_and_mins(1500)
# => 1.5
puts get_duration_hrs_and_mins(864000)
# => 14:24

【讨论】:

  • 谢谢,但上面有几个问题。首先,使用 150400 调用函数会导致“00:02:30”。它应该是“2:30.4”(十分之一秒在你的函数中被截断)。此外,您的功能包括前导零,我想排除这些。
  • @Dave 所以,如果是00,你甚至想排除小时。在几秒钟内,您想要打印小数点后的一位或两位数 (.)。对吗?
  • 是的,绝对要排除前导零。您可以假设“持续时间”总是一个整数,所以我想包括所有小数位,以准确反映该值是什么。谢谢,
  • 什么是 number_with_precision ? taht 是内置的 Ruby 函数吗?当我尝试运行这个抱怨不知道“number_with_precision”是什么时,我遇到了一个错误。
  • number_with_precision 是 rails NumberHelper。我在答案中包含了那个助手 (ActionView::Helpers::NumberHelper)。
【解决方案2】:
def time_as_str(ms)
  secs, ms    =   ms.divmod(1000)
  mins, secs  = secs.divmod(60)
  hours, mins = mins.divmod(60)
  s = "%d.%d.%d.%s" % [hours, mins, secs, ms.zero? ? "0" : ms.to_s.sub(/0*\z/,'')]
  if hours > 0
    s
  elsif mins > 0
    s[2..-1]
  else
    s[4..-1]   
  end      
end

time_as_str(86_400_000)    #=> "24.0.0.0"
time_as_str(0)             #=> "0.0"
time_as_str(499)           #=> "0.499"
time_as_str(60_280)        #=> "1.0.28"
time_as_str(360_000)       #=> "6.0.0"
time_as_str(1_000_000_200) #=> "277.46.40.2"

请参阅Integer#divmod,这是一种经常被忽视的方法。

假设

ms = 2_045_670

然后

secs, ms    =   ms.divmod(1000)
  #=> [2045, 670] (secs #=> 2045, ms #=> 670)
mins, secs  = secs.divmod(60)
  #=> [34, 5]
hours, mins = mins.divmod(60)
  #=> [0, 34]
ms = ms.zero? ? "0" : ms.to_s.sub(/0*\z/,'')
  #=> "67" (truncate)
s = "%d.%d.%d.%s" % [hours, mins, secs, ms]
  #=> "0.34.5.67"
if hours > 0   # false
  "0.34.5.67" 
elsif mins > 0 # true
  "34.5.67"
else           # not evaluated
  "5.67" 
end
  #=> "34.5.67"

【讨论】:

  • 嗨,卡里,感谢您的回答。我遇到了与 Atul 的回答相同的问题,即如果我将 150400 插入您的函数,我会得到“0:02:30”。我应该得到“2:30.4”是好的和准确的。还有什么办法可以消除前导零?
  • 戴夫,这很容易解决。两个问题。 1) 你想要几秒的精度(例如,150412ms 应该是“2.30.412”吗?2)你想删除分钟和秒以及小时的前导零吗?
  • 嗨,我希望精度能够准确地代表我的毫秒数。因为我永远不会有毫秒的小数(它总是一个整数),我猜精度不会超过小数点后的 3 位。我“认为”我的声明是正确的。是的,我想删除分钟和秒以及小时的前导零。
  • 我修改了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-16
  • 1970-01-01
  • 2011-05-07
  • 1970-01-01
  • 1970-01-01
  • 2015-09-15
  • 2013-10-13
相关资源
最近更新 更多