Comment analyser les dates ISO8601 avec la commande Linux Date

15

J'essaie d'utiliser la commande date pour générer un horodatage de fichier que la commande date elle-même peut interpréter. Cependant, la commande date ne semble pas aimer sa propre sortie, et je ne sais pas comment contourner cela. Exemple concret:

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

date semble interpréter la chaîne avec un décalage de 15 heures. Existe-t-il des solutions connues à cela?

Edit: ce n'est pas un problème d'affichage:

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

Il est toujours éteint de 15 heures lorsqu'il est affiché sous forme d'horodatage Unix.

EDIT # 1

Je devrais peut-être poser cette question un peu différemment. Disons que j'ai une liste d'horodatages de base ISO8601 de la forme:

  • YYYYMMDDThhmm
  • YYYYMMDDThhmmss

Quelle est la manière la plus simple de les convertir en horodatages Unix correspondants?

Par exemple:

- 20140103T1422   = 1388787720
- 20140103T142233 = 1388787753
alex.forencich
la source
1
@drewbenn Je ne peux pas avoir de caractères spéciaux dans l'horodatage. Juste des chiffres et des lettres. Donc non, je ne peux pas faire ça, malheureusement.
alex.forencich
@sim TZ n'est pas défini, mais / etc / localtime est lié.
alex.forencich
Tu me tues, c'est ta dernière question? 8-)
slm
20140103T1518n'est pas valide ISO 8601, il manque la partie du fuseau horaire
Ferrybig

Réponses:

9

Vous demandez des «solutions de contournement connues». En voici une simple:

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

Cela permet sedde remplacer "T" par un espace. Le résultat est un format qui datecomprend.

Si nous ajoutons des secondes à la date ISO8601, cela datenécessite plus de changements:

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

Dans ce qui précède, sedremplace le "T" par un espace et sépare également HHMMSS en HH: MM: SS.

John1024
la source
Fonctionne pour moi si le + est supprimé. Cependant, cela ne fonctionne pas pour les horodatages de deuxième précision, seulement pour une précision infime.
alex.forencich
@ alex.forencich Réponse mise à jour avec une précision de secondes. Faites-moi savoir si le format des secondes que j'ai choisi n'est pas celui dont vous avez besoin.
John1024
8

La documentation info coreutils indique que le "format étendu" ISO 8601 est pris en charge.

Vous devrez ajouter des tirets, des deux-points et un +%zpour le faire fonctionner.

$ 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

Pour répondre à votre deuxième partie de la question ...

Étant donné que le format de date ne contient que des chiffres et des symboles, vous pouvez remplacer chaque symbole par une lettre unique, par exemple en utilisant 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 vous pouvez l'analyser en utilisant le Tet le -ou +comme séparateurs, par exemple en utilisant le shell ${var%word}et l' ${var#word}expansion

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

ou en utilisant bashla correspondance d'expressions régulières

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

Mikel
la source
L'horodatage ne peut pas contenir de caractères spéciaux. Connaissez-vous un bon moyen de les ajouter automatiquement?
alex.forencich
6

GNU coreutils ne prend en charge les dates ISO 8601 qu'en entrée depuis la version 8.13 (publiée le 2011-09-08). Vous devez utiliser une ancienne version.

Sous les anciennes versions, vous devez remplacer le Tpar un espace. Sinon, il est interprété comme un fuseau horaire militaire américain .

Même sous les versions récentes, seule la forme entièrement ponctuée est reconnue, pas le format de base avec seulement des chiffres et un Tau milieu.

# 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"
Gilles 'SO- arrête d'être méchant'
la source
2

J'ai remarqué cette note dans la page de manuel 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.

Ce n'est pas concluant, mais il n'affiche pas explicitement une chaîne de format d'heure qui inclut le Tcomme vous essayez, pour [ISO 8601]. Comme indiqué dans la réponse de @Gilles , la prise en charge d' ISO 8601 dans GNU CoreUtils est relativement nouvelle.

Reformater la chaîne

Vous pouvez utiliser Perl pour reformuler votre chaîne.

Exemple:

$ 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

Vous pouvez rendre cette poignée à la fois des chaînes qui incluent des secondes et celles qui ne le font pas.

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
slm
la source
@ alex.forencich - une commande alternative qui gérera les deux formats d'heure. Faites-moi une faveur et supprimez les commentaires ci-dessus qui ne sont plus pertinents.
slm
1

Selon la page de manuel de date, le format que vous sortez n'est pas le même que celui dateattendu en entrée. Voici ce que dit la page de manuel:

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

Vous pouvez donc le faire comme ceci:

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

Parce que dans les variables qui sont utilisées pour définir la chaîne de sortie, +%m%d%H%M%Yserait égale à ce qu'elle attend en entrée.

rejouer
la source
Pouvez-vous alors fournir une commande pour mapper une date au format ISO8601 à quelle date l'exige? Les horodatages stockés réels doivent être au format ISO8601 afin de pouvoir être triés par date.
alex.forencich