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
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..
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
}
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 )
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.
$ 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.
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
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.