İki tarih arasındaki gün farkı nasıl bulunur?


84

A = "2002-20-10"
B = "2003-22-11"

İki tarih arasındaki gün farkı nasıl bulunur?


5
Aşağıda açıklandığı gibi, ancak tarihleriniz yanlış yönde: yyyy-aa-gg olması gerekiyor
Peter Flynn

Çoğu cevap (GNU-) tarihinin -d seçeneğini kullanır. Bunun POSIX tarihinin bir parçası OLMADIĞINI, dolayısıyla daha az taşınabilir olduğunu eklemek istiyorum. OP, Solaris gibi Unix dağıtımları üzerinde çalışmadığı sürece, sadece "genel Linux" üzerinde çalıştığı sürece, o iyi olmalıdır. İlgili bir etiket imho'ya yardımcı olacaktır.
Cadoiz

Yanıtlar:


34

GNU'nuz varsa, daterastgele bir tarihin temsilini yazdırmanıza izin verir ( -disteğe bağlı). Bu durumda tarihleri ​​EPOCH'tan itibaren saniyeye dönüştürün, çıkarın ve 24 * 3600'e bölün.

Veya taşınabilir bir yola mı ihtiyacınız var?


1
@Tree: Tarihleri ​​çıkarmanın taşınabilir bir yolu olmadığına inanıyorum, bunu kendiniz uygulamaktan daha az - o kadar da zor değil, ilgilenilen tek şey artık yıllar. Ancak günümüzde herkes göründüğü gibi tarihleri ​​çıkarmak istiyor: Unix.stackexchange.com/questions/1825/… ' a bir göz atın :)

15
Tembel ağ uğruna, user332325'in kastettiği şeyin bu olduğunu düşünüyorum:echo "( `date -d $B +%s` - `date -d $A +%s`) / (24*3600)" | bc -l
Pablo Mendes

2
Bu bağlamda taşınabilir ne anlama geliyor?
htellez

@htellez birden fazla platformda çalışan bir şey (windows, linux vb.)
Sumudu

Bu yanıta dayalı bash bir
satır için

64

Bash yolu - tarihleri% y% m% d biçimine dönüştürün ve sonra bunu doğrudan komut satırından yapabilirsiniz:

echo $(( ($(date --date="031122" +%s) - $(date --date="021020" +%s) )/(60*60*24) ))

9
Bunun için tarihlerin %y%m%dformatta olması gerekmez . Örneğin gnu tarihi %Y-%m-%d, OP'nin kullandığı biçimi kabul edecektir . Genel olarak, ISO-8601 iyi bir seçimdir (ve OP'nin formatı böyle bir formattır). 2 basamaklı biçimlerden kaçınılması daha iyidir.
mc0e

2
Bugünden farkı için, kullanmak isteyebilirsiniz days_diff=$(( (`date -d $B +%s` - `date -d "00:00" +%s`) / (24*3600) )). $days_diffbunun bir tamsayı olacağını unutmayın (yani ondalık sayılar yok)
Julien

1
Bu, datekurulu modele bağlıdır . GNU için çalışıyor date. POSIX ile çalışmaz date. Bu muhtemelen çoğu okuyucu için bir sorun değildir, ancak kayda değer.
mc0e

POSIX taşınabilirliğini gerçekten istiyorsanız, şunu kontrol edin: unix.stackexchange.com/a/7220/43835
mc0e

1
Sadece kafa karışıklığıyla mücadele etmek için: OP'nin formatı, biz sunana ve OP'nin formatı olana kadar değildir . İlgili xkcd: xkcd.com/1179%Y-%m-%dAugust 2.0October 2.0%Y-%d-%m
confetti

22

tl; dr

date_diff=$(( ($(date -d "2015-03-11 UTC" +%s) - $(date -d "2015-03-05 UTC" +%s)) / (60*60*24) ))

Dikkat et! Buradaki bash çözümlerinin çoğu, yaz saati uygulamasının başladığı tarihi kapsayan tarih aralıkları için bozulmuştur (uygun olduğu yerlerde). Bunun nedeni, $ ((math)) yapısının, sonuçta elde edilen değer üzerinde bir 'kat' / kesme işlemi yapıp yalnızca tam sayıyı döndürmesidir. Göstereyim:

DST bu yıl ABD'de 8 Mart'ta başladı, bu nedenle aşağıdakileri kapsayan bir tarih aralığı kullanalım:

start_ts=$(date -d "2015-03-05" '+%s')
end_ts=$(date -d "2015-03-11" '+%s')

Çift parantez ile ne elde ettiğimize bir bakalım:

echo $(( ( end_ts - start_ts )/(60*60*24) ))

"5" değerini döndürür.

Bunu daha doğru bir şekilde 'bc' kullanarak yapmak bize farklı bir sonuç verir:

echo "scale=2; ( $end_ts - $start_ts )/(60*60*24)" | bc

'5.95' değerini döndürür - eksik 0.05, DST geçişinden itibaren kayıp saattir.

Peki bu nasıl doğru yapılmalıdır?
Bunun yerine şunu kullanmanızı öneririm:

printf "%.0f" $(echo "scale=2; ( $end_ts - $start_ts )/(60*60*24)" | bc)

Burada 'printf', 'bc' ile hesaplanan daha doğru sonucu yuvarlayarak bize '6' olan doğru tarih aralığını verir.

Düzenleme: Son zamanlarda kullandığım @ hank-schultz'dan gelen bir yorumda cevabı vurgulamak:

date_diff=$(( ($(date -d "2015-03-11 UTC" +%s) - $(date -d "2015-03-05 UTC" +%s) )/(60*60*24) ))

Bu, aynı zamanda, her zaman daha sonraktan önceki tarihi çıkardığınız sürece, artık saniye güvenli olmalıdır, çünkü artık saniyeler sadece farkı artıracaktır - kesme işlemi, doğru sonuca etkili bir şekilde yuvarlar.


6
Bunun yerine, hem başlangıç ​​hem de bitiş için saat dilimini belirtirseniz (ve aynı olduklarından emin olursanız), artık şu gibi bir sorun yaşamazsınız: echo $(( ($(date --date="2015-03-11 UTC" +%s) - $(date --date="2015-03-05 UTC" +%s) )/(60*60*24) ))5 yerine 6 döndürür.
Hank Schultz

1
Ah, cevap verenlerden biri, diğerlerinin varyantlarda tekrarladığı temel çözümün yanlışlıklarını düşündü. Başparmak havaya! Artık saniyeler nasıl olur? Artık saniye güvenli mi? Belli zamanlarda çalıştırılırsa bir güne bir sonuç veren yazılımların emsalleri vardır , artık saniye ile ilgili sorunları ref .
Stéphane Gourichon

Hay aksi, örnek olarak verdiğiniz "yanlış" hesaplama burada doğru şekilde 6 döndürüyor (Ubuntu 15.04 AMD64, GNU tarih sürümü 8.23). Belki de örneğiniz saat dilimine bağlıdır? Saat dilimim "Avrupa / Paris".
Stéphane Gourichon

MSYS üzerinde GNU 2.0 tarihi ile "yanlış" hesaplama için aynı iyi sonuç, aynı saat dilimi.
Stéphane Gourichon

1
@ StéphaneGourichon, saat diliminizin yaz saati uygulaması değişikliği 29 Mart 2015'te gerçekleşmiş gibi görünüyor - bu nedenle bunu içeren bir tarih aralığı deneyin.
evan_b

21

Ve pitonda

$python -c "from datetime import date; print (date(2003,11,22)-date(2002,10,20)).days"
398

2
@mouviciel gerçekten değil. Python her zaman mevcut değildir.
mc0e

1
@ mc0e bu bağlamda, hiçbir şey her zaman mevcut değildir
Sumudu

5
Olarak OP belirtilen etiketleri @Anubis bashve shellben biz * Nix sistemi hakkında konuşuyor varsayabiliriz düşünüyorum böylece. Bu tür sistemler içinde echove dategüvenilir bir şekilde mevcuttur, ancak python yoktur.
mc0e

2
@ mc0e evet bu mantıklı. Python2 çoğu * nix sisteminde mevcut olsa da, konu alt uç cihazlar (gömülü vb.) Olduğunda, sadece birincil araçlarla hayatta kalmamız gerekecek.
Sumudu

13

Bu benim için çalışıyor:

A="2002-10-20"
B="2003-11-22"
echo $(( ($(date -d $B +%s) - $(date -d $A +%s)) / 86400 )) days

Baskılar

398 days

Ne oluyor?

  1. A ve B'de geçerli zaman dizesi sağlayın
  2. date -dZaman dizelerini işlemek için kullanın
  3. date %s1970'den beri zaman dizelerini saniyeye dönüştürmek için kullanın (unix epoche)
  4. Saniyeleri çıkarmak için bash parametresi genişletmeyi kullanın
  5. gün olarak fark elde etmek için günde saniyeye bölün (86400 = 60 * 60 * 24)
  6. ! DST hesaba katılmaz! Bu yanıtı unix.stackexchange adresinde görebilirsiniz !

7

Sisteminizde -d seçeneği çalışıyorsa, işte bunu yapmanın başka bir yolu. Yılda 365 gün düşündüğüm için artık yılları hesaba katmayacağına dair bir uyarı var.

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`

7

Rahatınız için MAC OS X sürümü burada.

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


1
-bash: (-) / 86400: sözdizimi hatası: işlenen bekleniyor (hata simgesi ") / 86400")
cavalcade

1
@cavalcade - çünkü henüz A="2002-20-10"; B="2003-22-11"
yapmadın

Teşekkürler, bu özel durum için çalışmasına rağmen, kullanmanızı echo $(((`date -jf "%Y-%d-%m" "$B" +%s` - `date -jf "%Y-%d-%m" "$A" +%s`)/86400)), yani hem biçimleri hem de değişkenleri çift tırnak içine %b %d, %YJul 5, 2017
almayı öneririm

@ RAM237 Bu , kafama dikkat etmediğim için aldığım şey İyi tespit edildi, teşekkürler
cavalcade

5

GNU tarihiniz olmasa bile, muhtemelen Perl'i yükleyeceksiniz:

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";

Bu, 398.041666666667gün ışığından yararlanma nedeniyle 398 gün ve bir saat döndürür .


Soru beslememde geri geldi. Perl ile paketlenmiş bir modül kullanan daha kısa bir yöntem burada

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


2

Bu centos 7 üzerinde çalışmayı başardığım en basit şey:

OLDDATE="2018-12-31"
TODAY=$(date -d $(date +%Y-%m-%d) '+%s')
LINUXDATE=$(date -d "$OLDDATE" '+%s')
DIFFDAYS=$(( ($TODAY - $LINUXDATE) / (60*60*24) ))

echo $DIFFDAYS

2

İşte zsh kullanarak çalışma yaklaşımım. OSX'te test edildi:

# 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

Sonuç:

Your value:  1577

Temel olarak, tarih dizimizi tekrar bir zaman damgasına dönüştürmek için strftimereverse ( -r) özelliğini kullanıyoruz ve ardından hesaplamamızı yapıyoruz.


1

Ruby'de başka bir olası çözüm sunacağım. Şimdiye kadarki en küçük ve en temiz görünen şey gibi görünüyor

A=2003-12-11
B=2002-10-10
DIFF=$(ruby -rdate -e "puts Date.parse('$A') - Date.parse('$B')")
echo $DIFF

2
Bash'in bir yolunu arıyor.
Cojones

2
Kabuğun içinde bunu yapmanın taşınabilir bir yolu yoktur. Tüm alternatifler belirli harici programları kullanır (örn. GNU dateveya bazı komut dosyası dili) ve dürüst olmak gerekirse Ruby'nin buraya gitmek için iyi bir yol olduğunu düşünüyorum . Bu çözüm çok kısadır ve standart olmayan kitaplıkları veya diğer bağımlılıkları kullanmaz. Aslında, Ruby'nin kurulu olma şansının, birinin GNU tarihinin kurulu olacağından daha yüksek olduğunu düşünüyorum.
GreyCat

1

Başka bir Python sürümü:

python -c "from datetime import date; print date(2003, 11, 22).toordinal() - date(2002, 10, 20).toordinal()"


1

unix üzerinde GNU tarihlerinin kurulu olması gerekir. Bash'den sapmanıza gerek yoktur. İşte sadece adımları göstermek için günleri dikkate alan gergin bir çözüm. basitleştirilebilir ve tam tarihlere uzatılabilir.

DATE=$(echo `date`)
DATENOW=$(echo `date -d "$DATE" +%j`)
DATECOMING=$(echo `date -d "20131220" +%j`)
THEDAY=$(echo `expr $DATECOMING - $DATENOW`)

echo $THEDAY 

1

Bu, bir ayın yılın 1 / 12'si olduğunu varsayar:

#!/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])
}

0

Bunu bir deneyin:

perl -e 'use Date::Calc qw(Delta_Days); printf "%d\n", Delta_Days(2002,10,20,2003,11,22);'

0

Oracle DB yedeklemelerini üçüncü bir diske manuel olarak yeniden senkronize ettiğimizi varsayalım. Daha sonra o diskteki eski yedeklemeleri silmek istiyoruz. İşte küçük bir bash betiği:

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

Dir'leri ve saklama süresini ("30 gün") ihtiyaçlarınıza göre değiştirin.


Oracle, yedekleme kümelerini 'YYYY_MM_DD' gibi bir format kullanarak Flash Kurtarma Alanına koyar, bu nedenle alt çizgileri 'tarih -d'ye geçirmek için sileriz
Alex Cherkas

0

MacOS sierra için (belki Mac OS X yosemate'den),

Bir dosyadan dönem zamanını (1970'den saniye) almak ve bir var'a kaydetmek için: old_dt=`date -j -r YOUR_FILE "+%s"`

Geçerli zamanın çağ zamanını öğrenmek için new_dt=`date -j "+%s"`

İki dönemin üzerindeki farkı hesaplamak için (( diff = new_dt - old_dt ))

Farkın 23 günden fazla olup olmadığını kontrol etmek için (( new_dt - old_dt > (23*86400) )) && echo Is more than 23 days


0
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

bugün tarihinizi ve istediğiniz tarihten itibaren saniyenizi almak için kullanın. günlerle istiyorsanız sadece 86400 ile bölün

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.