在 shell 中格式化并显示带有小数秒的间隔

在 shell 中格式化并显示带有小数秒的间隔

我有一个时间间隔(以秒为单位,带小数),我想以人类可读的(H:MM:SS.SSS)格式显示。例如,16633.284 应显示为 4:37:13.284。所有这些时间都在 24 小时以内,但如果超过了,也只是几个小时而已。

再举几个例子:

   0      → 0:00:00.000
  60.394  → 0:01:00.394
8944.77   → 2:29:04.770

请注意,它在 10 小时内是固定宽度的。当然,使用 可以很容易地做到这一点printf

我将我的解决方案作为答案发布,但感觉必须有更好的方法来做到这一点,所以我问:还有其他方法吗?我对 bashisms、zshisms 等方式持开放态度。

注:这与将秒显示为天/小时/分钟/秒?但这些方法都不起作用,因为 bash 中的算术仅是整数。

答案1

小数部分对小时数、分钟数或秒数没有任何贡献,因此您可以简单地将其放在一边,在没有它的情况下进行计算,然后将其添加到末尾。像这样的东西:

#!/bin/sh
seconds=16633.284
f=${seconds##*.}
if [ "$f" = "$seconds" ]; then f=0; fi
t=${seconds%.*}
s=$((t%60))
m=$((t/60%60))
h=$((t/60/60))
printf '%d:%02d:%06.3f\n' $h $m $s.$f

输出:

   0     → 0:00:00.000
  33.    → 0:00:33.000
    .21  → 0:00:00.210
  60.394 → 0:01:00.394
8944.77  → 2:29:04.770

这适用于任何类似 sh 的 shell(除了一些古老的 POSIX 之前的 shell)。

答案2

我的方法是这样的,用来dc进行计算:

seconds=16633.284
printf '%i:%02i:%06.3f\n' \
    $(dc -e "$seconds d 3600 / n [ ] n d 60 / 60 % n [ ] n 60 % f")

希望 printf 很熟悉,但如果不熟悉,%i则表示一个整数。%02i表示如果整数是个位数,则用前导 0 填充该整数。%06.3f最奇怪的是,它的意思是小数点后3位,如果总长度小于6(包括小数点)则用前导0填充。

故意不加引号的$(…)替换给出了这三个参数,使用dc

首先,将$seconds(展开后的数字)压入堆栈;复制它;然后除以(截断)3600 即可得到小时数。弹出并打印该内容,然后推入空格并打印它 ( [ ] n)。

堆栈上只剩下秒了。复制它,除以 60(再次截断)。这给出了分钟;采取 mod 60 来丢弃小时数。打印出来,再次加上空格。只在堆栈上留下秒数。

最后,mod 60 只给出秒字段,在直流中,模数非常适合小数并保留精度。f然后打印堆栈加上换行符。

答案3

您可以轻松地用简单的 sh 完成它。您可以使用 GNU date 使代码稍微短一些(希望您不介意向下舍入毫秒,四舍五入到最接近的值会稍微长一些):

hours=$((${seconds%.*} / 3600))
min_s_nano=$(TZ=GMT date -d @$seconds +%M:%S.%N)
time_string=$hours:${min_s_nano%??????}

答案4

我们调用dc秒数并将其放入堆栈中,然后执行计算(秒->HH/MM/SS.MSEC)、格式化并显示结果。

seconds=16633.284

dc <<DC
# rearrange stack for number < 1hr
[r ldx q]sb

# rearrange stack for number < 1min
[rd ldxq]sc

# min++, sec-=60   hrs++, min-=60
[r1+r 60-d 60!>a]sa

# display result as h:mm:ss.sss
[n 58an 2lpx 58an 2lpx 46an 3lpx 10an]sd

# perform the conversion of seconds -> hours, minutes, seconds
[d60 >c lax r0rd60 >b lax r ldx]si

[
d1000* 1000% 1/r   # fractional portion
 1000* 1000/ 1/0r  # integer    portion
lix
]sh

# left zero-pad a number
[lk1+d sk 0n le >g ]sg
[sedZd sk    le >gn]sp

# setup the number on the stack and begin computation
$seconds lhx
DC

结果

0                 -->  0:00:00.000
60.394            -->  0:01:00.394
8944.77           -->  2:29:04.770
86399.99          -->  23:59:59.990
59.9999           -->  0:00:59.999
599.9999          -->  0:09:59.999
16633.284         -->  4:37:13.284
33                -->  0:00:33.000
.21               -->  0:00:00.210
123456789.123456  -->  34293:33:09.123

相关内容