String de data de reformatação

2

Estou tentando descobrir como reformatar um arquivo cheio de datas formatadas incorretamente. A fonte é assim:

{"_id":"","timestamp":"Mon Apr 20 08:30:55 +0000 2015"}
{"_id":"","timestamp":"Mon Apr 20 08:32:25 +0000 2015"}
{"_id":"","timestamp":"Mon Apr 20 08:35:39 +0000 2015"}

Existem cerca de 3 milhões de entradas como esta. Preciso que o timestamp seja formatado no formato ISO-8601, ou seja: YYYY-MM-DDTHH:mm:ss.mmm<+/-Offset>

Eu tentei isso, o que funciona:

date -d "Mon Apr 20 08:35:39 +0000 2015" +%FT%T%z

Como obviamente não vou percorrer todos os 3 milhões de entradas manualmente, procurei usar sed para isso:

cat input.json | sed "s|\"timestamp\":\"\(.*\)\"|\"timestamp\":\"$(date -d  +%FT%T%z)\"|g" > output.json

No entanto, isso imprime a mesma saída (errada) a cada, ou seja, 2015-05-08T01:00:00+0000 . Há alguma mente brilhante em algum lugar por aqui que possa me ajudar?

    
por Diederik 08.05.2015 / 22:49

3 respostas

3

Seria possível usar sed para isso, mas awk é mais natural:

awk -F'"' -v OFS='"'  '$8 {cmd="date -d \""$8"\" +%FT%T%z"; cmd | getline $8; close(cmd)} 1' input.json
{"_id":"","timestamp":"2015-04-20T01:30:55-0700"}
{"_id":"","timestamp":"2015-04-20T01:32:25-0700"}
{"_id":"","timestamp":"2015-04-20T01:35:39-0700"}

Os itens acima mostram um deslocamento de -7: 00 horas. Isso reflete o fuso horário padrão do sistema. Alterar a variável do shell TZ mudará o padrão.

Como funciona

  • -F'"' -v OFS='"'

    Isso define os separadores de campos de entrada e saída como " .

  • $8 {cmd="date -d \""$8"\" +%FT%T%z"; cmd | getline $8; close(cmd)}

    Com " como o separador de campo, a data é o campo número 8. Isso cria uma cadeia com o comando date correto e, em seguida, executa o comando que captura a saída em um campo atualizado 8.

    O $8 na frente significa que esta parte só será executada se houver um valor não vazio para o campo 8. Isso permite, por exemplo, que linhas vazias passem sem ser molestadas.

  • 1

    Esta é a abreviada enigmática de awk para "imprimir esta linha".

Manipulação de citações duplas extras

Como estamos usando " como o separador de campo. Suponha que haja um número variável de " antes do registro de data e hora. Nesse caso, precisamos nos referir ao timestamp como o segundo último campo, $(NF-1) , em vez do oitavo campo, $8 . Neste caso:

awk -F'"' -v OFS='"'  '$8 {cmd="date -d \""$(NF-1)"\" +%FT%T%z"; cmd | getline $(NF-1); close(cmd)} 1' input.json

Adicionando formatação personalizada ao campo de data

$ awk -F'"' -v OFS='"'  '$8 {cmd="date -d \""$(NF-1)"\" +%FT%T%z"; cmd | getline $(NF-1); close(cmd);$(NF-1)="{$date:" $(NF-1) "}"} 1' input.json
{"_id":"","timestamp":"{$date:2015-04-20T01:30:55-0700}"}
{"_id":"","timestamp":"{$date:2015-04-20T01:32:25-0700}"}
{"_id":"","timestamp":"{$date:2015-04-20T01:35:39-0700}"}
    
por 08.05.2015 / 23:08
1

jq , node :

cat /tmp/what \
| jq '.timestamp' \
| while read line ; do \
  node -e "console.log(new Date($line).toISOString())" ;\
  done

Também pode usar um único programa Node.js para processar todo o arquivo em busca de desempenho, mas isso pode estar indo muito longe no JS-land. (Ping se você quiser detalhes.)

    
por 09.05.2015 / 00:23
0

Se você pode garantir que a entrada está formatada exatamente como você mostrou, você pode fazer isso inteiramente em sed . É um pouco de força bruta:

sed \
        -e 's/"timestamp":"... Jan/"timestamp":"01/' \
        -e 's/"timestamp":"... Feb/"timestamp":"02/' \
        -e 's/"timestamp":"... Mar/"timestamp":"03/' \
        -e 's/"timestamp":"... Apr/"timestamp":"04/' \
        -e 's/"timestamp":"... May/"timestamp":"05/' \
        -e 's/"timestamp":"... Jun/"timestamp":"06/' \
        -e 's/"timestamp":"... Jul/"timestamp":"07/' \
        -e 's/"timestamp":"... Aug/"timestamp":"08/' \
        -e 's/"timestamp":"... Sep/"timestamp":"09/' \
        -e 's/"timestamp":"... Oct/"timestamp":"10/' \
        -e 's/"timestamp":"... Nov/"timestamp":"11/' \
        -e 's/"timestamp":"... Dec/"timestamp":"12/' \
-e 's/"timestamp":"\(..\) \(..\) \(..:..:..\) \(.....\) \(....\)"/"timestamp":"--T"/' \
        input.json > output.json

Substitui o nome do mês pelo seu número e então cria a string de data (agora) totalmente numérica, e coloca as peças de volta na ordem desejada. Se você quiser adicionar .000 por milissegundos, insira-o entre e .

    
por 09.05.2015 / 04:30