我使用日期+“%T”打印开始和结束时间,结果如下:

10:33:56
10:36:10

我如何计算并打印这两者之间的差值呢?

我想要的是:

2m 14s

当前回答

以下是我的做法:

START=$(date +%s);
sleep 1; # Your stuff
END=$(date +%s);
echo $((END-START)) | awk '{print int($1/60)":"int($1%60)}'

非常简单,取开始时的秒数,然后取结束时的秒数,打印出以分钟为单位的差值:秒。

其他回答

% start=$(date +%s)
% echo "Diff: $(date -d @$(($(date +%s)-$start)) +"%M minutes %S seconds")"
Diff: 00 minutes 11 seconds

要测量经过的时间(以秒为单位),我们需要:

表示经过的秒数和的整数 一种将这种整数转换为可用格式的方法。


以秒为单位的整数值:

There are two bash internal ways to find an integer value for the number of elapsed seconds: Bash variable SECONDS (if SECONDS is unset it loses its special property). Setting the value of SECONDS to 0: SECONDS=0 sleep 1 # Process to execute elapsedseconds=$SECONDS Storing the value of the variable SECONDS at the start: a=$SECONDS sleep 1 # Process to execute elapsedseconds=$(( SECONDS - a )) Bash printf option %(datefmt)T: a="$(TZ=UTC0 printf '%(%s)T\n' '-1')" ### `-1` is the current time sleep 1 ### Process to execute elapsedseconds=$(( $(TZ=UTC0 printf '%(%s)T\n' '-1') - a ))


将这样的整数转换为可用的格式

bash内部printf可以直接做到这一点:

$ TZ=UTC0 printf '%(%H:%M:%S)T\n' 12345
03:25:45

类似的

$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
00:12:34

但是这将在持续时间超过24小时时失败,因为我们实际上打印的是wallclock时间,而不是真正的持续时间:

$ hours=30;mins=12;secs=24
$ elapsedseconds=$(( ((($hours*60)+$mins)*60)+$secs ))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
06:12:24

对于细节爱好者,请访问bash.hackers.org:

%(FORMAT)T输出使用FORMAT产生的日期-时间字符串 作为strftime(3)的格式字符串。相关联的参数是 从Epoch开始的秒数,或-1(当前时间)或-2 (shell 启动时间)。如果没有提供相应的参数,则当前 时间为默认值。

所以你可能只想调用textifyDuration $elpasedseconds,其中textifyDuration是持续时间打印的另一个实现:

textifyDuration() {
   local duration=$1
   local shiff=$duration
   local secs=$((shiff % 60));  shiff=$((shiff / 60));
   local mins=$((shiff % 60));  shiff=$((shiff / 60));
   local hours=$shiff
   local splur; if [ $secs  -eq 1 ]; then splur=''; else splur='s'; fi
   local mplur; if [ $mins  -eq 1 ]; then mplur=''; else mplur='s'; fi
   local hplur; if [ $hours -eq 1 ]; then hplur=''; else hplur='s'; fi
   if [[ $hours -gt 0 ]]; then
      txt="$hours hour$hplur, $mins minute$mplur, $secs second$splur"
   elif [[ $mins -gt 0 ]]; then
      txt="$mins minute$mplur, $secs second$splur"
   else
      txt="$secs second$splur"
   fi
   echo "$txt (from $duration seconds)"
}

角马日期。

为了获得格式化的时间,我们应该以多种方式使用外部工具(GNU date)来获得几乎一年的长度,包括纳秒。

数学里面的日期。

不需要外部算术,在日期内一步完成:

date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"

是的,命令字符串中有一个0 - 0。这是需要的。

这是假设您可以将date +"%T"命令更改为date +"%s"命令,因此值将以秒为单位存储(打印)。

注意,该命令仅限于:

$StartDate和$FinalDate秒为正值。 $FinalDate中的值比$StartDate大(时间晚)。 时差小于24小时。 您接受带有小时、分钟和秒的输出格式。很容易改变。 使用-u UTC时间是可以接受的。避免“DST”和本地时间的修正。


如果你一定要用10:33:56这个字符串,那就把它转换成秒, 另外,seconds这个词也可以缩写为sec:

string1="10:33:56"
string2="10:36:10"
StartDate=$(date -u -d "$string1" +"%s")
FinalDate=$(date -u -d "$string2" +"%s")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S"

请注意,秒时间转换(如上所述)是相对于“这”一天(今天)的开始。


这个概念可以扩展到纳秒,就像这样:

string1="10:33:56.5400022"
string2="10:36:10.8800056"
StartDate=$(date -u -d "$string1" +"%s.%N")
FinalDate=$(date -u -d "$string2" +"%s.%N")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S.%N"

如果需要计算更长的(最多364天)时差,我们必须使用(某)年的开始作为参考,格式值%j(一年中的天数):

类似于:

string1="+10 days 10:33:56.5400022"
string2="+35 days 10:36:10.8800056"
StartDate=$(date -u -d "2000/1/1 $string1" +"%s.%N")
FinalDate=$(date -u -d "2000/1/1 $string2" +"%s.%N")
date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N"

Output:
026 days 00:02:14.340003400

遗憾的是,在这种情况下,我们需要手动从天数中减去1个1。 date命令将一年中的第一天显示为1。 没那么难…

a=( $(date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N") )
a[0]=$((10#${a[0]}-1)); echo "${a[@]}"

使用长秒数是有效的,并记录在这里: https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


Busybox日期

一个用于小型设备的工具(一个非常小的可执行文件):Busybox。

创建一个名为date的busybox链接:

$ ln -s /bin/busybox date

然后通过调用这个日期来使用它(将其放置在包含PATH的目录中)。

或者像这样做一个别名:

$ alias date='busybox date'

Busybox date有一个很好的选项:-D来接收输入时间的格式。 这就打开了很多格式来作为时间。 使用-D选项,我们可以直接转换时间10:33:56:

date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"

从上面的命令输出中可以看到,一天被假设为“today”。获取纪元开始的时间:

$ string1="10:33:56"
$ date -u -D "%Y.%m.%d-%H:%M:%S" -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

Busybox date甚至可以接收没有-D的时间(以上面的格式):

$ date -u -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

输出格式甚至可以是epoch之后的秒。

$ date -u -d "1970.01.01-$string1" +"%s"
52436

对于这两次,还有一点bash数学(busybox还不能做数学运算):

string1="10:33:56"
string2="10:36:10"
t1=$(date -u -d "1970.01.01-$string1" +"%s")
t2=$(date -u -d "1970.01.01-$string2" +"%s")
echo $(( t2 - t1 ))

或格式:

$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14

这里有一些魔法:

time1=14:30
time2=$( date +%H:%M ) # 16:00
diff=$(  echo "$time2 - $time1"  | sed 's%:%+(1/60)*%g' | bc -l )
echo $diff hours
# outputs 1.5 hours

Sed将a:替换为要转换为1/60的公式。然后用bc计算时间

下面是Daniel Kamil Kozar的回答,表示小时/分钟/秒:

echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"

所以完整的脚本是:

date1=$(date +"%s")
date2=$(date +"%s")
DIFF=$(($date2-$date1))
echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"

我想提出另一种避免召回日期命令的方法。如果你已经收集了%T日期格式的时间戳,这可能会有帮助:

ts_get_sec()
{
  read -r h m s <<< $(echo $1 | tr ':' ' ' )
  echo $(((h*60*60)+(m*60)+s))
}

start_ts=10:33:56
stop_ts=10:36:10

START=$(ts_get_sec $start_ts)
STOP=$(ts_get_sec $stop_ts)
DIFF=$((STOP-START))

echo "$((DIFF/60))m $((DIFF%60))s"

我们甚至可以用同样的方法处理毫秒。

ts_get_msec()
{
  read -r h m s ms <<< $(echo $1 | tr '.:' ' ' )
  echo $(((h*60*60*1000)+(m*60*1000)+(s*1000)+ms))
}

start_ts=10:33:56.104
stop_ts=10:36:10.102

START=$(ts_get_msec $start_ts)
STOP=$(ts_get_msec $stop_ts)
DIFF=$((STOP-START))

min=$((DIFF/(60*1000)))
sec=$(((DIFF%(60*1000))/1000))
ms=$(((DIFF%(60*1000))%1000))

echo "${min}:${sec}.$ms"