Convert seconds to hours, minutes, seconds
Asked Answered
F

18

90

How can I convert seconds to hours, minutes and seconds?

show_time() {
  ?????
}

show_time 36 # 00:00:36
show_time 1036 # 00:17:26
show_time 91925 # 25:32:05
Foreside answered 30/8, 2012 at 14:49 Comment(2)
Use awk unix.stackexchange.com/questions/34017/…Sidelong
see also #13423243 (tl;dr: date -u -d @${sec} +"%T")Grillroom
T
37

I use the following function myself:

function show_time () {
    num=$1
    min=0
    hour=0
    day=0
    if((num>59));then
        ((sec=num%60))
        ((num=num/60))
        if((num>59));then
            ((min=num%60))
            ((num=num/60))
            if((num>23));then
                ((hour=num%24))
                ((day=num/24))
            else
                ((hour=num))
            fi
        else
            ((min=num))
        fi
    else
        ((sec=num))
    fi
    echo "$day"d "$hour"h "$min"m "$sec"s
}

Note it counts days as well. Also, it shows a different result for your last number.

Ticknor answered 30/8, 2012 at 14:59 Comment(1)
This is a good cross-platform solution, works on both MacOS and Linux. I had issues getting echo Elapsed time: $(date -ud "@$elapsed" +'$((%s/3600/24)) days %H hr %M min %S sec')" to work on both (even tried using gdate instead, without success)Rozek
C
134

Use date, converted to UTC:

$ date -d@36 -u +%H:%M:%S
00:00:36
$ date -d@1036 -u +%H:%M:%S
00:17:16
$ date -d@12345 -u +%H:%M:%S
03:25:45

The limitation is the hours will loop at 23, but that doesn't matter for most use cases where you want a one-liner.

On macOS, run brew install coreutils and replace date with gdate

Cheep answered 26/3, 2015 at 1:55 Comment(7)
I think this is only GNU's date, and is not portable.Survivor
OP asked for bash, so I assumed GNU dateCheep
I upvoted your answer because it's very helpful to me, but I'm using GNU/Linux. It's not safe to assume that anyone using bash is using GNU/Linux (and GNU date). The first case that comes to mind is OSX, but I think BSD falls in this category, too.Survivor
It is probably better to explain why the limitation is in place in more detail as it can be confusing. This function is a bit of a hack to the OP's request as it converts epoch seconds (number of seconds from 1970) to a chosen format, which means that if you give it more than 24 hours worth of seconds it will increase the day count and start the hours againRuscher
@Cheep Could you maybe add an example of how to get the seconds from stdin?Backwater
No need for coreutils on MacOS, you can use the ootb date: https://mcmap.net/q/95395/-convert-seconds-to-hours-minutes-secondsVotyak
even more hacky, but without the restriction to less than 3600 seconds: date -u +"$(( ${seconds} / 3600 )):%M:%S" -d "@${seconds}"Jareb
M
92
#!/bin/sh

convertsecs() {
 ((h=${1}/3600))
 ((m=(${1}%3600)/60))
 ((s=${1}%60))
 printf "%02d:%02d:%02d\n" $h $m $s
}
TIME1="36"
TIME2="1036"
TIME3="91925"

echo $(convertsecs $TIME1)
echo $(convertsecs $TIME2)
echo $(convertsecs $TIME3)

For float seconds:

convertsecs() {
 h=$(bc <<< "${1}/3600")
 m=$(bc <<< "(${1}%3600)/60")
 s=$(bc <<< "${1}%60")
 printf "%02d:%02d:%05.2f\n" $h $m $s
}
Mayer answered 30/8, 2012 at 14:58 Comment(2)
The first part of the answer with ((...)) is not portable: github.com/koalaman/shellcheck/wiki/SC2039 and neither the second: bash: bc: command not foundMarroquin
@user, ((...)) is ksh syntax indeed (also supported by zsh/bash). bc however is a non-optional standard POSIX command. <<< is zsh syntax though (also supported by bash and a few other shells these days, but not standard sh syntax). Leaving the parameter expansions unquoted implies zsh syntax. In other shells, they're subject to split+glob. But even in zsh the unquoted $(...) would still be subject to splitting. The support of %f is optional in POSIX printf.Marivaux
D
80

Simple one-liner

$ secs=236521
$ printf '%dh:%dm:%ds\n' $((secs/3600)) $((secs%3600/60)) $((secs%60))
65h:42m:1s

With leading zeroes

$ secs=236521
$ printf '%02dh:%02dm:%02ds\n' $((secs/3600)) $((secs%3600/60)) $((secs%60))
65h:42m:01s

With days

$ secs=236521
$ printf '%dd:%dh:%dm:%ds\n' $((secs/86400)) $((secs%86400/3600)) $((secs%3600/60)) \
  $((secs%60))
2d:17h:42m:1s

With nanoseconds

$ secs=21218.6474912
$ printf '%02dh:%02dm:%02fs\n' $(echo -e "$secs/3600\n$secs%3600/60\n$secs%60"| bc)
05h:53m:38.647491s

Based on https://mcmap.net/q/95395/-convert-seconds-to-hours-minutes-seconds but edit got rejected.

Dishwasher answered 12/9, 2016 at 14:24 Comment(0)
D
64

The simplest way I know of:

secs=100000
printf '%dh:%dm:%ds\n' $((secs/3600)) $((secs%3600/60)) $((secs%60))

Note - if you want days then just add other unit and divide by 86400.

Dahabeah answered 11/2, 2015 at 9:59 Comment(3)
This will not give you leading 0s: use %02d instead of %d.Guerrilla
busybox safe :)Bishop
if you want days you also need to change hours so they do not go above 24: printf '%dd %dh:%dm:%ds\n' $(($secs/86400)) $(($secs%86400/3600)) $(($secs%3600/60)) $(($secs%60))Sheepdog
T
37

I use the following function myself:

function show_time () {
    num=$1
    min=0
    hour=0
    day=0
    if((num>59));then
        ((sec=num%60))
        ((num=num/60))
        if((num>59));then
            ((min=num%60))
            ((num=num/60))
            if((num>23));then
                ((hour=num%24))
                ((day=num/24))
            else
                ((hour=num))
            fi
        else
            ((min=num))
        fi
    else
        ((sec=num))
    fi
    echo "$day"d "$hour"h "$min"m "$sec"s
}

Note it counts days as well. Also, it shows a different result for your last number.

Ticknor answered 30/8, 2012 at 14:59 Comment(1)
This is a good cross-platform solution, works on both MacOS and Linux. I had issues getting echo Elapsed time: $(date -ud "@$elapsed" +'$((%s/3600/24)) days %H hr %M min %S sec')" to work on both (even tried using gdate instead, without success)Rozek
P
17

For us lazy people: ready-made script available at https://github.com/k0smik0/FaCRI/blob/master/fbcmd/bin/displaytime :

#!/bin/bash

function displaytime {
  local T=$1
  local D=$((T/60/60/24))
  local H=$((T/60/60%24))
  local M=$((T/60%60))
  local S=$((T%60))
  [[ $D > 0 ]] && printf '%d days ' $D
  [[ $H > 0 ]] && printf '%d hours ' $H
  [[ $M > 0 ]] && printf '%d minutes ' $M
  [[ $D > 0 || $H > 0 || $M > 0 ]] && printf 'and '
  printf '%d seconds\n' $S
}

displaytime $1

Basically just another spin on the other solutions, but has the added bonus of suppressing empty time units (f.e. 10 seconds instead of 0 hours 0 minutes 10 seconds). Couldn't quite track down the original source of the function, occurs in multiple git repos..

Patsis answered 23/8, 2015 at 8:31 Comment(2)
This is really nice — the only thing it's missing is dropping the -s from singular day(s), hour(s), minute(s) and second(s).Paraglider
I think the only BASHism in there is [[...]]. Replace it with [...], and it's completely POSIX.Clipboard
I
14

Using dc:

$ echo '12345.678' | dc -e '?1~r60~r60~r[[0]P]szn[:]ndZ2>zn[:]ndZ2>zn[[.]n]sad0=ap'
3:25:45.678

The expression ?1~r60~r60~rn[:]nn[:]nn[[.]n]sad0=ap does the following:

?   read a line from stdin
1   push one
~   pop two values, divide, push the quotient followed by the remainder
r   reverse the top two values on the stack
60  push sixty
~   pop two values, divide, push the quotient followed by the remainder
r   reverse the top two values on the stack
60  push sixty
~   pop two values, divide, push the quotient followed by the remainder
r   reverse the top two values on the stack
[   interpret everything until the closing ] as a string
  [0]   push the literal string '0' to the stack
  n     pop the top value from the stack and print it with no newline
]   end of string, push the whole thing to the stack
sz  pop the top value (the string above) and store it in register z
n   pop the top value from the stack and print it with no newline
[:] push the literal string ':' to the stack
n   pop the top value from the stack and print it with no newline
d   duplicate the top value on the stack
Z   pop the top value from the stack and push the number of digits it has
2   push two
>z  pop the top two values and executes register z if the original top-of-stack is greater
n   pop the top value from the stack and print it with no newline
[:] push the literal string ':' to the stack
n   pop the top value from the stack and print it with no newline
d   duplicate the top value on the stack
Z   pop the top value from the stack and push the number of digits it has
2   push two
>z  pop the top two values and executes register z if the original top-of-stack is greater
n   pop the top value from the stack and print it with no newline
[   interpret everything until the closing ] as a string
  [.]   push the literal string '.' to the stack
  n     pop the top value from the stack and print it with no newline
]   end of string, push the whole thing to the stack
sa  pop the top value (the string above) and store it in register a
d   duplicate the top value on the stack
0   push zero
=a  pop two values and execute register a if they are equal
p   pop the top value and print it with a newline

An example execution with the stack state after each operation:

    : <empty stack>
?   : 12345.678
1   : 1, 12345.678
~   : .678, 12345
r   : 12345, .678  # stack is now seconds, fractional seconds
60  : 60, 12345, .678
~   : 45, 205, .678
r   : 205, 45, .678  # stack is now minutes, seconds, fractional seconds
60  : 60, 205, 45, .678
~   : 25, 3, 45, .678
r   : 3, 25, 45, .678  # stack is now hours, minutes, seconds, fractional seconds

[[0]n]  : [0]n, 3, 25, 45, .678
sz  : 3, 25, 45, .678  # '[0]n' stored in register z

n   : 25, 45, .678  # accumulated stdout: '3'
[:] : :, 25, 45, .678
n   : 25, 45, .678  # accumulated stdout: '3:'
d   : 25, 25, 45, .678
Z   : 2, 25, 45, .678
2   : 2, 2, 25, 45, .678
>z  : 25, 45, .678  # not greater, so register z is not executed
n   : 45, .678  # accumulated stdout: '3:25'
[:] : :, 45, .678
n   : 45, .678  # accumulated stdout: '3:25:'
d   : 45, 45, .678
Z   : 2, 45, 45, .678
2   : 2, 2, 45, .678
>z  : 45, .678  # not greater, so register z is not executed
n   : .678  # accumulated stdout: '3:25:45'

[[.]n]  : [.]n, .678
sa  : .678  # '[.]n' stored to register a
d   : .678, .678
0   : 0, .678, .678
=a  : .678  # not equal, so register a not executed
p   : <empty stack>  # accumulated stdout: '3:25:45.678\n'

In the case of 0 fractional seconds:

    : 3, 25, 45, 0  # starting just before we begin to print

n   : 25, 45, .678  # accumulated stdout: '3'
[:] : :, 25, 45, .678
n   : 25, 45, .678  # accumulated stdout: '3:'
d   : 25, 25, 45, .678
Z   : 2, 25, 45, .678
2   : 2, 2, 25, 45, .678
>z  : 25, 45, .678  # not greater, so register z is not executed
n   : 45, .678  # accumulated stdout: '3:25'
[:] : :, 45, .678
n   : 45, .678  # accumulated stdout: '3:25:'
d   : 45, 45, .678
Z   : 2, 45, 45, .678
2   : 2, 2, 45, .678
>z  : 45, .678  # not greater, so register z is not executed
n   : .678  # accumulated stdout: '3:25:45'

[[.]n]  : [.]n, 0
sa  : 0  # '[.]n' stored to register a
d   : 0, 0
0   : 0, 0, 0
=a  : 0  # equal, so register a executed
  [.] : ., 0
  n   : 0  # accumulated stdout: '3:35:45.'
p   : <empty stack>  # accumulated stdout: '3:25:45.0\n'

In case of a minutes value less than 10:

    : 3, 9, 45, 0  # starting just before we begin to print

n   : 9, 45, .678  # accumulated stdout: '3'
[:] : :, 9, 45, .678
n   : 9, 45, .678  # accumulated stdout: '3:'
d   : 9, 9, 45, .678
Z   : 1, 9, 45, .678
2   : 2, 1, 9, 45, .678
>z  : 9, 45, .678  # greater, so register z is executed
  [0]   : 0, 9, 45, .678
  n     : 9, 45, .678  # accumulated stdout: '3:0' 
n   : 9, .678  # accumulated stdout: '3:09'
# ...and continues as above

EDIT: this had a bug where strings like 7:7:34.123 could be printed. I have modified it to print a leading zero if necessary.

EDIT 2: I realized that the handling of fractional seconds is redundant, and this simplified dc expression performs almost the same job:

$ echo '12345.678' | dc -e '?60~r60~r[[0]P]szn[:]ndZ2>zn[:]ndZ2>zp'
3:25:45.678
$ echo '12345.0' | dc -e '?60~r60~r[[0]P]szn[:]ndZ2>zn[:]ndZ2>zp'
3:25:45.0
$ echo '12345' | dc -e '?60~r60~r[[0]P]szn[:]ndZ2>zn[:]ndZ2>zp'
3:25:45

The downside of this is that different but equal inputs produce different outputs; any number of zeroes after the decimal point will be echoed verbatim in the output because of the fixed-point nature of dc.

Immorality answered 5/2, 2019 at 19:2 Comment(4)
+1, certainly a strong candidate for "unmaintainable code of the week award" :-) But seriously, thanks for reminding us about "dc", very powerful.Spohr
Thanks for this! I feel it's simpler overall if you hand the formatting off to another util, but I really appreciate what dc brings to the table here ie. a terse divide+remainder operation which allows the overall logic to be expressed without repetition. Anyway something like this is what I mean by handing the formatting off: echo 12345.678 | dc -e '? 1~r 60~r 60~r f' | paste -s | awk '{printf "%02d:%02d:%02d%s\n", $1, $2, $3, $4}'Vespine
Could be tweaked to allow multiple lines? Like the output of seq 12345 12349Verney
Definitely could be! I think you'd have to put the whole program into a register as a string macro with [] and then execute repeatedly by having it call itself at the end. I could imagine there's a limit to the number of nested macros, but maybe using c somewhere in there (as shown here www2.latech.edu/~acm/helloworld/dc1.html) would solve that. If you write it, I'd love to see it posted here!Immorality
V
9

A MacOS-specific answer which is using the OOTB /bin/date and does not require the GNU version of date:

# convert 195 seconds to MM:SS format, i.e. 03:15
/bin/date -ju -f "%s" 195 "+%M:%S"

## OUTPUT: 03:15

If you also want to have hours:

/bin/date -ju -f "%s" 3600 "+%H:%M:%S"
# OUTPUT: 01:00:00

NOTE: If you want to deal with hours then -u is required as it's forcing UTC time and without it you'll get wrong output unless you live in the UTC time zone:

-u      Display or set the date in UTC (Coordinated Universal) time.

For an explanation why -u is needed see this.

Votyak answered 3/4, 2021 at 8:9 Comment(3)
Even shorter: date -ur 3600 +%T. The -r tells it to read the input as seconds since the epoch and not set the system data (essentially equivalent to -j -f "%s"), and the %T format is short for %H:%M:%S. This works on macOS and at least NetBSD, but not with GNU's date command (i.e. on Linux), where the -r option means something completely different.Mikemikel
if the duration is longer than one day (no less than 86400 seconds), day part will overflow. any idea how to fix this?Larceny
@jackxujh: if you use "+%H:%M:%S" as the format then anything over one day will not fit and you'll get a rollover effect. See man date on mac and notice [+output_fmt] at the end. What you put there will depend on what you expect to get, for example /bin/date -ju -f "%s" 86400 "+%d,%H:%M:%S"; date is not really meant to work with intervals, so the syntax above is bordering on abuse; if you need more flexibility/granularity better resort to python/ruby/js or whatever you are more comfortable with.Votyak
M
9

Straight forward through awk:

echo $SECONDS | awk '{printf "%d:%02d:%02d", $1/3600, ($1/60)%60, $1%60}'
Marthena answered 16/11, 2021 at 5:17 Comment(2)
By far the best option!Knocker
thank you mate!Marthena
A
7

All above is for bash, disregarding there "#!/bin/sh" without any bashism will be:

convertsecs() {
    h=`expr $1 / 3600`
    m=`expr $1  % 3600 / 60`
    s=`expr $1 % 60`
    printf "%02d:%02d:%02d\n" $h $m $s
}
Antevert answered 8/9, 2013 at 12:46 Comment(1)
The POSIX shell can do arithmetic as well; replace expr $1 / 3600 with $(( $1 / 3600 )), for example. This is more efficient, as it does not require spawning a new process for each call to expr.Hebetic
M
6
t=12345;printf %02d:%02d:%02d\\n $((t/3600)) $((t%3600/60)) $((t%60)) # POSIX
echo 12345|awk '{printf "%02d:%02d:%02d",$0/3600,$0%3600/60,$0%60}' # POSIX awk
date -d @12345 +%T # GNU date
date -r 12345 +%T # OS X's date

If others were searching for how to do the reverse:

IFS=: read h m s<<<03:25:45;echo $((h*3600+m*60+s)) # POSIX
echo 03:25:45|awk -F: '{print 3600*$1+60*$2+$3}' # POSIX awk
Meyers answered 26/3, 2014 at 14:8 Comment(1)
Using the date command with the @-timespec seems so most convenient to me, since it is a simple one-liner: date -u -d "@3661" "+%H:%M:%S" gives 01:01:01 as expected.Subsidy
U
4

I couldn't get Vaulter's/chepner's code to work correctly. I think that the correct code is:

convertsecs() {
    h=$(($1/3600))
    m=$((($1/60)%60))
    s=$(($1%60))
    printf "02d:%02d:%02d\n $h $m $s
}
Unwholesome answered 29/9, 2013 at 16:56 Comment(2)
miss closing of " after \n ;-)Plum
And it should be %02d at the beginning ;-)Tildatilde
K
4

In one line :

show_time () {

    if [ $1 -lt 86400 ]; then 
        date -d@${1} -u '+%Hh:%Mmn:%Ss';
    else 
        echo "$(($1/86400)) days $(date -d@$(($1%86400)) -u '+%Hh:%Mmn:%Ss')" ;
    fi
}

Add days if exist.

Killoran answered 9/6, 2016 at 14:23 Comment(0)
R
2

Yet another version. Only handles full integers, doesn't pad with 0:

format_seconds() {
    local sec tot r

    sec="$1"

    r="$((sec%60))s"
    tot=$((sec%60))

    if [[ "$sec" -gt "$tot" ]]; then
        r="$((sec%3600/60))m:$r"
        let tot+=$((sec%3600))
    fi

    if [[ "$sec" -gt "$tot" ]]; then
        r="$((sec%86400/3600))h:$r"
        let tot+=$((sec%86400))
    fi

    if [[ "$sec" -gt "$tot" ]]; then
        r="$((sec/86400))d:$r"
    fi

    echo "$r"
}

$ format_seconds 59
59s
$ format_seconds 60
1m:0s
$ format_seconds 61
1m:1s
$ format_seconds 3600
1h:0m:0s
$ format_seconds 236521
2d:17h:42m:1s
Reinertson answered 4/3, 2021 at 14:8 Comment(0)
B
1

This is old post ovbioius -- but, for those who might are looking for the actual time elapsed but in military format (00:05:15:22 - instead of 0:5:15:22 )

!#/bin/bash
    num=$1
    min=0
    hour=0
    day=0
    if((num>59));then
        ((sec=num%60))
        ((num=num/60))
            if((num>59));then
            ((min=num%60))
            ((num=num/60))
                if((num>23));then
                    ((hour=num%24))
                    ((day=num/24))
                else
                    ((hour=num))
                fi
            else
                ((min=num))
            fi
        else
        ((sec=num))
    fi
    day=`seq -w 00 $day | tail -n 1`
    hour=`seq -w 00 $hour | tail -n 1`
    min=`seq -w 00 $min | tail -n 1`
    sec=`seq -w 00 $sec | tail -n 1`
    printf "$day:$hour:$min:$sec"
 exit 0
Bayonet answered 27/10, 2016 at 10:33 Comment(0)
P
1

on MacOSX 10.13 Slight edit from @eMPee584 's code to get it all in one GO (put the function in some .bashrc like file and source it, use it as myuptime. For non-Mac OS, replace the T formula by one that gives the seconds since last boot.

myuptime () 
{ 
    local T=$(($(date +%s)-$(sysctl -n kern.boottime | awk '{print $4}' | sed 's/,//g')));
    local D=$((T/60/60/24));
    local H=$((T/60/60%24));
    local M=$((T/60%60));
    local S=$((T%60));
    printf '%s' "UpTime: ";
    [[ $D > 0 ]] && printf '%d days ' $D;
    [[ $H > 0 ]] && printf '%d hours ' $H;
    [[ $M > 0 ]] && printf '%d minutes ' $M;
    [[ $D > 0 || $H > 0 || $M > 0 ]] && printf '%d seconds\n' $S
}
Palpebrate answered 13/2, 2018 at 8:43 Comment(0)
C
1

My pure SecToString function:

sec2str() {
    if [[ $1 == -v ]] ;then
        local -n _result=$2
        shift 2
    else
        local _result
    fi
    local -a _elapsed
    TZ=UTC printf -v _elapsed "%(%Y %j %H %M %S)T" $1
    read -a _elapsed <<<"$_elapsed"
    _elapsed[0]=$((10#${_elapsed[0]}-1970)) _elapsed[1]=$((10#${_elapsed[1]}-1))
    printf -v _elapsed " %dy %dd %.0fh %.0f' %.0f\"" ${_elapsed[@]}
    _result=${_elapsed// 0?}
    [[ ${_result@A} == _result=* ]] && echo "$_result"
}

In use:

sec2str 864001 
 10d 1"
sec2str -v str 864001
echo "$str"
 10d 1"
for i in 36 1036 91925 23590 7227 38688848;do
    sec2str -v str $i
    printf "%11d -> %20s\n" $i "$str"
done
         36 ->                  36"
       1036 ->              17' 16"
      91925 ->         1d 1h 32' 5"
      23590 ->           6h 33' 10"
       7227 ->               2h 27"
   38688848 ->    1y 82d 18h 54' 8"

Same but for µsec, obtained by ${EPOCHREALTIME/.}

I often use

jobstart=${EPOCHREALTIME/.}
process strong job
jobend=${EPOCHREALTIME/.}

So Elapsed time in µsec in integer= $(( jobend - jobstart )).

musec2str () { 
    if [[ $1 == -v ]] ;then local -n _result=$2; shift 2
    else local _result
    fi
    local _elapsed=000000$1
    printf -v _elapsed "%0.6f" ${_elapsed::-6}.${_elapsed:${#_elapsed}-6}
    TZ=UTC printf -v _result "%(%Y %j %H %M %S)T" ${_elapsed%.*}
    _elapsed=.${_elapsed#*.}
    if shopt -q extglob; then _elapsed=${_elapsed%%*(0)}
    else shopt -s extglob; _elapsed=${_elapsed%%*(0)}
        shopt -u extglob
    fi
    _elapsed=${_elapsed%.}
    [ "$_elapsed" ] || unset _elapsed
    read -ra _result <<<"$_result"
    _result[0]=$((10#${_result[0]}-1970)) _result[1]=$((10#${_result[1]}-1))
    printf -v _result "%dy %dd %.0fh %.0f' %.0fs" ${_result[@]}
    read -ra _result <<<"$_result"
    printf -v _result[0] "%s " ${_result[@]#0?}
    [ "${_result[0]#*s }" ] && { 
        [ "$_elapsed" ] && _result[0]=${_result[0]# }0
    } || { 
        [ "$_elapsed" ] && _result[0]=${_result[0]%s }\"
    }
    _result=("${_result[0]%s }${_elapsed}${_elapsed+\"}")
    [[ ${_result@A} == declare\ -a\ _result=* ]] && echo "$_result"
}

Then

musec2str -v str $(( jobend -jobstart ))
echo $str

for i in 14682081 38688848083836 3272661 12189780 23590528902 170295464 938372079
do
    musec2str -v str $i
    printf "%22u -> %32s\n" $i "$str"
done
              14682081 ->                       14.682081"
        38688848083836 ->         1y 82d 18h 54' 8.083836"
               3272661 ->                        3.272661"
              12189780 ->                        12.18978"
           23590528902 ->                6h 33' 10.528902"
             170295464 ->                    2' 50.295464"
             938372079 ->                   15' 38.372079"
read -r ut _ </proc/uptime
musec2str -v hUt $((${ut/./ * 10**6 + 10**4 * 10#}))
printf 'System is up from %s.\n' "$hUt"
System is up from 21d 21h 47' 20.1".

Note: In last sample, 2nd line could be written:

musec2str -v hUt ${ut/.}0000

For fun:

while read -r ut _ </proc/uptime ; musec2str -v hUt ${ut/.}0000;
    ! read -p $'\rSystem is up from '"$hUt " -rsn 1 -t .01 _;do :;done;echo

This will print uptime until you hit Any key.

Confessional answered 20/5, 2022 at 9:13 Comment(0)
T
0

This is my code for zshrc to show executed time of a command. It expect input as milliseconds, but easily can be converted to seconds:

ms_format() {
    local s=(d h m s ms) d=(86400000 3600000 60000 1000 1) r= t=$1 i=0 v=
    for i in {1..5}; do
        v=$(( $t / ${d[i]} ))
        [[ $v -gt 0 ]] && t=$(( $t - ($v * ${d[i]}) )) && r+="$v${s[i]} "
        [[ -n $r && $2 == "round" ]] && print -P "$r" && return 0
    done
    print -P "$r"
}

To use, easy as that:

    $ ms_format 1024
    # will print: 1s 24ms

You can use the option round, and then will show only the bigger value:

    $ ms_format 1024 "round"
    # will print: 1s

To use second as input, you can do this: (100 seconds input):

    $ ms_format $(( 100 * 1000 ))
    # will print: 1m 40s
Transient answered 29/9, 2022 at 16:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.