Come analizzare le date ISO8601 con il comando date di linux


15

Sto cercando di utilizzare il comando date per generare un timestamp di file che il comando date stesso può interpretare. Tuttavia, al comando date non sembra piacere il proprio output e non sono sicuro di come aggirare questo problema. Caso in questione:

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

la data sembra interpretare la stringa con un offset di 15 ore. Ci sono soluzioni alternative conosciute per questo?

Modifica: questo non è un problema di visualizzazione:

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

È ancora spento per 15 ore quando viene visualizzato come timestamp unix.

EDIT # 1

Forse dovrei porre questa domanda in modo leggermente diverso. Supponiamo di avere un elenco di timestamp di base ISO8601 del modulo:

  • YYYYMMDDThhmm
  • YYYYMMDDThhmmss

Qual è il modo più semplice per convertirli nei corrispondenti timestamp Unix?

Per esempio:

- 20140103T1422   = 1388787720
- 20140103T142233 = 1388787753

1
@drewbenn Non riesco ad avere caratteri speciali nel timestamp. Solo numeri e lettere. Quindi no, purtroppo non posso farlo.
alex.forencich,

@sim TZ non è impostato, ma / etc / localtime è collegato.
alex.forencich,

Mi stai uccidendo, è questa la tua ultima domanda? Cool
slm

20140103T1518non è valido ISO 8601, manca la parte del fuso orario
Ferrybig

Risposte:


9

Chiedete "soluzioni alternative conosciute". Eccone uno semplice:

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

Questo utilizza sedper sostituire "T" con uno spazio. Il risultato è un formato che datecapisce.

Se aggiungiamo secondi alla data ISO8601, sono datenecessarie ulteriori modifiche:

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

In quanto sopra, sedsostituisce la "T" con uno spazio e separa anche HHMMSS in HH: MM: SS.


Funziona per me se il + viene eliminato. Tuttavia, non funziona per i timestamp di seconda precisione, solo una precisione minima.
alex.forencich,

@ alex.forencich Risposta aggiornata con precisione dei secondi. Fammi sapere se il formato dei secondi che ho scelto non è quello che ti serve.
Giovanni 1024

8

I documenti informativi di coreutils affermano che il "formato esteso" ISO 8601 è supportato.

Dovrai aggiungere trattini, due punti e un +%zper farlo funzionare.

$ 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

Per rispondere alla tua seconda parte della domanda ...

Poiché il formato della data contiene solo numeri e simboli, è possibile sostituire ogni simbolo con una lettera univoca, ad esempio utilizzando 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

Oppure puoi analizzarlo usando Te -e +come separatori, ad esempio usando shell ${var%word}ed ${var#word}espansione

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

o usando bashla corrispondenza delle espressioni regolari

$ 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

o Perl, Python, ecc. ecc.


Il timestamp non può contenere caratteri speciali. Conosci un buon modo per aggiungerli automaticamente?
alex.forencich,

6

I coreutils GNU hanno supportato solo le date ISO 8601 come input dalla versione 8.13 (rilasciata il 2011-09-08). Devi utilizzare una versione precedente.

Nelle versioni precedenti, è necessario sostituire uno Tcon uno spazio. Altrimenti viene interpretato come un fuso orario militare statunitense .

Anche nelle versioni recenti, viene riconosciuta solo la forma completamente punteggiata, non il formato base con solo cifre e una Tal centro.

# 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"

2

Ho notato questa nota nella pagina man di 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.

Non è conclusivo, ma non mostra esplicitamente una stringa di formato orario che include il Tmetodo che stai tentando, per [ISO 8601]. Come indicato dalla risposta di @Gilles , il supporto di ISO 8601 in GNU CoreUtils è relativamente nuovo.

Riformattazione della stringa

Puoi usare Perl per riformulare la tua stringa.

Esempio:

$ 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

Puoi fare in modo che gestiscano entrambe le stringhe che includono secondi e quelle che non lo fanno.

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

@ alex.forencich - un comando alternativo che gestirà entrambi i formati di ora. Fammi un favore ed elimina i commenti sopra che non sono più pertinenti.
slm

1

Secondo la pagina man della data, il formato che si genera non è lo stesso di quello che si dateaspetta come input. Questo è ciò che dice la pagina man:

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

Quindi potresti farlo in questo modo:

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

Perché nelle variabili utilizzate per definire la stringa di output, +%m%d%H%M%Ysarebbe uguale a ciò che si aspetta come input.


Quindi puoi fornire un comando per mappare una data in formato ISO8601 in quale data richiede? I timestamp memorizzati effettivi devono essere in formato ISO8601 in modo che possano essere ordinati per data.
alex.forencich,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.