Como analisar datas ISO8601 com o comando linux date

15

Estou tentando usar o comando date para gerar um registro de data e hora de arquivo que o próprio comando date pode interpretar. No entanto, o comando de data não parece gostar de sua própria saída, e não tenho certeza de como contornar isso. Caso em questão:

sh-4.2$ date
Fri Jan  3 14:22:19 PST 2014
sh-4.2$ date +%Y%m%dT%H%M
20140103T1422
sh-4.2$ date -d "20140103T1422"
Thu Jan  2 23:22:00 PST 2014
A data

parece estar interpretando a string com um deslocamento de 15 horas. Existe alguma solução conhecida para isso?

Editar: isso não é um problema de exibição:

sh-4.2$ date +%s
1388791096
sh-4.2$ date +%Y%m%dT%H%M
20140103T1518
sh-4.2$ date -d 20140103T1518 +%s
1388737080
sh-4.2$ python
Python 3.3.3 (default, Nov 26 2013, 13:33:18) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1388737080 - 1388791096
-54016
>>> 54016/3600
15.004444444444445
>>> 

Ainda está desligado por 15 horas quando exibido como um registro de data e hora unix.

EDIT # 1

Talvez eu deva colocar essa questão um pouco diferente. Digamos que eu tenha uma lista de timestamps básicos ISO8601 no formulário:

  • AAAAMMDDThhmm
  • AAAAMMDDThhmmss

Qual é a maneira mais simples de convertê-los nos timestamps Unix correspondentes?

Por exemplo:

- 20140103T1422   = 1388787720
- 20140103T142233 = 1388787753
    
por alex.forencich 03.01.2014 / 23:56

5 respostas

9

Você pede "soluções conhecidas". Aqui está um simples:

$ date -d "$(echo 20140103T1422 | sed 's/T/ /')"
Fri Jan  3 14:22:00 PST 2014

Isso usa sed para substituir "T" por um espaço. O resultado é um formato que date entende.

Se adicionarmos segundos à data ISO8601, date precisará de mais alterações:

$ date -d "$(echo 20140103T142211 | sed -r 's/(.*)T(..)(..)(..)/ ::/')"
Fri Jan  3 14:22:11 PST 2014

Acima, sed substitui o "T" por um espaço e também separa HHMMSS em HH: MM: SS.

    
por 04.01.2014 / 00:19
6

Os documentos de informação do coreutils diz que o ISO 8601 "formato estendido" é suportado.

Você precisará adicionar hífens, dois pontos e um +%z para que funcione.

$ date +"%Y-%m-%dT%H:%M:%S%z"
2014-01-03T16:08:23-0800
$ date -d 2014-01-03T16:08:23-0800
Fri Jan  3 16:08:23 PST 2014

Para responder à sua segunda parte da pergunta ...

Como o formato de data contém apenas números e símbolos, você pode substituir cada símbolo por uma única, por exemplo, usando tr

$ ts="$(date +"%Y-%m-%dT%H:%M:%S%z" | tr -- '-:+' 'hcp')"; echo "$ts"
2014h01h03T16c18c04h0800
$ date -d "$(echo "$ts" | tr -- 'hcp' '-:+')"
Fri Jan  3 16:18:04 PST 2014

Ou você pode analisá-lo usando os separadores T e - ou + as, por exemplo usando shell ${var%word} e ${var#word} expansion

$ ts="$(date +"%Y%m%dT%H%M%S%z")"; echo "$ts"
20140103T162228-0800
$ date=${ts%T*}; time=${ts#*T}
etc.    

ou usando bash correspondência de expressão regular

$ ts="$(date +"%Y%m%dT%H%M%S%z")"; echo "$ts"
20140103T165611-0800
$ [[ "$ts" =~ (.*)(..)(..)T(..)(..)(..)(.....) ]]
$ match=("${BASH_REMATCH[@]}")
$ Y=${match[1]}; m=${match[2]}; d=${match[3]}; H=${match[4]}; M=${match[5]}; S=${match[6]}; z=${match[7]}
$ date -d "$Y-$m-$d"T"$H:$M:$S$z"
Fri Jan  3 16:56:11 PST 2014

ou Perl, Python, etc. etc.

    
por 04.01.2014 / 01:12
5

GNU coreutils suportam apenas ISO 8601 datas como entrada desde versão 8.13 (lançado em 2011-09-08). Você deve estar usando uma versão mais antiga.

Em versões mais antigas, você precisa substituir o T por um espaço. Caso contrário, isso é interpretado como um fuso horário militar dos EUA .

Mesmo nas versões recentes, somente o formulário totalmente pontuado é reconhecido, não o formato básico com apenas dígitos e um T no meio.

# Given a possibly abbreviated ISO date $iso_date...
date_part=${iso_date%%T*}
if [ "$date_part" != "$iso_date" ]; then
  time_part=${abbreviated_iso_date#*T}
  case ${iso_date#*T} in
    [!0-9]*) :;;
    [0-9]|[0-9][0-9]) time_part=${time_part}:00;;
    *)
      hour=${time_part%${time_part#??}}
      minute=${time_part%${time_part#????}}; minute=${minute#??}
      time_part=${hour}:${minute}:${time_part#????};;
  esac
else
  time_part=
fi
date -d "$date_part $time_part"
    
por 04.01.2014 / 02:47
2

Eu notei essa nota na man page de date .

DATE STRING
      The --date=STRING is a mostly free format human readable date string
      such as "Sun, 29 Feb 2004 16:21:42 -0800"  or  "2004-02-29
      16:21:42"  or  even  "next Thursday".  A date string may contain 
      items indicating calendar date, time of day, time zone, day of
      week, relative time, relative date, and numbers.  An empty string 
      indicates the beginning of the day.  The date  string  format
      is more complex than is easily documented here but is fully described 
      in the info documentation.

Não é conclusivo, mas não mostra explicitamente uma cadeia de formato de hora que inclua o T , como você está tentando, para [ISO 8601]. Como @Gilles answer indicado, o apoio de ISO 8601 no GNU CoreUtils é relativamente novo.

Reformatando a string

Você pode usar o Perl para reformular sua string.

Exemplo:

$ date -d "$(perl -pe 's/(.*)T(\d{2})(\d{2})(\d{2})/$1 $2:$3:$4/' \
    <<<"20140103T142233")"
Fri Jan  3 14:22:33 EST 2014

Você pode fazer isso manipular as duas strings que incluem segundos e as que não o fazem.

20140103T1422:

$ date -d "$(perl -pe 's/^(.*)T(\d{2})(\d{2})(\d{2})$/$1 $2:$3:$4/ || \
     s/^(.*)T(\d{2})(\d{2})$/$1 $2:$3:00/' <<<"20140103T1422")"
Fri Jan  3 14:22:00 EST 2014

20140103T142233:

$ date -d "$(perl -pe 's/^(.*)T(\d{2})(\d{2})(\d{2})$/$1 $2:$3:$4/ || \
     s/^(.*)T(\d{2})(\d{2})$/$1 $2:$3:00/' <<<"20140103T142233")"
Fri Jan  3 14:22:33 EST 2014
    
por 04.01.2014 / 00:05
1

De acordo com a página do manual de data, o formato que você produz não é o mesmo que date espera como entrada. Isto é o que a página man diz:

date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]

Então você poderia fazer assim:

# date +%m%d%H%M%Y
010402052014
# date 010402052014
Sat Jan  4 02:05:00 EAT 2014

Como nas variáveis usadas para definir a string de saída, +%m%d%H%M%Y seria igual ao que espera como entrada.

    
por 04.01.2014 / 00:06