Analysieren von ISO8601-Daten mit dem Befehl linux date

15

Ich versuche, den Dateibefehl zu verwenden, um einen Dateizeitstempel zu generieren, den der Dateibefehl selbst interpretieren kann. Der Befehl date scheint jedoch keine eigene Ausgabe zu mögen, und ich bin nicht sicher, wie ich das umgehen soll. Ein typisches Beispiel:

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

Das Datum scheint die Zeichenfolge mit einem Versatz von 15 Stunden zu interpretieren. Gibt es dafür bekannte Workarounds?

Bearbeiten: Dies ist kein Problem der Anzeige:

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

Bei Anzeige als Unix-Zeitstempel ist die Zeit immer noch um 15 Stunden verrutscht.

EDIT # 1

Vielleicht sollte ich diese Frage etwas anders stellen. Angenommen, ich habe eine Liste der ISO8601-Basiszeitstempel des Formulars:

  • JJJJMMTThhmm
  • JJJJMMTThhmmss

Was ist die einfachste Möglichkeit, sie in die entsprechenden Unix-Zeitstempel umzuwandeln?

Beispielsweise:

- 20140103T1422   = 1388787720
- 20140103T142233 = 1388787753
alex.forencich
quelle
1
@drewbenn Ich kann keine Sonderzeichen im Zeitstempel haben. Nur Zahlen und Buchstaben. Also nein, das kann ich leider nicht.
alex.forencich
@sim TZ ist nicht gesetzt, aber / etc / localtime ist verlinkt.
alex.forencich
Du bringst mich um, ist das deine letzte Frage? 8-)
slm
20140103T1518ist nicht gültig nach ISO 8601, Zeitzone
fehlt

Antworten:

9

Sie fragen nach "bekannten Problemumgehungen". Hier ist eine einfache:

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

Dies wird verwendet sed, um "T" durch ein Leerzeichen zu ersetzen. Das Ergebnis ist ein Format, das dateversteht.

Wenn wir dem ISO8601-Datum Sekunden hinzufügen, datesind weitere Änderungen erforderlich:

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

sedErsetzt oben das "T" durch ein Leerzeichen und trennt HHMMSS ebenfalls in HH: MM: SS.

John1024
quelle
Funktioniert bei mir wenn das + gelöscht wird. Es funktioniert jedoch nicht für Zeitstempel mit zweiter Genauigkeit, sondern nur mit Minutengenauigkeit.
alex.forencich
@ alex.forencich Antwort sekundengenau aktualisiert. Lassen Sie mich wissen, ob das von mir gewählte Sekundenformat nicht das von Ihnen benötigte ist.
John1024
8

Das Coreutils-Info-Dokument besagt, dass ISO 8601 "erweitertes Format" unterstützt wird.

Sie müssen Bindestriche, Doppelpunkte und ein hinzufügen, +%zdamit es funktioniert.

$ 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

Um Ihren zweiten Teil der Frage zu beantworten ...

Da das Datumsformat nur Zahlen und Symbole enthält, können Sie jedes Symbol durch einen eindeutigen Buchstaben ersetzen, z. B. mit 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

Oder Sie können es mit Tund -oder +als Trennzeichen analysieren , z. B. mit Shell ${var%word}und ${var#word}Expansion

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

oder mit bashregulären Ausdrücken abgleichen

$ 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

oder Perl, Python usw. usw.

Mikel
quelle
Der Zeitstempel darf keine Sonderzeichen enthalten. Kennen Sie eine gute Möglichkeit, diese automatisch wieder hinzuzufügen?
alex.forencich
6

GNU-Coreutils haben seit Version 8.13 (veröffentlicht am 08.09.2011) nur ISO 8601-Daten als Eingabe unterstützt . Sie müssen eine ältere Version verwenden.

In älteren Versionen müssen Sie das Zeichen Tdurch ein Leerzeichen ersetzen . Ansonsten wird es als Zeitzone des US-Militärs interpretiert .

Selbst in neueren Versionen wird nur die vollständig unterbrochene Form erkannt, nicht das Basisformat mit nur Ziffern und einem Tin der Mitte.

# 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 - hör auf böse zu sein'
quelle
2

Ich habe diesen Hinweis in der Manpage für bemerkt 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.

Es ist nicht schlüssig, zeigt aber nicht explizit eine Zeitformat-Zeichenfolge an, die die Zeichenfolge Tfür [ISO 8601] enthält, wie Sie es versuchen. Wie @Gilles antwortete , ist die Unterstützung von ISO 8601 in GNU CoreUtils relativ neu.

Formatieren Sie die Zeichenfolge neu

Sie können Perl verwenden, um Ihre Zeichenfolge neu zu formulieren.

Beispiel:

$ 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

Sie können dafür sorgen, dass sowohl Zeichenfolgen mit Sekunden als auch solche ohne Sekunden verarbeitet werden.

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
quelle
@ alex.forencich - ein alternativer Befehl, der beide Zeitformate verarbeitet. Tu mir einen Gefallen und lösche die Kommentare, die nicht mehr relevant sind.
SLM
1

Gemäß der Manpage of Date stimmt das von Ihnen ausgegebene Format nicht mit dem dateerwarteten Format für die Eingabe überein. Dies ist, was die Manpage sagt:

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

Du könntest es also so machen:

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

Da in den Variablen, die zum Definieren der Ausgabezeichenfolge verwendet +%m%d%H%M%Ywerden, gleich wäre, was es als Eingabe erwartet.

Wiederholung
quelle
Können Sie dann einen Befehl bereitstellen, um ein Datum im ISO8601-Format auf das erforderliche Datum abzubilden? Die aktuell gespeicherten Zeitstempel müssen im ISO8601-Format vorliegen, damit sie nach Datum sortiert werden können.
alex.forencich