web-dev-qa-db-ja.com

日付とbashを使用して時間を引く

SEネットワークに関する他のすべての質問は、日付がnow[〜#〜] q [〜#〜] )であると想定されるか、日付のみが想定されるシナリオを扱います( [〜#〜] q [〜#〜] )が指定されます。

私がやりたいことは、日付と時刻を指定し、それから時刻を引くことです。
これは私が最初に試したものです:

date -d "2018-12-10 00:00:00 - 5 hours - 20 minutes - 5 seconds"

これは2018-12-10 06:39:55-7時間追加されました。その後、20:05分が差し引かれました。

maninfoおよびdateページを読んだ後、次のように修正したと思いました。

date -d "2018-12-10T00:00:00 - 5 hours - 20 minutes - 5 seconds"

しかし、同じ結果。 7時間はどこから来るのですか?

その日は7200うるう秒だと思ったので、他の日付も試してみました。しかし同じ結果。

さらにいくつかの例:

$ date -d "2018-12-16T00:00:00 - 24 hours" +%Y-%m-%d_%H:%M:%S
2018-12-17_02:00:00

$ date -d "2019-01-19T05:00:00 - 2 hours - 5 minutes" +%Y-%m-%d_%H:%M:%S
2019-01-19_08:55:00

しかし、ここでそれは興味深いものになります。入力時間を省略した場合、正常に機能します。

$ date -d "2018-12-16 - 24 hours" +%Y-%m-%d_%H:%M:%S
2018-12-15_00:00:00

$ date -d "2019-01-19 - 2 hours - 5 minutes" +%Y-%m-%d_%H:%M:%S
2019-01-18_21:55:00

$ date --version
date (GNU coreutils) 8.30

何が欠けていますか?

更新:私は最後にZを追加しました、そしてそれは振る舞いを変えました:

$ date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
2019-01-19_04:00:00

私はまだ混乱しています。これについては GNU情報ページ にはあまり詳しくありません。

これはタイムゾーンの問題だと思いますが、 ISO 8601The Calendar Wiki を引用しています:

時間表現でUTC関係情報が指定されていない場合、時間は現地時間であると見なされます。

私が欲しいのはそれです。私の現地時間も正しく設定されています。日時を指定し、それから何かを差し引こうとするこの単純なケースで、なぜ日付がタイムゾーンに影響を与えるのかはわかりません。最初に日付文字列から時間を差し引くべきではありませんか?最初に日付に変換し、次に減算を行っても、減算を省略した場合、正確に必要なものが得られます。

$ date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S
2019-01-19_05:00:00

したがって、[〜#〜] if [〜#〜]これは本当にタイムゾーンの問題であり、その狂気はどこから来たのですか?

20
confetti

最後の例では、timezonesが明確になっているはずです。

$ TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
2019-01-19_03:00:00
$ TZ=Asia/Colombo date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S 
2019-01-19_08:30:00

出力はタイムゾーンによって明らかに異なるので、タイムゾーンが指定されていない時間文字列に対していくつかの非自明なデフォルトが採用されているのではないかと思います。いくつかの値をテストすると、それは TC-05: のようですが、それが何かはわかりません。

$ TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S%Z
2019-01-19_08:00:00UTC
$ TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S%Z
2019-01-19_03:00:00UTC
$ TZ=UTC date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S%Z           
2019-01-19_05:00:00UTC

日付の計算を実行するときにのみ使用されます。


ここでの問題は- 2 hoursではなく算術として解釈されますが、タイムゾーン指定子として使用されます

# TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC-02
date: parsed relative part: +1 hour(s)
date: input timezone: parsed date/time string (-02)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02' = 1547881200 Epoch-seconds
date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
date:     new time = 1547884800 Epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547884800.000000000 (Epoch-seconds)
date: final: (Y-M-D) 2019-01-19 08:00:00 (UTC)
date: final: (Y-M-D) 2019-01-19 08:00:00 (UTC+00)
2019-01-19_08:00:00UTC

したがって、計算が行われていないだけでなく、 夏時間 時間の調整は1時間で、私たちにとっては少し無意味な時間につながります。

これは追加にも当てはまります:

# TZ=UTC date -d "2019-01-19T05:00:00 + 5:30 hours" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC+05:30
date: parsed relative part: +1 hour(s)
date: input timezone: parsed date/time string (+05:30)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=+05:30'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=+05:30' = 1547854200 Epoch-seconds
date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
date:     new time = 1547857800 Epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547857800.000000000 (Epoch-seconds)
date: final: (Y-M-D) 2019-01-19 00:30:00 (UTC)
date: final: (Y-M-D) 2019-01-19 00:30:00 (UTC+00)
2019-01-19_00:30:00UTC

もう少しデバッグすると、解析は次のようになります:2019-01-19T05:00:00 - 2-2はタイムゾーン)、およびhours(= 1時間)で、暗黙的に追加されます。代わりに分を使用すると、より見やすくなります。

# TZ=UTC date -d "2019-01-19T05:00:00 - 2 minutes" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC-02
date: parsed relative part: +1 minutes
date: input timezone: parsed date/time string (-02)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02' = 1547881200 Epoch-seconds
date: after time adjustment (+0 hours, +1 minutes, +0 seconds, +0 ns),
date:     new time = 1547881260 Epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547881260.000000000 (Epoch-seconds)
date: final: (Y-M-D) 2019-01-19 07:01:00 (UTC)
date: final: (Y-M-D) 2019-01-19 07:01:00 (UTC+00)
2019-01-19_07:01:00UTC

それで、まあ、日付の計算は、私たちが求めたものではなく、行われています。 ¯\(ツ)/¯

20
Olorin

最初に入力日付をISO 8601に変換すると、正しく機能します。

$ date -d "$(date -Iseconds -d "2018-12-10 00:00:00") - 5 hours - 20 minutes - 5 seconds"
So 9. Dez 18:39:55 CET 2018
6
pLumo

TLDR:これはバグではありません。 dateの微妙だが文書化された動作の1つを見つけました。 dateで時間演算を実行する場合は、タイムゾーンに依存しない形式(Unix時間など)を使用するか、ドキュメントを注意深く読みvery、方法を確認してくださいこのコマンドを適切に使用します。


GNU dateは、システム設定(TZ環境変数、または設定されていない場合はシステムのデフォルト)を使用して、-d/--dateオプションで指定された日付と、 +format引数。 --dateオプションを使用すると、独自のオプション引数のタイムゾーンもオーバーライドできますが、+formatのタイムゾーンはオーバーライドできません。これが混乱の根源、IMHOです。

私のタイムゾーンがUTC-6であることを考慮して、次のコマンドを比較します。

$ date -d '1970-01-01 00:00:00' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 00:00:00 -06:00
Unix: 21600
$ date -d '1970-01-01 00:00:00 UTC' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1969-12-31 18:00:00 -06:00
Unix: 0
$ TZ='UTC0' date -d '1970-01-01 00:00:00 UTC' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 00:00:00 +00:00
Unix: 0

最初のものは、-d+formatの両方に私のタイムゾーンを使用します。 2つ目は-dにUTCを使用しますが、+formatには私のタイムゾーンを使用します。 3つ目は、両方にUTCを使用します。

ここで、次の単純な操作を比較します。

$ date -d '1970-01-01 00:00:00 UTC +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 18:00:00 -06:00
Unix: 86400
$ TZ='UTC0' date -d '1970-01-01 00:00:00 UTC +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-02 00:00:00 +00:00
Unix: 86400

Unixの時刻が同じことを私に伝えていても、自分のタイムゾーンが原因で「通常」の時刻は異なります。

同じ操作をしたいが私のタイムゾーンのみを使用したい場合:

$ TZ='CST+6' date -d '1970-01-01 00:00:00 -06:00 +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-02 00:00:00 -06:00
Unix: 108000
5
nxnev

GNU dateは単純な日付計算をサポートしますが、@ sudodusの回答に示されているエポック時間の計算はより明確になる(そして移植性が上がる)ことがあります。

タイムスタンプにタイムゾーンが指定されていないときに+/-を使用すると、他のものが解析される前に、次にタイムゾーンを一致させる試みがトリガーされます。

これを行う1つの方法は、「-」の代わりに「ago」を使用することです。

_$ date -d "2018-12-10 00:00:00 5 hours ago 20 minutes ago 5 seconds ago"
Sun Dec  9 18:39:55 GMT 2018
_

または

_$ date -d "2018-12-10 00:00:00Z -5 hours -20 minutes -5 seconds"
Sun Dec  9 18:39:55 GMT 2018
_

(任意に "Z"を使用することはできませんが、それは私のゾーンで機能しますが、それはそれをUTC/GMTゾーンのタイムスタンプにします-タイムスタンプに${TZ:-$(date +%z)}を追加して独自のゾーン、または%z /%Zを使用します代わりに。)

これらのフォームの時間条件を追加すると、時間が調整されます。

  • 「5時間前」から5時間を差し引く
  • 「4時間」に4時間を追加(暗黙)
  • 「3時間後」に3時間を追加(明示的)(古いバージョンではサポートされていません)

多くの複雑な調整を任意の順序で使用できます(ただし、「14週間、つまり最後の月曜日」などの相対的で変動する用語は問題を引き起こしています;-)

(ここにも別の小さな弱点があります。dateは常に有効な日付を提供するので、_date -d "2019-01-31 1 month"_は2019-03-03を提供します。「翌月」も同様です)

サポートされているさまざまな時刻と日付の形式を考えると、タイムゾーンの解析は必ずずさんです。1文字または複数文字のサフィックス、1時間または1時間:分のオフセット、名前 "America/Denver"(またはファイル名でさえも) TZ変数の場合)。

「T」は単なるタイムゾーンではなく区切り文字であるため、あなたの_2018-12-10T00:00:00_バージョンは機能しません。最後に「Z」を追加すると、期待どおりに機能します(選択したゾーンの正確さによる)。

参照: https://www.gnu.org/software/tar/manual/html_node/Date-input-formats.html 、特にセクション7.7。

4
mr.spuratic

このソリューションは理解が簡単ですが、もう少し複雑なので、私はそれをシェルスクリプトとして示しています。

  • 「1970-01-01 00:00:00 UTCからの秒数」に変換
  • 差を加算または減算する
  • 最後のdateコマンドラインを使用して、人間が読める形式に戻す

シェルスクリプト:

#!/bin/bash

startdate="2018-12-10 00:00:00"

ddif="0"          # days
diff="-5:-20:-5"  # hours:minutes:seconds

#-----------------------------------------------------------------------------

ss1970in=$(date -d "$startdate" "+%s")  # seconds since 1970-01-01 00:00:00 UTC
printf "%11s\n" "$ss1970in"

h=${diff%%:*}
m=${diff#*:}
m=${m%:*}
s=${diff##*:}
difs=$(( (((ddif*24+h)*60)+m)*60+s ))
printf "%11s\n" "$difs"

ss1970ut=$((ss1970in + difs))  # add/subtract the time difference
printf "%11s\n" "$ss1970ut"

date -d "@$ss1970ut" "+%Y-%m-%d %H:%M:%S"
1
sudodus