it-swarm-eu.dev

Rychle vypočítat rozdíly mezi daty

Často chci provést několik rychlých výpočtů data, například:

  • Jaký je rozdíl mezi těmito dvěma daty?
  • Jaké je datum n týdny po tomto druhém datu?

Obvykle otevírajím kalendář a počítám dny, ale myslím, že by měl existovat program/skript, který mohu použít k provádění těchto druhů výpočtů. Nějaké návrhy?

93
daniel kullmann

„N týdnů po datu“ je snadné s GNU datum (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

Nevím o jednoduchém způsobu výpočtu rozdílu mezi dvěma daty, ale pomocí funkce Shell můžete zabalit malou logiku kolem data (1).

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

Zaměnit d1 a d2 pokud chcete výpočet data opačným způsobem, nebo si nechejte trochu fantazii, aby na tom nezáleželo. Dále, v případě, že dojde k přechodu mimo DST na DST v intervalu, bude jeden ze dnů dlouhý pouze 23 hodin; můžete kompenzovat přidáním ½ dne k částce.

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

Pro sadu přenosných nástrojů zkuste své vlastní dateutils . Vaše dva příklady by se scvrkly na jednoplášťové:

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

nebo v týdnech a dnech:

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

a

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

Důležitá poznámka týkající se instalace Ubuntu:

Tyto velmi stejné dateutils jsou k dispozici jako balíček Ubuntu, a proto je lze instalovat pomocí Sudo apt install dateutils Příkazům však musí předcházet dateutils. předpona jako v dateutils.ddiff 2019-03-28 2019-05-16

43
hroptatyr

A python příklad pro výpočet počtu dní, které jsem prošel planetou:

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

Obvykle dávám přednost času a datu v unixovém utime formátu (počet sekund od epochy, kdy se začalo sedmdesátá léta, UTC). Tímto způsobem se vždy scvrkává na obyčejné odčítání nebo sčítání sekund.

Problém, který se obvykle stává transformací data/času do tohoto formátu.

V prostředí/skriptu prostředí Shell to můžete získat pomocí date '+%s' V době psaní je aktuální čas 1321358027.

Pro srovnání s 2011-11-04 (moje narozeniny), date '+%s' -d 2011-11-04, Což přináší 1320361200. Odečíst: expr 1321358027 - 1320361200 Dává 996827 Sekund, což je expr 996827 / 86400 = Před 11 dny.

Převod z utime (formát 1320361200) na datum je velmi jednoduchý, například C, PHP nebo Perl, pomocí standardních knihoven.) GNU date, argument -d lze doplnit do @ a označit formát „Sekundy od epochy“.

14
MattBianco

Pokud je pro vás grafický nástroj v pořádku, vřele doporučuji qalculate (kalkulačka s důrazem na převody jednotek, přichází s rozhraním GTK a KDE, IIRC). Zde můžete říci např.

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

pro získání počtu dní (140, protože rok 1900 nebyl přestupným rokem) mezi daty, ale samozřejmě můžete udělat i to samé pro časy:

17:12:45 − 08:45:12

výnosy 8.4591667 hodin nebo, pokud nastavíte výstup na formátování času, 8:27:33.

8
quazgar

Toto přišlo při použití date -d "$death_date - $y years - $m months - $d days" získat datum narození (pro genealogii). Tento příkaz je nesprávný. Měsíce nejsou všechny stejné délky, takže (date + offset) - offset != date. Věk v roce/měsíci/den jsou opatření směřující dopředu od data narození.

$ 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)

Datum dává správný výstup v obou případech, ale ve druhém případě jste se ptali na špatnou otázku. Záleží na tom, KTERÉ 11 měsíců v roce pokrývají +/- 11, před přidáním/odečtením dnů. Například:

$ 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

Aby bylo odečtením obrácená operace sčítání, musí být pořadí operací obráceno. Přidání přidá roky, THEN měsíce, THEN dny. Pokud by odečtení použilo opačné pořadí, vrátili byste se k výchozímu bodu. Ne, takže ne, pokud posun dnů překročí hranici měsíce v jiné délce měsíce.

Pokud potřebujete pracovat zpět od data a věku ukončení, můžete to provést pomocí více vyvolání date. Nejprve odečtěte dny, pak měsíce, pak roky. (Nemyslím si, že je bezpečné spojit roky a měsíce do jediné date vyvolání, protože přestupné roky mění délku února.)

8
Peter Cordes

S pomocí řešení dannas to lze provést v jednom řádku s následujícím kódem:

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

(Pracuje v obou Python 2.x a Python 3.))

4

Další způsob výpočtu rozdílu mezi dvěma daty stejného kalendářního roku můžete použít:

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"
  • Řádek 1 prohlašuje Shell, který tlumočník použít.
  • Řádek 2 přiřadí hodnotu z date proměnné DATEfirstnum. Příznak -d Zobrazuje řetězec v časovém formátu, v tomto případě 14. května 2014 a +"%j" Řekne date, aby formátoval výstup pouze na den roku (1-365) ).
  • Řádek 3 je stejný jako řádek 2, ale s jiným datem a jiným formátem pro řetězec, 31. prosince 2014.
  • Řádek 4 přiřazuje hodnotu DAYSdif rozdílu dvou dnů.
  • Řádek 5 zobrazuje hodnotu DAYSdif.

Toto funguje s verzí GNU ve verzi date, ale ne ve verzi PC-BSD/FreeBSD. Nainstaloval jsem coreutils ze stromu portů a místo toho jsem použil příkaz /usr/local/bin/gdate.

4
Justin Holcomb

Často používám SQL pro výpočty data. Například MySQL , PostgreSQL nebo 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

Jindy se cítím v náladě pro JavaScript. Například SpiderMonkey , WebKit , Seed nebo 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

(Dávejte pozor, když předáváte měsíc konstruktoru objektu Java Date. Začíná 0.)

3
manatwork

date a bash umí provádět rozdíly v datech (zobrazené možnosti OS X). Nejdříve vložte druhé datum.

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

Toto je poněkud staré vlákno, ale objevil jsem něco zajímavého. Jsem zaseknutý na vestavěném systému s BusyBoxem (v1.19) a byl jsem trochu zklamaný, že super šikovný now - 10 days notace tam selhala. Ve skutečnosti je pro -d možnost.

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]

Některé matematické údaje však lze provést k datu vstupu.

S ohledem na tyto formáty jsem zjistil, že když nastavíte den zobrazení na nulu, dostanete poslední den předchozího měsíce:

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

A zadávání záporných čísel jde ještě dále:

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

Může někdo potvrdit, jestli to funguje také na datum GNU datum?) Ačkoli now - 10 days je mnohem lepší)

1
Nate

Camhova odpověď se o většinu postará, ale můžeme se zlepšit s řešením zaokrouhlování, časových pásem atd., navíc získáme extra přesnost a schopnost vybrat si naše jednotky:

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 řekne date, že dodáváme datum a čas pro převod. +%s.%N změní datum a čas na sekundy. Druhé sekundy od 1970-01-01 00:00:00 UTC. bc vypočítá rozdíl mezi těmito dvěma čísly a faktor dělení nám dá různé jednotky. printf v případě potřeby přidá 0 před desetinné místo (bc ne) a zajistí zaokrouhlování na nejbližší (bc jen zkrátí). Můžete také použít awk.

Nyní to pojďme s funky testovacím případem,

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

Všimněte si, že protože délka měsíce nebo roku není vždy stejná, neexistuje jediná „správná“ odpověď, i když dnes lze jako rozumnou aproximaci použít 365,24 dní.

1
TTT

K dispozici je také GNU časové výpočty jednotek v kombinaci s GNU datum:

$ 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 jsou jednotky v Linuxu, gdate je datum)

1
Larry

datediff.sh na github: Gist

#!/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

Toto je poněkud staré vlákno, ale objevil jsem něco zajímavého v BusyBox, alespoň co se týče přidávání a odečítání N jednotek času od známého data. Není to ale tak moc, že ​​byste si mohli určit interval mezi dvěma známými daty.

Jsem zaseknutý na vestavěném systému s BusyBoxem a super šikovným now - 10 days notace tam selhala. Ve skutečnosti je pro -d možnost.

$ 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]

Některé matematické údaje však lze provést k datu vstupu. Vzhledem k těmto formátům jsem zjistil, že když nastavíte číslo dne vstupního data na nulu, dostanete poslední den předchozího měsíce:

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

Zadávání záporných čísel dále odečítá zpět, což jsem určitě neočekával:

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

Takže můžete udělat nějakou rychlou matematiku ke svému známému datu a vygenerovat hodnoty, které se budou posílat do -d volba v BusyBoxu date volání:

#!/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

Výstup:

2018-05-25

Může někdo potvrdit, zda to funguje také s GNU, BSD nebo MacOS date? Určitě oceňuji, že GNU now - 10 days je mnohem lepší, ale mám zájem o to, aby to bylo co nejpřenosnější. Potvrdil jsem, že Android je velmi odlišný (žádný přístup tam nefunguje).

0
Nate

date -d je specifické pro GNU Datum a není definováno POSIX:

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

Jak již uvedli ostatní, je to něco, co by lépe vyhovovalo správnému programovacímu jazyku. Například PHP:

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

Výsledek:

days: 10
0
Steven Penny