Cómo agregar duraciones de time expresadas en días-hh: min: formatting ss.nnn?

El formatting del file es el siguiente:

aaa 1-00:12:43.122 aaa 0-21:14:43.133 bbb 3-11:14:54.433 bbb 2-00:00:10 

Los numbers en la segunda columna tienen el formatting d-hh:mm:ss.nnn . Los últimos tres dígitos decimales están ausentes en algunos casos. Estoy tratando de calcular cómo agruparlos por primera columna para get la duración total de cada usuario. Entonces, para este ejemplo, el resultado sería:

 aaa 1-21:27:26.255 bbb 5-11:15:04.433 

Con perl , utilizando un hash y el module DateTime :: Format :: Duration , con una sustitución de expresiones regulares para regularizar los nanosegundos:

 perl -MDateTime::Format::Duration -ane ' BEGIN { $p = DateTime::Format::Duration->new(pattern => "%e-%H:%M:%S.%N"); } $F[1] =~ s/\d+(\.\d+)?$/sprintf "%02.9f", $&/e; $h{$F[0]} = $h{$F[0]} ? $dt->add_duration($h{$F[0]}) : $dt if $dt = $p->parse_duration($F[1]); END { $p = DateTime::Format::Duration->new(pattern => "%e-%H:%M:%S.%3N", normalize => 1); for $k (sort keys %h) {printf "%s %s\n", $k, $p->format_duration($h{$k})} } ' file aaa 1-21:27:26.255 bbb 5-11:15:04.433 

Usa este script awk :

 BEGIN { FS=" |-|:" } { data[$1][2]+=$2 data[$1][3]+=$3 data[$1][4]+=$4 data[$1][5]+=$5 } END { for( record in data ) { if( data[record][5]>=60 ) { data[record][4]+=1 data[record][5]-=60.0 } if( data[record][4]>=60 ) { data[record][3]+=1 data[record][4]-=60 } if( data[record][3]>=24 ) { data[record][2]+=1 data[record][3]-=24 } printf( "%s %d-%02.0f:%02.0f:%06.3f\n", record, data[record][2], data[record][3], data[record][4], data[record][5] ) } } 

Uso:

 ~/scratch/se/379631$ cat input aaa 1-00:12:43.122 aaa 0-21:14:43.133 bbb 3-11:14:54.433 bbb 2-00:00:10 ~/scratch/se/379631$ gawk -f 379631.awk input aaa 1-21:27:26.255 bbb 5-11:15:04.433 
 perl -F'\h+|[-.:]' -lane ' $h[keys %h]=$F[0] unless $h{$F[0]}; $h{$F[0]}[$_-1] += $F[$_] for 1..$#F}{for ( @h ) { my @Arefs = map { \$_ } my($days, $hrs, $mins, $secs, $msec) = @{$h{$_}}; while ( $msec >= 1000 ) { $secs++; $msec -= 1000; } while ( $secs >= 60 ) { $mins++; $secs -= 60; } while ( $mins >= 60 ) { $hrs++; $mins -= 60; } while ( $hrs >= 24 ) { $days++; $hrs -= 24; } print $_, sprintf " %d-%02d:%02d:%02d.%03d", map $$_, @Arefs; } ' text.file perl -F'\h+|[-.:]' -lane ' $h[keys %h]=$F[0] unless $h{$F[0]}; $h{$F[0]}[$_-1] += $F[$_] for 1..$#F}{for ( @h ) { use integer; my @A = @{$h{$_}}; $A[0] += ($A[1] += ($A[2] += ($A[3] += $A[4]/1000)/60)/60)/24; $A[4] %= 1000; $A[3] %= 60;$A[2] %= 60; $A[1] %= 24; print $_, sprintf " %d-%02d:%02d:%02d.%03d", @A; } ' text_file 

Resultados

 aaa 1-21:27:26.255 bbb 5-11:15:04.433 

Explicación

  • El separador de campo está configurado para separar los milisegundos, segundos, minutos, horas, días y la primera key de campo.
  • La línea de input se divide y almacena en una matriz @F con los elementos: $ F [0] -> key (aaa / bbb / etc.) $ F [1] -> días, $ F [2] -> horas , $ F [3] -> minutos, $ F [4] -> segundos, $ F [5] -> milisegundos.
  • Las keys hash por su propia naturaleza no se acceden en el order en que se crearon, por lo tanto, tenemos una matriz @h cuyos elementos son hash %h keys en el order en que se vieron.
  • Hash %h está constituido de esta manera:

 %h = ( aaa => [ days, hours, minutes, seconds, milliseconds ], bbb => [ ... ], ... );