Displaying seconds as days/hours/mins/seconds?

Is it possible to easily format seconds as a human-readable time in bash?

I don’t want to format it as a date, but as the number of days/hours/minutes, etc…

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

You can use something like this:

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 secondsn' $S
}

Examples:

$ displaytime 11617
3 hours 13 minutes and 37 seconds
$ displaytime 42
42 seconds
$ displaytime 666
11 minutes and 6 seconds

Method 2

Easiest and cleanest way is this one liner (here assuming GNU date):

If the number of seconds is, say:

seconds=123456789 # as in one of the answers above

eval "echo $(date -ud "@$seconds" +'$((%s/3600/24)) days %H hours %M minutes %S seconds')"

–> output: 1428 days 21 hours 33 minutes 09 seconds

Method 3

Credit goes to Stéphane Gimenez but if someone would like to display seconds only if a period is less than a minute here is my modified version that I use (also with fixed pluralization):

converts()
{
    local t=$1

    local d=$((t/60/60/24))
    local h=$((t/60/60%24))
    local m=$((t/60%60))
    local s=$((t%60))

    if [[ $d > 0 ]]; then
            [[ $d = 1 ]] && echo -n "$d day " || echo -n "$d days "
    fi
    if [[ $h > 0 ]]; then
            [[ $h = 1 ]] && echo -n "$h hour " || echo -n "$h hours "
    fi
    if [[ $m > 0 ]]; then
            [[ $m = 1 ]] && echo -n "$m minute " || echo -n "$m minutes "
    fi
    if [[ $d = 0 && $h = 0 && $m = 0 ]]; then
            [[ $s = 1 ]] && echo -n "$s second" || echo -n "$s seconds"
    fi  
    echo
}

An alternative example in POSIX:

converts(){
    t=$1

    d=$((t/60/60/24))
    h=$((t/60/60%24))
    m=$((t/60%60))
    s=$((t%60))

    if [ $d -gt 0 ]; then
            [ $d = 1 ] && printf "%d day " $d || printf "%d days " $d
    fi
    if [ $h -gt 0 ]; then
            [ $h = 1 ] && printf "%d hour " $h || printf "%d hours " $h
    fi
    if [ $m -gt 0 ]; then
            [ $m = 1 ] && printf "%d minute " $m || printf "%d minutes " $m
    fi
    if [ $d = 0 ] && [ $h = 0 ] && [ $m = 0 ]; then
            [ $s = 1 ] && printf "%d second" $s || printf "%d seconds" $s
    fi
    printf 'n'
}

Method 4

I’d do it like this:

$ seconds=123456789; echo $((seconds/86400))" days "$(date -d "1970-01-01 + $seconds seconds" "+%H hours %M minutes %S seconds")
1428 days 21 hours 33 minutes 09 seconds
$

Here’s the one liner above, broken down so that it’s easier to understand:

$ seconds=123456789
$ echo $((seconds/86400))" days"
     $(date -d "1970-01-01 + $seconds seconds" "+%H hours %M minutes %S seconds")

In the above I’m echoing out the output of another command that’s run inside the $( ... ) sub-command. That sub-command is doing this, calculating the number of days (seconds/86400), then using the date command in another sub-command $(date -d ... ), to generate the hours, minutes, and seconds for a given number of seconds.

Method 5

I modified the displaytime function above… as follows:

seconds2time ()
{
   T=$1
   D=$((T/60/60/24))
   H=$((T/60/60%24))
   M=$((T/60%60))
   S=$((T%60))

   if [[ ${D} != 0 ]]
   then
      printf '%d days %02d:%02d:%02d' $D $H $M $S
   else
      printf '%02d:%02d:%02d' $H $M $S
   fi
}

because I always want to see HH:MM:SS, even if they are zeros.

Method 6

I’m building on atti’s answer which I liked as an idea.

You can do this with the bash builtin printf which will take the seconds since the epoch as an argument. No need to fork to run date.

You have to set the timezone to UTC for printf because it formats the time in your local timezone and you will get the wrong answer if you are not in UTC time.

$ seconds=123456789
$ TZ=UTC printf "%d days %(%H hours %M minutes %S seconds)Tn" $((seconds/86400)) $seconds
1428 days 21 hours 33 minutes 09 seconds

In my local time (which is currently NZDT – +1300) the answer is wrong if I do not set the timezone

$ seconds=123456789
$ printf "%d days %(%H hours %M minutes %S seconds)Tn" $((seconds/86400)) $seconds
1428 days 09 hours 33 minutes 09 seconds

With and without setting the timezone

$ seconds=$(( 3600 * 25))
$ printf "%d days %(%H hours %M minutes %S seconds)Tn" $((seconds/86400)) $seconds
1 days 13 hours 00 minutes 00 seconds

$ TZ=UTC printf "%d days %(%H hours %M minutes %S seconds)Tn" $((seconds/86400)) $seconds
1 days 01 hours 00 minutes 00 seconds

Method 7

In addition to the other answers, to get an output in [[[[d and ]hh:]mm:]ss | ss 's'] format it could be done like this:

function format_seconds() {
  (($1 >= 86400)) && printf '%d days and ' $(($1 / 86400)) # days
  (($1 >= 3600)) && printf '%02d:' $(($1 / 3600 % 24))     # hours
  (($1 >= 60)) && printf '%02d:' $(($1 / 60 % 60))         # minutes
  printf '%02d%sn' $(($1 % 60)) "$( (($1 < 60 )) && echo ' s.' || echo '')"
}

For example, if we execute:

format_seconds 1000000
format_seconds 86450
format_seconds 9000
format_seconds 2500
format_seconds 60
format_seconds 34

We will get the following output:

11 days and 13:46:40
1 days and 00:00:50
02:30:00
41:40
01:00
34 s.

Although the question was asked more than 8 years ago i hope it helps to someone 🙂

Method 8

Building on Stéphane Gimenez’s answer, but an alternative to dimir’s:

Since printf is already in play, may as well use %s and pass is the s (or not) to pluralize when needed:

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 day%s ' $D $( (( $D > 1 )) && echo s)
  (( $H > 0 )) && printf '%d hour%s ' $H $( (( $H > 1 )) && echo s)
  (( $M > 0 )) && printf '%d minute%s ' $M $( (( $M > 1 )) && echo s)
  (( $D > 0 || $H > 0 || $M > 0 )) && printf 'and '
  printf '%d second%sn' $S $( (( $S != 1 )) && echo s)
}

Method 9

Here one

secs=378444
echo $(($secs/86400))d $(($(($secs - $secs/86400*86400))/3600))h:$(($(($secs - $secs/86400*86400))%3600/60))m:$(($(($secs - $secs/86400*86400))%60))s

Output:

4d 9h:7m:24s

Method 10

date --date '@1005454800' gives you Sun Nov 11 00:00:00 EST 2001, which is 1005454800 seconds after the Unix epoch. You can format that with the date +FORMAT option.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x