web-dev-qa-db-ja.com

日付の違いをすばやく計算する

私はしばしば次のようないくつかの簡単な日付計算をしたいです:

  • これら2つの日付の違いは何ですか?
  • この別の日付nから何週間後ですか?

私は通常、カレンダーを開いて日数を数えますが、この種の計算に使用できるプログラム/スクリプトがあるはずだと思います。助言がありますか?

93
daniel kullmann

「n週間後の日付」は、GNU date(1):

$ date -d 'now + 3 weeks'
Tue Dec  6 23:58:04 EST 2011
$ date -d 'Aug 4 + 3 weeks'
Thu Aug 25 00:00:00 EST 2011
$ date -d 'Jan 1 1982 + 11 weeks'
Fri Mar 19 00:00:00 EST 1982

2つの日付の差を計算する簡単な方法はわかりませんが、シェル関数を使用してdate(1)に小さなロジックをラップできます。

datediff() {
    d1=$(date -d "$1" +%s)
    d2=$(date -d "$2" +%s)
    echo $(( (d1 - d2) / 86400 )) days
}
$ datediff '1 Nov' '1 Aug'
91 days

スワップd1およびd2日付の計算を逆にしたい場合、または少し複雑にして重要でない場合。さらに、間隔にDST以外の [〜#〜] dst [〜#〜] への遷移がある場合、1日は23時間しかありません。合計に½日を追加することで補正できます。

echo $(( (((d1-d2) > 0 ? (d1-d2) : (d2-d1)) + 43200) / 86400 )) days
113
camh

ポータブルツールのセットについては、独自の dateutils を試してください。あなたの2つの例は1ライナーに要約されます:

ddiff 2011-11-15 2012-04-11
=>
  148

または週と日で:

ddiff 2011-11-15 2012-04-11 -f '%w %d'
=>
  21 1

そして

dadd 2011-11-15 21w
=>
  2012-04-10

Ubuntuのインストールに関する重要な注意:

これらのまったく同じdateutilsはUbuntuパッケージとして使用できるため、Sudo apt install dateutilsを使用してインストールできます。ただし、コマンドの前にdateutilsを付ける必要があります。 dateutils.ddiff 2019-03-28 2019-05-16のような接頭辞

43
hroptatyr

A python私が惑星を歩いた日数を計算する例:

$ python
>>> from datetime import date as D
>>> print (D.today() - D(1980, 6, 14)).days
11476
32
Daniel Näslund

私は通常、UNIXのutime形式(70年代が始まったときのエポックからの秒数、UTC)で時刻/日付を使用することを好みます。その方法は、常に単純な減算または秒の加算に要約されます。

問題は通常、日付/時刻をこの形式に変換することになります。

シェル環境/スクリプトでは、date '+%s'で取得できます。執筆時点では、現在の時刻は1321358027です。

2011年11月4日(私の誕生日)と比較すると、date '+%s' -d 2011-11-041320361200になります。減算:expr 1321358027 - 1320361200996827秒を与えます。つまり、expr 996827 / 86400 = 11日前です。

Utime(1320361200形式)から日付への変換は、C、PHPまたはPerl、標準ライブラリを使用して)で行うのは非常に簡単です。GNU date-d引数の前に@を追加して、「エポック以降の秒数」形式を示すことができます。

14
MattBianco

グラフィカルツールで問題がなければ、qalculateを強くお勧めします(単位変換に重点を置いた計算機で、GTKおよびKDEインターフェース、IIRCが付属しています)。そこであなたは言うことができます.

days(1900-05-21, 1900-01-01)

日付間の日数(1900年はうるう年ではなかったため、140)を取得しますが、もちろん同じことを時間に対して行うこともできます。

17:12:45 − 08:45:12

8.4591667時間、または出力を時間形式に設定した場合は8:27:33

8
quazgar

これはdate -d "$death_date - $y years - $m months - $d days"(家系図の場合)生年月日を取得します。そのコマンドは間違っています。月はすべて同じ長さではないので、(date + offset) - offset != date。年/月/日の年齢は、生年月日からの経過日数です。

$ date --utc -d 'mar 28 1867 +72years +11months +2days'
Fri Mar  1 00:00:00 UTC 1940

$ date --utc -d 'mar 1 1940 -72years -11months -2days'
Sat Mar 30 00:00:00 UTC 1867
# (2 days later than our starting point)

日付はどちらの場合も正しい出力を示しますが、2番目の場合は間違った質問をしていました。日数を加算/減算する前に、1年の11か月+/- 11をカバーすることが重要です。例えば:

$ date --utc -d 'mar 31 1939  -1month'
Fri Mar  3 00:00:00 UTC 1939
$ date --utc -d 'mar 31 1940  -1month' # leap year
Sat Mar  2 00:00:00 UTC 1940
$ date --utc -d 'jan 31 1940  +1month' # leap year
Sat Mar  2 00:00:00 UTC 1940

減算が加算の逆演算になるためには、演算の順序を逆にする必要があります。追加すると、年、月、日が追加されます。減算で逆の順序を使用した場合は、開始点に戻ります。そうではないので、日オフセットが異なる長さの月の月の境界を超えている場合はそうではありません。

終了日と経過日数から遡って作業する必要がある場合は、dateを複数回呼び出すことで実行できます。最初に日を減算し、次に月、次に年を減算します。 (うるう年は2月の長さを変更するため、年と月を単一のdate呼び出しで組み合わせるのは安全ではないと私は思います。)

8
Peter Cordes

Dannasソリューションの助けを借りて、これは次のコードで1行で実行できます。

python -c "from datetime import date as d; print(d.today() - d(2016, 7, 26))"

(Python 2.xとPython 3.の両方で機能します。)

4

同じ暦年の2つの日付の差を計算する別の方法として、次の方法を使用できます。

date_difference.sh
1  #!/bin/bash
2  DATEfirstnum=`date -d "2014/5/14" +"%j"`
3  DATElastnum=`date -d "12/31/14" +"%j"`
4  DAYSdif=$(($DATElastnum - $DATEfirstnum))
5  echo "$DAYSdif"
  • 1行目は、使用するインタープリターをシェルに宣言します。
  • 行2は、dateの値を変数DATEfirstnumに割り当てます。 -dフラグは文字列を時間形式で表示します。この場合は2014年5月14日で、+"%j"dateに出力を年の日付のみ(1-365)にフォーマットするように指示します。
  • 行3は行2と同じですが、文字列の日付と形式が異なります(2014年12月31日)。
  • 4行目は、2つの日の差に値DAYSdifを割り当てます。
  • 5行目はDAYSdifの値を表示しています。

これはGNU dateのバージョンでは機能しますが、PC-BSD/FreeBSDバージョンでは機能しません。ポートツリーからcoreutilsをインストールし、コマンド/usr/local/bin/gdateを使用しました代わりに。

4
Justin Holcomb

日付の計算にSQLを頻繁に使用しています。例: MySQLPostgreSQL または SQLite

bash-4.2$ mysql <<< "select datediff(current_date,'1980-06-14')"
datediff(current_date,'1980-06-14')
11477

bash-4.2$ psql <<< "select current_date-'1980-06-14'"
 ?column? 
----------
    11477
(1 row)

bash-4.2$ sqlite2 <<< "select julianday('now')-julianday('1980-06-14');"
11477.3524537035

また、JavaScriptの気分だけを感じる場合もあります。例 SpiderMonkeyWebKitSeed または Node.js

bash-4.2$ js -e 'print((new Date()-new Date(1980,5,14))/1000/60/60/24)'
11477.477526192131

bash-4.2$ jsc-1 -e 'print((new Date()-new Date(1980,5,14))/1000/60/60/24)'
11477.47757960648

bash-4.2$ seed -e '(new Date()-new Date(1980,5,14))/1000/60/60/24'
11477.4776318287

bash-4.2$ node -pe '(new Date()-new Date(1980,5,14))/1000/60/60/24'
11624.520061481482

(JavaScript Dateオブジェクトのコンストラクターに月を渡すときに注意してください。0から始まります。)

3
manatwork

dateとbashは日付の違いを実行できます(OS Xオプションを表示)。後者の日付を最初に配置します。

echo $((($(date -jf%D "04/03/16" +%s) - $(date -jf%D "03/02/16" +%s)) / 86400))
# 31
3
Dave

これはかなり古いスレッドですが、興味深いものを発見しました。 BusyBox(v1.19)が組み込まれたシステムで立ち往生していて、超便利なnow - 10 days表記はそこで失敗します。実際、非常に限られた入力フォーマットのセットが-dオプション。

Recognized TIME formats:
    hh:mm[:ss]
    [YYYY.]MM.DD-hh:mm[:ss]
    YYYY-MM-DD hh:mm[:ss]
    [[[[[YY]YY]MM]DD]hh]mm[.ss]

ただし、一部の計算は入力日に行うことができます。

これらの形式を考慮して、表示する日付の日をゼロに設定すると、前月の最終日が得られることがわかりました。

$ date -d 2020.03.00-12:00
Sat Feb 29 12:00:00 EST 2020 

負の数を入力すると、さらに戻ります。

$ date -d 2020.03.-10-12:00
Wed Feb 19 12:00:00 EST 2020

これがGNUの日付でも機能するかどうかは誰でも確認できますか?(now - 10 daysは非常に優れています)

1
Nate

camhの答え はそのほとんどを処理しますが、丸め、タイムゾーンなどを処理するように改善できます。さらに、いくつかの追加の精度とユニットを選択する機能が得られます。

datediff() {
#convert dates to decimal seconds since 1970-01-01 00:00:00 UTC
date1seconds=$(date +%s.%N -d "$date1")
date2seconds=$(date +%s.%N -d "$date2")

#Calculate time difference in various time units
timeseconds=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)"))
timeminutes=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/60"))
  timehours=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/3600"))
   timedays=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/86400"))
  timeweeks=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/604800"))
}

-dは、変換する日時を指定していることをdateに通知します。 +%s.%Nは、日時を1970-01-01 00:00:00 UTC以降のseconds.nanosecondsに変更します。 bcは2つの数値の差を計算し、分割係数は異なる単位を取得します。 printfは、必要に応じて小数点の前に0を追加し(bcはしない)、丸めが最も近い値になるようにします(bcは切り捨てるだけです)。 awkを使用することもできます。

ファンキーなテストケースでこれを実行してみましょう。

date1='Tue Jul  9 10:18:04.031 PST 2020'
date2='Wed May  8 15:19:34.447 CDT 2019'
datediff "$date1" "$date2"

echo $timeseconds seconds
-36971909.584000000 seconds

echo $timeminutes minutes
-616198.493066667 minutes

echo $timehours hours
-10269.974884444 hours

echo $timedays days
-427.915620185 days

echo $timeweeks weeks
-61.130802884 weeks

1か月または1年の長さが常に同じであるとは限らないので、「正しい」答えは1つではないことに注意してください。ただし、今日では妥当な概算として365.24日を使用できます。

1
TTT

GNUユニットの時間計算とGNU日付の組み合わせ:

$ gunits $(gdate +%s)sec-$(gdate +%s -d -1234day)sec 'yr;mo;d;hr;min;s'
        3 yr + 4 mo + 16 d + 12 hr + 37 min + 26.751072 s
$ gunits $(gdate +%s -d '2015-1-2 3:45:00')sec-$(gdate +%s -d '2013-5-6 7:43:21')sec 'yr;mo;d;hr;min;s'
        1 yr + 7 mo + 27 d + 13 hr + 49 min + 26.206759 s

(gunitsはLinuxの単位、gdateは日付です)

1
Larry

github:Gistのdatediff.sh

#!/bin/bash
#Simplest calculator two dates difference. By default in days

# Usage:
# ./datediff.sh first_date second_date [-(s|m|h|d) | --(seconds|minutes|hours|days)]

first_date=$(date -d "$1" "+%s")
second_date=$(date -d "$2" "+%s")

case "$3" in
"--seconds" | "-s") period=1;;
"--minutes" | "-m") period=60;;
"--hours" | "-h") period=$((60*60));;
"--days" | "-d" | "") period=$((60*60*24));;
esac

datediff=$(( ($first_date - $second_date)/($period) ))
echo $datediff
1
user178063

これはかなり古いスレッドですが、少なくとも既知の日付からN単位の時間を加算および減算することに関して、BusyBoxで興味深いものを発見しました。ただし、2つの既知の日付の間隔を決定するための別の方法についてはそれほどではありません。

BusyBoxが組み込まれたシステムで立ち往生しており、非常に便利なnow - 10 days表記がそこで失敗します。実際、-dオプションで使用できる入力フォーマットは非常に限られています。

$ busybox date --help
BusyBox v1.19.0 (2019-07-14 10:52:19 UTC) multi-call binary.

Usage: date [OPTIONS] [+FMT] [TIME]

Display time (using +FMT), or set time

        [-s,--set] TIME Set time to TIME
        -u,--utc        Work in UTC (don't convert to local time)
        -R,--rfc-2822   Output RFC-2822 compliant date string
        -I[SPEC]        Output ISO-8601 compliant date string
                        SPEC='date' (default) for date only,
                        'hours', 'minutes', or 'seconds' for date and
                        time to the indicated precision
        -r,--reference FILE     Display last modification time of FILE
        -d,--date TIME  Display TIME, not 'now'
        -D FMT          Use FMT for -d TIME conversion

Recognized TIME formats:
        hh:mm[:ss]
        [YYYY.]MM.DD-hh:mm[:ss]
        YYYY-MM-DD hh:mm[:ss]
        [[[[[YY]YY]MM]DD]hh]mm[.ss]

ただし、一部の計算は入力日に行うことができます。これらの形式を考えると、入力日付の日数をゼロに設定すると、前月の最終日が得られることがわかりました。

$ date -d 2020.03.00-12:00
Sat Feb 29 12:00:00 EST 2020 

さらに、負の数を入力すると、逆に減算され続けます。

$ date -d 2020.03.-10-12:00
Wed Feb 19 12:00:00 EST 2020

そのため、既知の日付でいくつかの簡単な計算を行って、BusyBox date呼び出しの-dオプションにフィードする値を生成できます。

#!/bin/bash
#Other shells not tested.

start_year=2020
start_month=1
start_day=20

offset_year=-2
offset_month=4
offset_day=5

new_year=$(( $start_year + $offset_year ))
new_month=$(( $start_month + $offset_month ))
new_day=$(( $start_day + $offset_day ))

new_date$( busybox date -d $new_year.$new_month.$new_day-12:00 +%Y-%m-%d )
echo $new_date

出力:

2018-05-25

これがGNU、BSD、またはMacOS dateでも機能するかどうかを誰かが確認できますか? GNU now - 10 daysの方がはるかに優れていることは確かに感謝しますが、これをできるだけ移植性のあるものにすることに興味があります。 Androidは非常に異なっていることを確認しました(どちらのアプローチもそこで機能しません)。

0
Nate

date -dはGNU日付に固有であり、POSIXでは定義されていません:

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/date.html

他の人が述べたように、これは適切なプログラミング言語により適しているかもしれません。たとえば、PHP:

<?php
$o1 = date_create('2020-02-26');
$o2 = date_create('2020-02-16');
$o3 = date_diff($o2, $o1);
echo 'days: ', $o3->d, "\n";

結果:

days: 10
0
Steven Penny