日付とバッシュを使用して時間を引く


18

どちらかの日付があると想定されるシナリオとSEネットワークの契約上の他のすべてのご質問nowQ日付が指定されている場合のみ)または(Q)。

私がやりたいのは、日付と時刻を指定し、それから時刻を減算することです。
私が最初に試したことは次のとおりです。

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

この結果2018-12-10 06:39:55-7時間追加されました。次に、20:05分を差し引きました。

manとのinfoページを読んだ後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 8601のThe Calendar Wikiを引用しています

UTC関係情報が時間表現とともに提供されない場合、時間は現地時間であると想定されます。

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

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

だから、IF、これは本当にその狂気から来ない時間帯の問題で、ありますか?


回答:


20

最後の例では、タイムゾーンを明確にしているはずです。

$ 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

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

$ 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

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


1
@confettiそれは確かに行います。このデフォルトのタイムゾーンは、加算/減算時(TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S対VS TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S)にのみ使用されると思います
オロリン

2
で可能バグかもしれdateないタイムゾーンが供給されない場合は、としてISO 8601標準当たり、ローカルタイム(ゾーン)を演算する場合に、それがされていない、と仮定されるべきです。私にとって非常に奇妙な問題。
紙吹雪

1
@confettiが問題を発見しました- 2 hours。ここではタイムゾーン指定子として使用されます。
オロリン

2
ワオ。今ではどういうわけか理にかなっています。まあ、少なくともそれは説明しています。それを理解してくれてありがとう。私にとっては、パーサーには更新が必要なようですが、明らかにこのような形式と空白では、「-2時間」でタイムゾーンを指定したくないので、間違いなく混乱を引き起こしています。パーサーを更新したくない場合は、少なくともマニュアルでこれについてのメモを取得する必要があります。
紙吹雪

1
今日、日付の--debugオプションについて学びました!素晴らしい説明。
ジェフシャラー

6

最初に入力日付を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

ありがとう、それは実際に動作しますが、これについての説明はありますか?私は質問にISO 8601のことに関する追加情報を追加しましたがdate、減算が提供されないため、これを混乱させる場所は本当にわかりません。タイムゾーン情報または変換。
紙吹雪

わかりません、ごめんなさい。他の誰かがこれに答えることができたら、私も不思議に思っていたので幸せです。
pLumo

私も喜んでいますが、私の問題を解決するので、今のところこれを受け入れます!
紙吹雪

3
これは、オロリンの答えdate -I2018-12-10T00:00:00+02:00
-ilkkachu

6

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


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

私のタイムゾーンが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番目はUTCを使用します-dが、私のタイムゾーンはを使用し+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

4
芋時間演算を行うための唯一のまともな方法である、エポック/ Unixの時間を言及するためUpvoted
ルイF・リベイロ

1
TL; DRズールー時間から現地時間のオフセットがわかっている場合(たとえば、米国西海岸が-8時間または-08:00)、それを日付時刻に入力するだけで、他に何も調整する必要はありません。例:date -d '2019-02-28 14:05:36-08:00 +2 days 4 hours 3 seconds' +'local: %F %T'与えローカル:2019年3月2日夜06時05分36秒を
Bレイヤー

1
@BLayerその通りです、そのシナリオでは期待通りに動作します。しかし、そのアプローチを使用する特定のスクリプトが異なるタイムゾーンの場所で実行される状況を想像してください。出力は、技術的には正しいものの、+format引数によって提供される情報によっては間違って見える場合があります。たとえば、私のシステムでは、同じコマンドが出力します:local: 2019-03-02 20:05:39に追加%:zする+formatと、情報が正しく、不一致がタイムゾーンに起因することが明らかになります)。
nxnev

@BLayer IMHO、より良い一般的なアプローチは、予期しない結果を避けるために、答えで言ったように、Unix時間-d+formatUnix時間の両方に同じタイムゾーンを使用することです。
nxnev

1
@nxnev合意、公開/共有するスクリプトを書いている場合。「あなた自身のタイムゾーンを知っている場合」という冒頭の言葉は、文字通りの意味を持つことに加えて、カジュアルな/個人的な使用を暗示することになっています。自分のシステムのtzだけを知っているのと同じくらい薄弱なものに依存しているスクリプトを公開している人は、おそらくスクリプト共有ゲームに参加すべきではありません。:)一方、私はすべての個人的な環境で独自のコマンドを使用します/使用します。
Bレイヤー

4

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」をarbitrarily意的に使用することはできませんが、私のゾーンでは機能しますが、UTC / GMTゾーンのタイムスタンプになります-独自のゾーンを使用するか${TZ:-$(date +%z)}、代わりにタイムスタンプに追加して%z /%Zを使用します。)

これらのフォームに時間を追加すると、時間を調整できます。

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

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

(ここにも別の小さなわながあります。date常に有効な日付を提供するためdate -d "2019-01-31 1 month"、「来月」と同様に2019-03-03を提供します)

サポートされているさまざまな時刻と日付の形式を考えると、タイムゾーンの解析は必然的にずさんです:単一または複数文字の接尾辞、時間または時間:分のオフセット、名前「America / Denver」(またはファイル名でさえも可能です)TZ変数の場合)。

あなたの2018-12-10T00:00:00「T」は末尾に「Z」を追加して、ちょうど区切り、ない時間帯であるため、バージョンがあまりにも予想通りその仕事(選択したゾーンの正しさの対象を)作る仕事をしません。

https://www.gnu.org/software/tar/manual/html_node/Date-input-formats.html および特にセクション7.7を参照してください 。


1
その他のオプションが含まれます:date -d "2018-12-10 00:00:00 now -5 hours、またはtodayあるいは0 hour代わりにnow、告げる何date時間後にトークンがオフセットのタイムゾーンではないこと。
ステファンシャゼラス

1
または、徹底するために、引数を並べ替えて日時の後に何も残しません:date -d "-5 hours 2018-12-10 00:00:00" `
Bレイヤー

2

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

  • 「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"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.