¿Mostrar segundos como días / horas / minutos / segundos?

31

¿Es posible formatear fácilmente segundos como un tiempo legible para humanos en bash?

No quiero formatearlo como una fecha, sino como el número de días / horas / minutos, etc.

Tyilo
fuente
¿Podría proporcionar un ejemplo / varios ejemplos por favor?
Gabe.
1
¿Estás diciendo que tienes un intervalo o una fecha desde la época?
Paul Tomblin

Respuestas:

41

Puedes usar algo como esto:

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
}

Ejemplos:

$ displaytime 11617
3 hours 13 minutes and 37 seconds
$ displaytime 42
42 seconds
$ displaytime 666
11 minutes and 6 seconds
Stéphane Gimenez
fuente
11

La forma más fácil y limpia es este revestimiento (aquí asumiendo GNU date):

Si el número de segundos es, diga:

seconds=123456789 # as in one of the answers above

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

-> salida: 1428 days 21 hours 33 minutes 09 seconds

Jacques
fuente
Si su duración siempre será inferior a un día, puede usar el +%Tformato de Horas: Minutos: Segundos para que esto date +%T "1970-01-01 + ${seconds} seconds"llegue21:33:09
Yzmir Ramírez
7

El crédito es para Stéphane Giménez, pero si a alguien le gustaría mostrar segundos solo si un período es inferior a un minuto, esta es mi versión modificada que uso (también con pluralización fija):

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
}

Un ejemplo alternativo en 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'
}
dimir
fuente
Esto funcionó muy bien. Simplemente pegué la función en mi script y corrí convert $spara obtener una salida bonita.
flickerfly
3

Lo haría así:

$ 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
$

Aquí está el revestimiento de arriba, desglosado para que sea más fácil de entender:

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

En lo anterior, estoy haciendo eco de la salida de otro comando que se ejecuta dentro del $( ... )subcomando. Ese subcomando hace esto, calcula el número de días (segundos / 86400), luego usa el datecomando en otro subcomando $(date -d ... )para generar las horas, minutos y segundos para un número determinado de segundos.

atti
fuente
2

Modifiqué la función de tiempo de visualización anterior ... de la siguiente manera:

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
}

porque siempre quiero ver HH: MM: SS, incluso si son ceros.

Larry Helms
fuente
1

Estoy construyendo sobre la respuesta de atti que me gustó como idea.

Puede hacer esto con el bash incorporado, printfque tomará los segundos desde la época como argumento. No es necesario bifurcar para correr date.

Debe configurar la zona horaria en UTC printfporque formatea la hora en su zona horaria local y obtendrá la respuesta incorrecta si no está en la hora UTC.

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

En mi hora local (que actualmente es NZDT - +1300) la respuesta es incorrecta si no configuro la zona horaria

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

Con y sin configurar la zona horaria

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

$ TZ=UTC printf "%d days %(%H hours %M minutes %S seconds)T\n" $((seconds/86400)) $seconds
1 days 01 hours 00 minutes 00 seconds
Bill Ryder
fuente
Tenga en cuenta que bash printfintrodujo esta %(datefmt)Tnotación comenzando con bash-4.2-alpha.
obispo
-1

Aqui uno

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

Salida:

4d 9h:7m:24s
Shirish Shukla
fuente
-2

date --date '@1005454800'te da Sun Nov 11 00:00:00 EST 2001, que es 1005454800 segundos después de la época de Unix. Puede formatear eso con la opción de fecha + FORMATO.

Paul Tomblin
fuente
77
Esa es una fecha, no una duración, sobre la que se preguntaba.
Gilles 'SO- deja de ser malvado'