Obter diferença de hora de duas datas usando o awk

4

Eu tenho um arquivo de tamanho 7GB. Agora tenho duas datas em que eu quero usar o awk para obter essa diferença de tempo entre os dois DateTime.

Abaixo está a aparência dos meus arquivos:

A          B      C      D         E
18/06/28 09:19:07 295  141536 18-06-28 09:17:47
18/06/28 09:20:07 268  1160   18-06-28 09:18:58
18/06/28 09:21:07 317  1454   18-06-28 09:19:47
18/06/28 09:22:07 275  1491   18-06-28 09:20:59
18/06/28 09:23:07 320  1870   18-06-28 09:21:07
18/06/28 09:24:07 310  1869   18-06-28 09:22:30
18/06/28 09:25:07 150   693   18-06-28 09:23:28
18/06/28 09:26:07 414  2227   18-06-28 09:24:34 

Eu quero a diferença entre (AB) - (E).

Eu tentei isso:

cat filename | awk -F " " '{print date -d ($1$2)-($5)}' 

A saída deve ser a diferença de tempo entre os dois datetime. Como para a primeira diferença de linha será 1min 20sec

    
por Developer 28.06.2018 / 18:57

3 respostas

5

Usando o GNU awk:

gawk '
  function dt2epoch(date, time,      timestr) {
    timestr = "20" substr(date,1,2) " " substr(date,4,2) " " substr(date,7,2) \
               " " substr(time,1,2) " " substr(time,4,2) " " substr(time,7,2)
    return mktime(timestr)
  }
  function epoch2hms(t) {
    return strftime("%H:%M:%S", t, 1)
  }
  function abs(n) {return n<0 ? -1*n : n}
  NR == 1 {next}
  { print epoch2hms(abs(dt2epoch($5,$6) - dt2epoch($1,$2))) }
' file

saídas

00:01:20
00:01:09
00:01:20
00:01:08
00:02:00
00:01:37
00:01:39
00:01:33

com o perl, eu usaria o ecossistema DateTime :

perl -MDateTime::Format::Strptime -lane '
    BEGIN {$f = DateTime::Format::Strptime->new(pattern => "%y-%m-%d %H:%M:%S")}
    next if $. == 1;
    $F[0] =~ s{/}{-}g;
    $t1 = $f->parse_datetime("$F[0] $F[1]");
    $t2 = $f->parse_datetime("$F[4] $F[5]");
    $d = $t1->subtract_datetime($t2);
    printf "%02d:%02d:%02d\n", $d->hours, $d->minutes, $d->seconds;
' file

Uma versão perl muito mais rápida, que não requer nenhum módulo não núcleo

perl -MTime::Piece -lane '
    next if $. == 1;
    $t1 = Time::Piece->strptime("$F[0] $F[1]", "%y/%m/%d %H:%M:%S");
    $t2 = Time::Piece->strptime("$F[4] $F[5]", "%y-%m-%d %H:%M:%S");
    $diff = gmtime(abs($t1->epoch - $t2->epoch));
    print $diff->hms;
' file

ou saída alternativa:

$ perl -MTime::Piece -lane '
    next if $. == 1;
    $t1 = Time::Piece->strptime("$F[0] $F[1]", "%y/%m/%d %H:%M:%S");
    $t2 = Time::Piece->strptime("$F[4] $F[5]", "%y-%m-%d %H:%M:%S");
    print abs($t1 - $t2)->pretty;
' file
1 minutes, 20 seconds
1 minutes, 9 seconds
1 minutes, 20 seconds
1 minutes, 8 seconds
2 minutes, 0 seconds
1 minutes, 37 seconds
1 minutes, 39 seconds
1 minutes, 33 seconds
    
por 28.06.2018 / 19:14
0

Usando bash e awk combinados:

$ awk 'NR>1 {print $1,$2,$5,$6}' input  | while read d1 t1 d2 t2; do
  i1=$(date -u -d "20$d1 $t1" +%s)
  i2=$(date -u -d "20$d1 $t2" +%s)
  date -d @"$((i1-i2))" +%M:%S; 
done
01:20
01:09
01:20
01:08
02:00
01:37
01:39
01:33
    
por 28.06.2018 / 19:16
0

avaliação comparativa: eu dupliquei seus dados de amostra várias vezes

$ wc -l file
131073 file

Agora, algum tempo:

$ time  awk 'NR>1 {print $1,$2,$5,$6}' file |
while read d1 t1 d2 t2; do
  i1=$(date -u -d "20$d1 $t1" +%s)
  i2=$(date -u -d "20$d1 $t2" +%s)
  date -d @"$((i1-i2))" +%M:%S
done > /dev/null

real    8m55.533s
user    5m46.956s
sys     1m33.726s
$ time perl -MDateTime::Format::Strptime -lane '
    BEGIN {$f = DateTime::Format::Strptime->new(pattern => "%y-%m-%d %H:%M:%S")}
    next if $. == 1;
    $F[0] =~ s{/}{-}g;
    $t1 = $f->parse_datetime("$F[0] $F[1]");
    $t2 = $f->parse_datetime("$F[4] $F[5]");
    $d = $t1->subtract_datetime($t2);printf "%02d:%02d:%02d\n", $d->hours, $d->minutes, $d->seconds;
' file > /dev/null

real    0m37.684s
user    0m33.168s
sys     0m0.058s
$ time gawk '
  function dt2epoch(date, time,      timestr) {
    timestr = "20" substr(date,1,2) " " substr(date,4,2) " " substr(date,7,2) \
               " " substr(time,1,2) " " substr(time,4,2) " " substr(time,7,2)
    return mktime(timestr)
  }
  function epoch2hms(t) {
    return strftime("%H:%M:%S", t, 1)
  }
  function abs(n) {return n<0 ? -1*n : n}
  NR == 1 {next}
  { print epoch2hms(abs(dt2epoch($5,$6) - dt2epoch($1,$2))) }
' file > /dev/null

real    0m1.074s
user    0m0.610s
sys     0m0.366s

O GNU awk, com as funções de tempo embutidas, é o vencedor claro, mesmo com toda a manipulação de strings.

Atualização: nova implementação do perl. Ainda mais lento do que gawk, mas milhas à frente da versão usando o módulo DateTime rico em recursos, mas pesado:

$ time perl -MTime::Piece -lane '
    next if $. == 1;
    $t1 = Time::Piece->strptime("$F[0] $F[1]", "%y/%m/%d %H:%M:%S");
    $t2 = Time::Piece->strptime("$F[4] $F[5]", "%y-%m-%d %H:%M:%S");
    $diff = gmtime(abs($t1->epoch - $t2->epoch));
    print $diff->hms;           
' file > /dev/null

real    0m2.631s
user    0m2.231s
sys     0m0.170s
    
por 28.06.2018 / 23:12

Tags