Как разобрать даты ISO8601 с помощью команды linux date

15

Я пытаюсь использовать команду даты для создания временной метки файла, которую сама команда даты может интерпретировать. Однако команде date, похоже, не нравится ее собственный вывод, и я не уверен, как обойти это. Дело в точке:

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

дата, кажется, интерпретирует строку со смещением 15 часов. Есть ли известные обходные пути для этого?

Изменить: это не проблема отображения:

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

Он все еще отключен на 15 часов, когда отображается как метка времени Unix.

РЕДАКТИРОВАТЬ # 1

Может быть, я должен задать этот вопрос немного по-другому. Скажем, у меня есть список основных временных отметок ISO8601 в форме:

  • YYYYMMDDThhmm
  • YYYYMMDDTHHMMSS

Какой самый простой способ преобразовать их в соответствующие метки времени Unix?

Например:

- 20140103T1422   = 1388787720
- 20140103T142233 = 1388787753
alex.forencich
источник
1
@drewbenn У меня не может быть никаких специальных символов в метке времени. Просто цифры и буквы. Так что нет, я не могу этого сделать, к сожалению.
alex.forencich
@sim TZ не установлен, но / etc / localtime связан.
alex.forencich
Ты убиваешь меня, это твой последний вопрос? 8-)
slm
20140103T1518недействителен ISO 8601, он пропускает часть часового пояса
Ferrybig

Ответы:

9

Вы просите «известные обходные пути». Вот простой:

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

Это использует sedдля замены «Т» с пробелом. Результатом является формат, который dateпонимает.

Если мы добавим секунды к дате ISO8601, то dateпотребуются дополнительные изменения:

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

В приведенном выше sedтексте «Т» заменяется пробелом, а также разделяет ЧЧММСС на ЧЧ: ММ: СС.

John1024
источник
У меня работает, если + удален. Тем не менее, он не работает для меток времени второй точности, только для минутной точности.
alex.forencich
@ alex.forencich Ответ обновлен с точностью до секунды. Дайте мне знать, если выбранный вами формат секунд не тот, который вам нужен.
John1024
8

В информационном документе coreutils говорится, что ISO 8601 "расширенный формат" поддерживается.

Вам нужно будет добавить дефисы, двоеточия, и, +%zчтобы это работало.

$ 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

Чтобы ответить на вашу вторую часть вопроса ...

Поскольку формат даты содержит только цифры и символы, вы можете заменить каждый символ уникальной буквой, например, используя 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

Или вы можете разобрать его, используя и Tи -или +как разделители, например, используя shell ${var%word}и ${var#word}расширение

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

или используя bashсопоставление регулярного выражения

$ 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

или Perl, Python и т. д. и т. д.

Mikel
источник
В отметке времени не должно быть никаких специальных символов. Знаете ли вы хороший способ добавить их обратно автоматически?
alex.forencich
6

GNU coreutils поддерживает только даты ISO 8601 в качестве входных данных начиная с версии 8.13 (выпущенной 2011-09-08). Вы должны использовать более старую версию.

В старых версиях вам необходимо заменить Tпробел. В противном случае он интерпретируется как военный часовой пояс США .

Даже в последних версиях распознается только полностью пунктуированная форма, а не базовый формат, состоящий только из цифр и символа a Tв середине.

# 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

Я заметил эту заметку на странице руководства для 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.

Он не является окончательным, но он явно не показывает строку формата времени, которая включает в себя, Tкак вы пытаетесь, для [ISO 8601]. Как указано в ответе @Gilles , поддержка ISO 8601 в GNU CoreUtils является относительно новой.

Переформатирование строки

Вы можете использовать Perl, чтобы переформулировать вашу строку.

Пример:

$ 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

Вы можете сделать это как для строк, которые содержат секунды, так и для тех, которые не содержат.

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 - альтернативная команда, которая будет обрабатывать оба формата времени. Сделайте мне одолжение и удалите комментарии выше, которые больше не актуальны.
SLM
1

В соответствии с man-страницей даты, формат, который вы выводите, не совпадает с тем, который dateожидает ввод. Вот что написано на странице руководства:

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

Так что вы можете сделать это так:

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

Потому что в переменных, которые используются для определения выходной строки, +%m%d%H%M%Yбудет равно тому, что он ожидает в качестве ввода.

переигровка
источник
Тогда можете ли вы предоставить команду для сопоставления даты в формате ISO8601 с какой датой требуется? Фактические сохраненные метки времени должны быть в формате ISO8601, чтобы их можно было отсортировать по дате.
alex.forencich