A = „2002-20-10”
B = „2003-22-11”
Jak znaleźć różnicę w dniach między dwiema datami?
A = „2002-20-10”
B = „2003-22-11”
Jak znaleźć różnicę w dniach między dwiema datami?
Odpowiedzi:
Jeśli masz GNU date
, to pozwala wydrukować reprezentację dowolnej daty ( -d
opcja). W tym przypadku zamień daty na sekundy od EPOCH, odejmij i podziel przez 24 * 3600.
Czy potrzebujesz przenośnego sposobu?
echo "( `date -d $B +%s` - `date -d $A +%s`) / (24*3600)" | bc -l
Sposób basha - przekonwertuj daty do formatu% y% m% d, a następnie możesz to zrobić bezpośrednio z wiersza poleceń:
echo $(( ($(date --date="031122" +%s) - $(date --date="021020" +%s) )/(60*60*24) ))
%y%m%d
formatem. Np. Data gnu zaakceptuje %Y-%m-%d
format używany przez PO. Ogólnie rzecz biorąc, ISO-8601 jest dobrym wyborem (a format PO jest jednym z takich formatów). Lepiej unikać formatów z latami dwucyfrowymi.
days_diff=$(( (`date -d $B +%s` - `date -d "00:00" +%s`) / (24*3600) ))
. zauważ, że $days_diff
będzie to liczba całkowita (tj. bez miejsc po przecinku)
date
zainstalowanego. Działa dla GNU date
. Nie działa z POSIX date
. Prawdopodobnie nie stanowi to problemu dla większości czytelników, ale warto to zauważyć.
%Y-%m-%d
, dopóki nie wprowadzimy August 2.0
i October 2.0
format OP nie jest %Y-%d-%m
. Odpowiednie xkcd: xkcd.com/1179
tl; dr
date_diff=$(( ($(date -d "2015-03-11 UTC" +%s) - $(date -d "2015-03-05 UTC" +%s)) / (60*60*24) ))
Uważaj! Wiele z rozwiązań bash tutaj jest uszkodzonych dla zakresów dat, które obejmują datę rozpoczęcia czasu letniego (w stosownych przypadkach). Dzieje się tak, ponieważ konstrukcja $ ((math)) wykonuje operację „piętra” / obcięcia na wynikowej wartości, zwracając tylko liczbę całkowitą. Pozwólcie, że zilustruję:
Czas letni rozpoczął się 8 marca tego roku w Stanach Zjednoczonych, więc użyjmy zakresu dat obejmującego:
start_ts=$(date -d "2015-03-05" '+%s')
end_ts=$(date -d "2015-03-11" '+%s')
Zobaczmy, co otrzymamy za pomocą podwójnych nawiasów:
echo $(( ( end_ts - start_ts )/(60*60*24) ))
Zwraca wartość „5”.
Wykonanie tego przy użyciu „bc” z większą dokładnością daje inny wynik:
echo "scale=2; ( $end_ts - $start_ts )/(60*60*24)" | bc
Zwraca wartość „5,95” - brakujące 0,05 oznacza utraconą godzinę od przejścia na czas letni.
Jak więc należy to zrobić poprawnie?
Sugerowałbym użycie tego zamiast tego:
printf "%.0f" $(echo "scale=2; ( $end_ts - $start_ts )/(60*60*24)" | bc)
Tutaj „printf” zaokrągla dokładniejszy wynik obliczony przez „bc”, dając nam prawidłowy zakres dat wynoszący „6”.
Edycja: zaznaczenie odpowiedzi w komentarzu od @ hank-schultz poniżej, którego ostatnio używałem:
date_diff=$(( ($(date -d "2015-03-11 UTC" +%s) - $(date -d "2015-03-05 UTC" +%s) )/(60*60*24) ))
Powinno to być również bezpieczne, o ile zawsze odejmujesz wcześniejszą datę od późniejszej, ponieważ sekundy przestępne tylko dodają różnicę - obcięcie skutecznie zaokrągla w dół do prawidłowego wyniku.
echo $(( ($(date --date="2015-03-11 UTC" +%s) - $(date --date="2015-03-05 UTC" +%s) )/(60*60*24) ))
co zwraca 6 zamiast 5.
I w Pythonie
$python -c "from datetime import date; print (date(2003,11,22)-date(2002,10,20)).days"
398
bash
i shell
, więc myślę, że możemy założyć, że mówimy o systemie * nix. W takich systemach echo
i date
są niezawodnie obecne, ale Python nie.
To działa dla mnie:
A="2002-10-20"
B="2003-11-22"
echo $(( ($(date -d $B +%s) - $(date -d $A +%s)) / 86400 )) days
Wydruki
398 days
Co się dzieje?
date -d
do obsługi ciągów czasowychdate %s
do konwersji ciągów czasowych na sekundy od 1970 (unix epoche)Jeśli opcja -d działa w twoim systemie, oto inny sposób, aby to zrobić. Jest zastrzeżenie, że nie uwzględniłoby to lat przestępnych, ponieważ rozważałem 365 dni w roku.
date1yrs=`date -d "20100209" +%Y`
date1days=`date -d "20100209" +%j`
date2yrs=`date +%Y`
date2days=`date +%j`
diffyr=`expr $date2yrs - $date1yrs`
diffyr2days=`expr $diffyr \* 365`
diffdays=`expr $date2days - $date1days`
echo `expr $diffyr2days + $diffdays`
Oto wersja MAC OS X dla Twojej wygody.
$ A="2002-20-10"; B="2003-22-11";
$ echo $(((`date -jf %Y-%d-%m $B +%s` - `date -jf %Y-%d-%m $A +%s`)/86400))
nJoy!
A="2002-20-10"; B="2003-22-11"
echo $(((`date -jf "%Y-%d-%m" "$B" +%s` - `date -jf "%Y-%d-%m" "$A" +%s`)/86400))
, czyli zawijanie obu formatów i zmiennych w podwójne cudzysłowy, inaczej może się nie udać na innym formacie daty (np %b %d, %Y
/ Jul 5, 2017
)
Nawet jeśli nie masz daty GNU, prawdopodobnie będziesz mieć zainstalowany Perl:
use Time::Local;
sub to_epoch {
my ($t) = @_;
my ($y, $d, $m) = ($t =~ /(\d{4})-(\d{2})-(\d{2})/);
return timelocal(0, 0, 0, $d+0, $m-1, $y-1900);
}
sub diff_days {
my ($t1, $t2) = @_;
return (abs(to_epoch($t2) - to_epoch($t1))) / 86400;
}
print diff_days("2002-20-10", "2003-22-11"), "\n";
Zwraca to 398.041666666667
- 398 dni i jedną godzinę ze względu na czas letni.
Pytanie wróciło do mojego kanału. Oto bardziej zwięzła metoda wykorzystująca dołączony moduł Perla
days=$(perl -MDateTime -le '
sub parse_date {
@f = split /-/, shift;
return DateTime->new(year=>$f[0], month=>$f[2], day=>$f[1]);
}
print parse_date(shift)->delta_days(parse_date(shift))->in_units("days");
' $A $B)
echo $days # => 398
Korzystanie z polecenia mysql
$ echo "select datediff('2013-06-20 18:12:54+08:00', '2013-05-30 18:12:54+08:00');" | mysql -N
Wynik: 21
UWAGA: W obliczeniach używane są tylko części wartości dotyczące daty
Źródła: http://dev.mysql.com/doc/refman/5.6/en/date-and-time-functions.html#function_datediff
Oto moje podejście do pracy przy użyciu zsh. Testowane na OSX:
# Calculation dates
## A random old date
START_DATE="2015-11-15"
## Today's date
TODAY=$(date +%Y-%m-%d)
# Import zsh date mod
zmodload zsh/datetime
# Calculate number of days
DIFF=$(( ( $(strftime -r %Y-%m-%d $TODAY) - $(strftime -r %Y-%m-%d $START_DATE) ) / 86400 ))
echo "Your value: " $DIFF
Wynik:
Your value: 1577
Zasadniczo używamy funkcji strftime
reverse ( -r
), aby przekształcić nasz ciąg z datą z powrotem na znacznik czasu, a następnie wykonujemy obliczenia.
Przedstawiłbym inne możliwe rozwiązanie w Rubim. Wygląda na to, że jest najmniejszy i najczystszy do tej pory:
A=2003-12-11
B=2002-10-10
DIFF=$(ruby -rdate -e "puts Date.parse('$A') - Date.parse('$B')")
echo $DIFF
date
lub jakiegoś języka skryptowego) i szczerze uważam, że Ruby to dobry sposób, aby tu przejść. To rozwiązanie jest bardzo krótkie i nie wykorzystuje żadnych niestandardowych bibliotek ani innych zależności. W rzeczywistości uważam, że istnieje większe prawdopodobieństwo zainstalowania Rubiego niż zainstalowania daty GNU.
Użyj funkcji powłoki z http://cfajohnson.com/shell/ssr/ssr-scripts.tar.gz ; pracują w każdej standardowej powłoce Uniksa.
date1=2012-09-22
date2=2013-01-31
. date-funcs-sh
_date2julian "$date1"
jd1=$_DATE2JULIAN
_date2julian "$date2"
echo $(( _DATE2JULIAN - jd1 ))
Zobacz dokumentację pod adresem http://cfajohnson.com/shell/ssr/08-The-Dating-Game.shtml
na Uniksie powinieneś mieć zainstalowane daty GNU. nie musisz odchodzić od bash. tutaj jest rozwlekłe rozwiązanie uwzględniające dni, tylko po to, aby pokazać kroki. można go uprościć i rozszerzyć na pełne daty.
DATE=$(echo `date`)
DATENOW=$(echo `date -d "$DATE" +%j`)
DATECOMING=$(echo `date -d "20131220" +%j`)
THEDAY=$(echo `expr $DATECOMING - $DATENOW`)
echo $THEDAY
Zakłada się, że miesiąc to 1/12 roku:
#!/usr/bin/awk -f
function mktm(datespec) {
split(datespec, q, "-")
return q[1] * 365.25 + q[3] * 365.25 / 12 + q[2]
}
BEGIN {
printf "%d\n", mktm(ARGV[2]) - mktm(ARGV[1])
}
Spróbuj:
perl -e 'use Date::Calc qw(Delta_Days); printf "%d\n", Delta_Days(2002,10,20,2003,11,22);'
Załóżmy, że ręcznie rsyncujemy kopie zapasowe Oracle DB na trzeci dysk. Następnie chcemy usunąć stare kopie zapasowe z tego dysku. Oto mały skrypt basha:
#!/bin/sh
for backup_dir in {'/backup/cmsprd/local/backupset','/backup/cmsprd/local/autobackup','/backup/cfprd/backupset','/backup/cfprd/autobackup'}
do
for f in `find $backup_dir -type d -regex '.*_.*_.*' -printf "%f\n"`
do
f2=`echo $f | sed -e 's/_//g'`
days=$(((`date "+%s"` - `date -d "${f2}" "+%s"`)/86400))
if [ $days -gt 30 ]; then
rm -rf $backup_dir/$f
fi
done
done
Zmień katalogi i okres przechowywania („30 dni”) zgodnie ze swoimi potrzebami.
Dla MacOS sierra (może z Mac OS X yosemate),
Aby uzyskać czas epoki (sekundy od 1970 roku) z pliku i zapisać go w zmiennej:
old_dt=`date -j -r YOUR_FILE "+%s"`
Aby uzyskać aktualny czas epoki
new_dt=`date -j "+%s"`
Aby obliczyć różnicę czasu powyżej dwóch epok
(( diff = new_dt - old_dt ))
Aby sprawdzić, czy różnica jest większa niż 23 dni
(( new_dt - old_dt > (23*86400) )) && echo Is more than 23 days
echo $(date +%d/%h/%y) date_today
echo " The other date is 1970/01/01"
TODAY_DATE="1970-01-01"
BEFORE_DATE=$(date +%d%h%y)
echo "THE DIFFERNS IS " $(( ($(date -d $BEFORE_DATE +%s) - $(date -d $TODAY_DATE +%s)) )) SECOUND
użyj go, aby uzyskać dzisiejszą datę i sekundę od wybranej daty. jeśli chcesz to z dniami, po prostu podziel je przez 86400
echo $(date +%d/%h/%y) date_today
echo " The other date is 1970/01/01"
TODAY_DATE="1970-01-01"
BEFORE_DATE=$(date +%d%h%y)
echo "THE DIFFERNS IS " $(( ($(date -d $BEFORE_DATE +%s) - $(date -d $TODAY_DATE +%s))/ 86400)) SECOUND