如何干燥`DD:HH:MM:SS`时间格式逻辑

时间:2015-12-24 18:33:01

标签: ruby-on-rails ruby date time

我想得到:

    {li} 60s格式sec==60dd==0 && hh==0 && mm==0) {li} 1min 30s格式sec==90dd==0 && hh==0

我这样做了:

def sec_int2str(sec)
  mm, ss = sec.divmod(60)
  hh, mm = mm.divmod(60)
  dd, hh = hh.divmod(24)
  if dd==0 && hh==0 && mm==0
    "#{ss}s"
  elsif dd==0 && hh==0
    "#{mm}min #{ss}s"
  elsif dd==0
    "#{hh}h #{mm}min #{ss}s"
  else
    "#{dd}d #{hh}h #{mm}min #{ss}s"
  end
end

但逻辑太复杂了。我怎样才能简单地转换?

2 个答案:

答案 0 :(得分:2)

正如您已经确定的那样,您基本上一遍又一遍地执行相同的操作(Numeric#divmod),但是间隔不同(60,60,24)。因此,对我来说,一个简单的解决方案是将这些间隔与相应的标签一起放在数据结构中:

UNITS = {
  "s"   => 60,
  "min" => 60,
  "h"   => 24,
  "d"   => Float::INFINITY
}.freeze

请注意Float::INFINITY用于“顶级”间隔。现在我们可以使用Enumerable#reduce迭代区间和标签,在每次迭代中将先前的divmod商除以新区间:

def sec_int2str(quo)
  UNITS.reduce([]) do |memo, (unit, int)|
    break memo if quo == 0
    break memo.unshift("#{quo}#{unit}") if quo == int
    quo, rem = quo.divmod(int)
    memo.unshift("#{rem.to_i}#{unit}")
  end.join(' ')
end

如您所见,如果商为0,则第一个break会提前结束迭代,因此我们不会得到0d 0h ...。如果商等于当前间隔,则第二个break结束迭代,因此我们得到60s而不是1h 0s(和24h ...而不是1d 0h ...)。

让我们测试一下:

secs = 1
mins = 60 * secs
hrs  = 60 * mins
days = 24 * hrs

[ 60 * secs,
  90 * secs,
  60 * mins,
  90 * mins + 15 * secs,
   6 * hrs  + 55 * mins + 30 * secs,
   1 * days + 10 * secs,
  75 * hrs  + 30 * mins
].each do |num|
  printf("%6d => %s\n", num, sec_int2str(num))
end

输出:

    60 => 60s
    90 => 1min 30s
  3600 => 60min 0s
  5415 => 1h 30min 15s
 24930 => 6h 55min 30s
 86410 => 24h 0min 10s
271800 => 3d 3h 30min 0s

看起来不错!

答案 1 :(得分:1)

根据how to convert 270921sec into days + hours + minutes + sec ? (ruby),您可以获取传入的秒数并获得hh mmss

然后你可以:

[
  hh>0 && "#{hh} hours",
  mm>0 && "#{mm} minutes",
  ss>0 && "#{ss} seconds",
].join ', '

当然你可以得到" 2小时,4秒和#34;如果分钟为零,则不在此。