Szybko obliczyć różnice dat


84

Często chcę dokonać szybkich obliczeń dat, takich jak:

  • Jaka jest różnica między tymi dwiema datami?
  • Jaka jest data n tygodni po tej drugiej dacie?

Zwykle otwieram kalendarz i liczę dni, ale myślę, że powinien istnieć program / skrypt, którego można użyć do tego rodzaju obliczeń. Jakieś sugestie?


3
Zobacz także Narzędzie w systemie UNIX do odejmowania dat, gdy data GNU jest niedostępna.
Gilles

Odpowiedzi:


106

„N tygodni po dacie” jest łatwe dzięki 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

Nie znam prostego sposobu obliczenia różnicy między dwiema datami, ale można zawrzeć trochę logiki wokół date (1) za pomocą funkcji powłoki.

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

Zamień d1i d2jeśli chcesz obliczyć datę w inny sposób, lub zdobądź nieco bardziej fanatyzmu, aby to nie miało znaczenia. Co więcej, w przypadku, gdy w danym przedziale czasowym nastąpi przejście bez DST na DST , jeden z dni będzie trwał tylko 23 godziny; możesz to zrekompensować, dodając do sumy ½ dnia.

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

40

Aby uzyskać zestaw przenośnych narzędzi, wypróbuj moje własne dateutils . Twoje dwa przykłady sprowadzałyby się do jednowierszowych:

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

lub w tygodniach i dniach:

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

i

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

3
Daj +1 swoim narzędziom rockowym (choć dateadd -i '%m%d%Y' 01012015 +1dwydaje się, że nie działa, po prostu zawiesza się tam w nieskończoność ... działa, jeśli specyfikacje daty są oddzielone char, dowolny char ... jakiś pomysł, co jest nie tak?)
don_crissti

2
@don_crissti Parser nie mógł rozróżnić dat i czasu trwania tylko liczbami, został naprawiony w bieżącym wzorcu (d0008f98)
hroptatyr

Czy masz binarną dystrybucję dateutils w oknie?
mosh

@ mosh nie, a ja nie mam możliwości wypróbowania.
hroptatyr

Późno na imprezę tutaj, ale dateutils jest na cygwin, jeśli musisz korzystać z systemu Windows.
gregb212

30

Przykład pythonowy do obliczania liczby dni, kiedy chodziłem po planecie:

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

2
Na wypadek, gdyby ktoś chciał, aby zachowywało się tak jak jedno polecenie, zamiast wpisywać interaktywnego tłumacza: ychaouche@ychaouche-PC ~ $ python -c "from datetime import date as d; print (d.today() - d(1980, 6, 14)).days" 12813 ychaouche@ychaouche-PC ~ $
ychaouche

1
to działa dla mnie python3 -c "od daty importu datetime jako d; print (d.today () - d (2016, 1, 9))" dni na końcu nie są wymagane
Kiran K Telukunta

13

Zwykle wolę mieć czas / datę w formacie uniksowym (liczba sekund od epoki, kiedy zaczęły się lata siedemdziesiąte, UTC). W ten sposób zawsze sprowadza się do zwykłego odejmowania lub dodawania sekund.

Problemem zwykle staje się przekształcenie daty / godziny w ten format.

W środowisku powłoki / skrypcie można go uzyskać. date '+%s' W chwili pisania tego aktualny czas to 1321358027.

Aby porównać z 2011-11-04 (moje urodziny) date '+%s' -d 2011-11-04, przynosząc 1320361200. Odejmij: expr 1321358027 - 1320361200daje 996827sekundy, czyli expr 996827 / 86400= 11 dni temu.

Konwertowanie z utime (format 1320361200) na datę jest bardzo proste, na przykład w C, PHP lub Perlu, przy użyciu standardowych bibliotek. GNU dateThe -dargument może być poprzedzane @wskazać „sekund od Epoki” formatu.


7
Z datą GNU date -d @1234567890konwertuje od sekundy od epoki do dowolnego określonego formatu daty.
Gilles

1
@Gilles: to jest świetne. Nie mogłem tego znaleźć na stronie podręcznika. Gdzie się tego nauczyłeś?
MattBianco,

1
@MattBianco, patrz info datezwłaszcza Sekundy od węzła Epoki : „Jeśli poprzedzisz liczbę znakiem„ @ ”, oznacza ona wewnętrzny znacznik czasu jako liczbę sekund.”
manatwork

8

Pojawiło się to podczas date -d "$death_date - $y years - $m months - $d days"uzyskiwania daty urodzenia (dla genealogii). To polecenie jest NIEPRAWIDŁOWE. Miesiące nie są jednakowej długości, więc (date + offset) - offset != date. Wiek, w roku / miesiącu / dniu, jest miarą naprzód od daty urodzenia.

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

Data daje poprawny wynik w obu przypadkach, ale w drugim przypadku zadałeś niewłaściwe pytanie. Ma znaczenie, KTÓRE 11 miesięcy w roku pokrywa +/- 11, przed dodaniem / odjęciem dni. Na przykład:

$ 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 odjęcie było odwrotną operacją dodawania, kolejność operacji musiałaby zostać odwrócona. Dodanie dodaje lata, NASTĘPNIE miesiące, NASTĘPNIE dni. Jeśli odejmujesz w odwrotnej kolejności, wrócisz do punktu początkowego. Nie zmienia się, więc nie, jeśli przesunięcie dni przekracza granicę miesiąca w innym miesiącu długości.

Jeśli musisz cofnąć się od daty i wieku zakończenia, możesz to zrobić za pomocą wielu wywołań date. Najpierw odejmij dni, potem miesiące, a potem lata. (Nie sądzę, że łączenie lat i miesięcy w jednym datewywołaniu jest bezpieczne, ponieważ lata przestępne zmieniają długość lutego).


7

Jeśli narzędzie graficzne jest dla Ciebie w porządku, szczerze polecam qalculate(kalkulator z naciskiem na konwersje jednostek, jest wyposażony w interfejs GTK i KDE, IIRC). Tam możesz powiedzieć np

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

aby uzyskać liczbę dni (140, ponieważ rok 1900 nie był rokiem przestępnym) między datami, ale oczywiście można również zrobić to samo dla czasów:

17:12:45 − 08:45:12

daje 8.4591667godzin lub, jeśli ustawisz wyjście do formatowania czasu 8:27:33.


3
To świetnie, a nawet bardziej, bo qalculate nie mają CLI. Spróbuj qalcwięc help days.
Sparhawk

qcalcprzykład: qalc -t 'days(1900-05-21, 1900-01-01)'-> 140
sierrasdetandil

3

Często używam SQL do obliczania daty. Na przykład MySQL , PostgreSQL lub 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

Innym razem mam ochotę na JavaScript. Na przykład SpiderMonkey , WebKit , Seed lub 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

(Uważaj podczas przekazywania miesiąca do konstruktora Dateobiektu JavaScript . Zaczyna się od 0.)


3

Można użyć tego innego sposobu obliczenia różnicy między dwiema datami tego samego roku kalendarzowego:

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"
  • Wiersz 1 deklaruje powłoce, którego interpretera użyć.
  • Wiersz 2 przypisuje wartość od wyjścia datedo zmiennej DATEpierwszy. -dFlaga wyświetla ciąg w formacie czasowym w tej sprawie 14 maja 2014 roku i +"%j"opowiada dateo sformatowanie wyjście tylko dzień roku (1-365).
  • Wiersz 3 jest taki sam jak wiersz 2, ale ma inną datę i inny format ciągu, 31 grudnia 2014 r.
  • Wiersz 4 przypisuje wartość DAYSdifdo różnicy dwóch dni.
  • Wiersz 5 wyświetla wartość DAYSdif.

Działa to z wersją GNU date, ale nie w wersji PC-BSD / FreeBSD. Zainstalowałem coreutilsz drzewa portów i /usr/local/bin/gdatezamiast tego użyłem polecenia .


1
Ten skrypt nie będzie działał. W ostatnim wierszu znajduje się literówka i spacje wokół przypisań zmiennych, więc bash próbuje uruchomić program o nazwie DATE pierwsza nazwa z dwoma argumentami. Spróbuj tego: DATEfirstnum=$(date -d "$1" +%s) DATElastnum=$(date -d "$2" +%s) Ponadto ten skrypt nie będzie w stanie obliczyć różnicy między dwoma różnymi latami. +%jodnosi się do dnia roku (001..366), więc ./date_difference.sh 12/31/2001 12/30/2014wyniki -1. Jak zauważyły ​​inne odpowiedzi, musisz przekonwertować obie daty na sekundy od 1970-01-01 00:00:00 UTC.
Sześć

Nie potrzebujesz $wewnętrznej wyrażenia arytmetycznego: $((DATElastnum - DATEfirstnum))zadziała również.
Ruslan

3

Za pomocą rozwiązań dannas można to zrobić w jednym wierszu za pomocą następującego kodu:

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

(Działa zarówno w Python 2.x jak i Python 3.)


1
Możesz edytować swój post zamiast komentować
Stephen Rauch

3

datei bash może różnic w datach (pokazane opcje OS X). Najpierw umieść tę drugą datę.

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

1

Istnieją również obliczenia czasu jednostki GNU w połączeniu z datą 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 to jednostki w Linuksie, gdate to data)


1

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

odpowiedź camh zajmuje się większością z nich, ale możemy poprawić radzenie sobie z zaokrąglaniem, strefami czasowymi itp., a także uzyskać dodatkową precyzję i możliwość doboru naszych jednostek:

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"))
}

-dinformuje, dateże podajemy datę i godzinę do konwersji. +%s.%Nzmienia datę i godzinę na sekundy. nanosekundy od 1970-01-01 00:00:00 UTC. bcoblicza różnicę między dwiema liczbami, a czynnik dzielący daje nam różne jednostki. printfw razie potrzeby dodaje 0 przed miejscem po przecinku ( bcnie) i zapewnia zaokrąglanie do najbliższego ( bcpo prostu obcina). Możesz także użyć awk.

Teraz uruchommy to z funky testowym przypadkiem,

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

Zwróć uwagę, że ponieważ długość miesiąca lub roku nie zawsze jest taka sama, nie ma tam jednej „poprawnej” odpowiedzi, choć w dzisiejszych czasach można by użyć 365,24 dni jako rozsądnego przybliżenia.


0

Możesz użyć biblioteki awk Velor :

velour -n 'print t_secday(t_utc("2017-4-12") - t_utc("2017-4-5"))'

Lub:

velour -n 'print t_secday(t_utc(ARGV[1]) - t_utc(ARGV[2]))' 2017-4-12 2017-4-5

Wynik:

7
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.