Cómo analizar las dates de ISO8601 con el command de date de Linux

Estoy tratando de usar el command de date para generar una timestamp de file que el command de date mismo puede interpretar. Sin embargo, al command de date no parece gustarle su propio resultado, y no estoy seguro de cómo solucionarlo. Caso en punto:

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 date parece interpretar la cadena con un desplazamiento de 15 horas. ¿Hay alguna solución conocida para esto?

Editar: esto no es un problema de visualización:

 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", "cnetworkingits" or "license" for more information. >>> 1388737080 - 1388791096 -54016 >>> 54016/3600 15.004444444444445 >>> 

Todavía está apagado por 15 horas cuando se muestra como una timestamp de Unix.

EDIT # 1

Tal vez debería plantear esta pregunta un poco diferente. Supongamos que tengo una list de las marcas de time básicas ISO8601 del formulario:

  • YYYYMMDDThhmm
  • YYYYMMDDThhmmss

¿Cuál es la forma más sencilla de convertirlos a las marcas de time Unix correspondientes?

Por ejemplo:

 - 20140103T1422 = 1388787720 - 20140103T142233 = 1388787753 

Usted solicita "soluciones temporales conocidas". Aquí hay uno simple:

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

Esto usa sed para replace "T" con un espacio. El resultado es un formatting que la date comprende.

Si agregamos segundos a la date ISO8601, entonces la date requiere más cambios:

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

En lo anterior, sed reemplaza la "T" con un espacio y también separa HHMMSS en HH: MM: SS.

Los documentos de información de coreutils dicen que ISO 8601 "formatting extendido" es compatible.

Tendrá que agregar guiones, dos puntos y a +%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 a su segunda parte de la pregunta …

Como el formatting de date solo contiene numbers y símbolos, puede replace cada símbolo con una letra única, por ejemplo, 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 

O puede analizarlo usando la T y la - o + como separadores, por ejemplo, usando shell ${var%word} y ${var#word} expansión

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

o usando bash expresión regular que coincida

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

Los coreutils de GNU solo han admitido las dates ISO 8601 como input desde la versión 8.13 (lanzada el 2011-09-08). Debes estar usando una versión anterior.

En versiones anteriores, debe replace la T por un espacio. De lo contrario, se interpreta como una zona horaria militar de los EE . UU .

Incluso en versiones recientes, solo se reconoce la forma totalmente puntuada, no el formatting básico con solo dígitos y una T en el medio.

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

Me di count de esta nota en la página del hombre para la 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. 

No es concluyente, pero no muestra explícitamente una cadena de formatting de time que incluye la T como está intentando, para [ISO 8601]. Como se indicó en la respuesta de @Gilles , el soporte de ISO 8601 en GNU CoreUtils es relativamente nuevo.

Reformateando la cadena

Puede usar Perl para reformular su cadena.

Ejemplo:

 $ 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 

Puede hacer que este maneje ambas cadenas, que incluyen segundos y las que no.

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 

De acuerdo con la página de date del manual, el formatting que usted emite no es el mismo que la date que date espera como input. Esto es lo que dice la página man:

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

Entonces podrías hacerlo así:

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

Porque en las variables que se utilizan para definir la cadena de salida, +%m%d%H%M%Y sería igual a lo que espera como input.