Як розібрати дати ISO8601 за допомогою команди дат Linux


15

Я намагаюся використовувати команду date для створення часової позначки файлу, яку сама команда 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

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для заміни "T" пробілом. В результаті виходить формат, який dateрозуміє.

Якщо ми додамо секунди до дати ISO8601, то dateпотрібно більше змін:

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

У вищесказаному sedзамінює "T" пробілом, а також розділяє HHMMSS на HH: MM: SS.


Для мене працює, якщо + видалено. Однак це не працює для часових позначок другої точності, лише хвилинна точність.
alex.forencich

@ alex.forencich Відповідь оновлюється з секундною точністю. Повідомте мене, якщо обраний нами секунд формат не той, який вам потрібен.
John1024

8

У Coreutils інформація Docs каже , що 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і -або, +як, наприклад, використовуючи оболонку ${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 тощо.


У позначці часу не може бути спеціальних символів. Чи знаєте ви про хороший спосіб автоматично додати їх назад?
alex.forencich

6

GNU coreutils підтримують лише дати ISO 8601 як вхідні дані з версії 8.13 (випущена 2011-09-08). Ви повинні використовувати старішу версію.

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

Навіть за останніми версіями розпізнається лише повністю розділена форма, а не основний формат із лише цифрами та символом 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

Я помітив цю примітку на сторінці man для 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

Згідно з довідковою сторінкою дати, формат, який ви виводите, не такий, як 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.