web-dev-qa-db-ja.com

LinuxのdateコマンドでISO8601の日付を解析する方法

私は日付コマンドを使用して、日付コマンド自体が解釈できるファイルのタイムスタンプを生成しようとしています。ただし、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

dateは、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
>>> 

UNIXタイムスタンプとして表示した場合、15時間ずれています。

編集#1

たぶん私はこの質問を少し違った方法で提起するべきです。次の形式のISO8601基本タイムスタンプのリストがあるとします。

  • YYYYMMDDThhmm
  • YYYYMMDDThhmmss

それらを対応するUnixタイムスタンプに変換する最も簡単な方法は何ですか?

例えば:

- 20140103T1422   = 1388787720
- 20140103T142233 = 1388787753
15
alex.forencich

「既知の回避策」を要求します。ここに簡単なものがあります:

$ 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に区切ります。

10
John1024

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

質問の2番目の部分に答えるには...

日付形式には数値と記号しか含まれていないため、各記号を一意の文字に置き換えることができます。 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など.

9
Mikel

GNU coreutilsは バージョン8.1 (2011-09-08にリリース)以降、入力として ISO 8601日付 のみをサポートしています。古いバージョンを使用している必要があります。

古いバージョンでは、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"

dateのmanページでこのメモに気付きました。

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.

これは決定的なものではありませんが、[ISO 8601]の場合、試みているようにTを含む時刻形式文字列を明示的に示していません。 @ Gilles回答 が示すように、 ISO 8601 in 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
2
slm

日付のマニュアルページによると、出力する形式は、dateが入力として期待する形式とは異なります。これはmanページが言うことです:

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は、入力として期待するものと等しくなります。

1
replay