Bh betiğinde geçen süre nasıl hesaplanır?


224

Başlangıç ​​ve bitiş zamanını kullanarak yazdırıyorum date +"%T", bu da şöyle bir şeyle sonuçlanır:

10:33:56
10:36:10

Bu ikisi arasındaki farkı nasıl hesaplayabilir ve yazdırabilirim?

Gibi bir şey almak istiyorum:

2m 14s

5
Başka bir notta, timekomutu kullanamaz mısınız?
anishsane

Bunun yerine unix zamanını kullanın, date +%sardından saniye cinsinden farkı elde etmek için çıkarın.
roblogic

Yanıtlar:


476

Bash, SECONDSkabuk başlatıldığından bu yana geçen saniye sayısını izleyen kullanışlı bir yerleşik değişkene sahiptir. Bu değişken atandığında özelliklerini korur ve atamadan sonra döndürülen değer atamadan sonra atanan değerin saniye cinsinden sayısıdır.

Böylece, SECONDSzamanlanmış olayı başlatmadan önce 0'a ayarlayabilir SECONDS, olaydan sonra kolayca okuyabilir ve görüntülemeden önce zaman aritmetiği yapabilirsiniz.

SECONDS=0
# do some work
duration=$SECONDS
echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."

Bu çözüm bağımlı olmadığından date +%s(bu bir GNU uzantısıdır), Bash tarafından desteklenen tüm sistemlere taşınabilir.


59
+1. Bu arada, datezaman aritmetiğini yazarak kandırabilirsiniz date -u -d @"$diff" +'%-Mm %-Ss'. (Bu $diff, çağdan beri saniye olarak yorumlanır ve UTC'deki dakika ve saniyeleri hesaplar.) Bu muhtemelen daha zarif değil, ancak daha iyi gizlenmiş. :-P
ruakh

13
Güzel ve basit. Birinin saatlere ihtiyacı varsa:echo "$(($diff / 3600)) hours, $((($diff / 60) % 60)) minutes and $(($diff % 60)) seconds elapsed."
chus

6
$(date -u +%s)Yaz saati uygulamasının değiştirildiği tarihlerde yarış koşulunu önlemek için okunmalıdır . Ve kötü sıçrama saniye dikkat;)
Tino

3
@ daniel-kamil-kozar Güzel bul. Ancak bu biraz kırılgan. Böyle bir değişken vardır, bu yüzden performansı ölçmek için tekrar tekrar kullanılamaz ve daha da kötüsü, gittikten sonra unset SECONDS:echo $SECONDS; unset SECONDS; SECONDS=0; sleep 3; echo $SECONDS
Tino

6
@ParthianShot: Düşündüğün için üzgünüm. Bununla birlikte, bu sorunun çoğu insanın böyle bir soruya cevap ararken ihtiyaç duyduğuna inanıyorum: olaylar arasında ne kadar zaman geçtiğini ölçmek, zamanı hesaplamak için değil.
Daniel Kamil Kozar

99

Saniye

Geçen süreyi (saniye cinsinden) ölçmek için ihtiyacımız olan:

  • geçen saniye sayısını temsil eden bir tam sayı ve
  • bu tamsayıyı kullanılabilir bir biçime dönüştürmenin bir yolu.

Geçen saniyelerin tamsayı değeri:

  • Geçen saniye sayısı için bir tamsayı değeri bulmanın iki bash dahili yolu vardır:

    1. Bash değişkeni SECONDS (SECONDS ayarlanmamışsa özel özelliğini kaybeder).

      • SECONDS değerinin 0 olarak ayarlanması:

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
      • Değişkenin değerini SECONDSbaşlangıçta saklama :

        a=$SECONDS
        sleep 1  # Process to execute
        elapsedseconds=$(( SECONDS - a ))
    2. Bash printf seçeneği %(datefmt)T:

      a="$(TZ=UTC0 printf '%(%s)T\n' '-1')"    ### `-1`  is the current time
      sleep 1                                  ### Process to execute
      elapsedseconds=$(( $(TZ=UTC0 printf '%(%s)T\n' '-1') - a ))

Böyle bir tamsayıyı kullanılabilir bir biçime dönüştürün

Bash dahili printfbunu doğrudan yapabilir:

$ TZ=UTC0 printf '%(%H:%M:%S)T\n' 12345
03:25:45

benzer şekilde

$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
00:12:34

ancak bu gerçekten bir süre değil, bir duvar saati süresi yazdırdığımız için 24 saatten uzun süreler boyunca başarısız olacaktır:

$ hours=30;mins=12;secs=24
$ elapsedseconds=$(( ((($hours*60)+$mins)*60)+$secs ))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
06:12:24

Ayrıntı sevenler için bash-hackers.org'dan :

%(FORMAT)TFORMAT'ın biçim dizesi olarak kullanılmasından kaynaklanan tarih-saat dizesini çıktılar strftime(3). İlişkili bağımsız değişken, Epoch'tan bu yana geçen saniye sayısı veya -1 (geçerli saat) veya -2 (kabuk başlangıç ​​saati). Karşılık gelen bağımsız değişken sağlanmazsa, geçerli saat varsayılan olarak kullanılır.

Bu nedenle , süre baskısının başka bir uygulaması olan textifyDuration $elpasedsecondsyeri aramak isteyebilirsiniz textifyDuration:

textifyDuration() {
   local duration=$1
   local shiff=$duration
   local secs=$((shiff % 60));  shiff=$((shiff / 60));
   local mins=$((shiff % 60));  shiff=$((shiff / 60));
   local hours=$shiff
   local splur; if [ $secs  -eq 1 ]; then splur=''; else splur='s'; fi
   local mplur; if [ $mins  -eq 1 ]; then mplur=''; else mplur='s'; fi
   local hplur; if [ $hours -eq 1 ]; then hplur=''; else hplur='s'; fi
   if [[ $hours -gt 0 ]]; then
      txt="$hours hour$hplur, $mins minute$mplur, $secs second$splur"
   elif [[ $mins -gt 0 ]]; then
      txt="$mins minute$mplur, $secs second$splur"
   else
      txt="$secs second$splur"
   fi
   echo "$txt (from $duration seconds)"
}

GNU tarihi.

Belirlenmiş zamanı elde etmek için, neredeyse bir yıl uzunluğa ulaşmak ve Nanosaniye dahil olmak üzere çeşitli şekillerde harici bir araç (GNU tarihi) kullanmalıyız.

Tarih içinde.

Dış aritmetiğe gerek yoktur, hepsini bir adımda yapın date:

date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"

Evet, 0komut dizesinde sıfır var . Gerekli.

Bu, date +"%T"komutun bir date +"%s"komutla değiştirilebileceği varsayılır, böylece değerler saniyeler içinde saklanır (yazdırılır).

Komutun aşağıdakilerle sınırlı olduğunu unutmayın:

  • Pozitif değerler $StartDateve $FinalDatesaniye.
  • Değer $FinalDate, daha sonradan (daha sonra) büyüktür $StartDate.
  • Zaman farkı 24 saatten küçük.
  • Saat, Dakika ve Saniye ile çıktı biçimini kabul edersiniz. Değiştirmesi çok kolay.
  • -U UTC saatlerini kullanmak kabul edilebilir. "DST" ve yerel saat düzeltmelerini önlemek için.

Eğer dizeyi kullanmanız gerekiyorsa10:33:56 , sadece saniyeye dönüştürün,
ayrıca saniye kelimesi sn olarak kısaltılabilir:

string1="10:33:56"
string2="10:36:10"
StartDate=$(date -u -d "$string1" +"%s")
FinalDate=$(date -u -d "$string2" +"%s")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S"

Saniyede zaman dönüşümünün (yukarıda gösterildiği gibi) "bu" günün başlangıcına (Bugün) göre olduğunu unutmayın.


Konsept nanosaniyeye uzatılabilir, şöyle:

string1="10:33:56.5400022"
string2="10:36:10.8800056"
StartDate=$(date -u -d "$string1" +"%s.%N")
FinalDate=$(date -u -d "$string2" +"%s.%N")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S.%N"

Daha uzun (364 güne kadar) zaman farklarını hesaplamak gerekirse, (bazı) yılın başlangıcını referans olarak ve biçim değerini %j(yıl içindeki gün numarası) kullanmalıyız:

Benzer:

string1="+10 days 10:33:56.5400022"
string2="+35 days 10:36:10.8800056"
StartDate=$(date -u -d "2000/1/1 $string1" +"%s.%N")
FinalDate=$(date -u -d "2000/1/1 $string2" +"%s.%N")
date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N"

Output:
026 days 00:02:14.340003400

Ne yazık ki, bu durumda, ONE'yi 1gün sayısından manuel olarak çıkarmamız gerekiyor . Tarih komutu yılın ilk gününü 1 olarak görüntüler. O kadar zor değil ...

a=( $(date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N") )
a[0]=$((10#${a[0]}-1)); echo "${a[@]}"



Uzun saniye kullanımı geçerlidir ve burada belgelenmiştir:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


Meşgul Kutusu tarihi

Daha küçük cihazlarda kullanılan bir araç (kurulumu çok küçük yürütülebilir): Busybox.

Date adlı bir meşgul kutusuna bağlantı verin:

$ ln -s /bin/busybox date

Daha sonra bunu çağırarak kullanın date(PATH dahil bir dizine yerleştirin).

Veya aşağıdaki gibi bir takma ad yapın:

$ alias date='busybox date'

Busybox tarihinin güzel bir seçeneği var: -D giriş zamanının biçimini almak için. Bu, zaman olarak kullanılacak birçok biçimi açar. -D seçeneğini kullanarak, zamanı 10:33:56 doğrudan dönüştürebiliriz:

date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"

Ve yukarıdaki Komuta'nın çıktısından da görebileceğiniz gibi, günün "bugün" olduğu varsayılır. Dönemden başlamak için:

$ string1="10:33:56"
$ date -u -D "%Y.%m.%d-%H:%M:%S" -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

Busybox tarihi saati -D olmadan da alabilir (yukarıdaki formatta):

$ date -u -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

Ve çıktı biçimi çağdan beri saniye bile olabilir.

$ date -u -d "1970.01.01-$string1" +"%s"
52436

Her iki kez ve biraz bash matematik (busybox henüz matematik yapamaz):

string1="10:33:56"
string2="10:36:10"
t1=$(date -u -d "1970.01.01-$string1" +"%s")
t2=$(date -u -d "1970.01.01-$string2" +"%s")
echo $(( t2 - t1 ))

Veya biçimlendirilmiş:

$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14

6
Bu, çok sayıda üs ve ince açıklamalarla kaplı inanılmaz bir cevaptır. Teşekkür ederim!
Devy

Ben bash entegre komut %(FORMAT)Tgeçirilen dize bakmak zorunda kaldı printf. Gönderen bash-hackers.org : %(FORMAT)Tçıkışı tarih-saat dize kullanarak kaynaklanan FORMATstrftime'a (3) için bir biçim dizesi olarak. İlişkili bağımsız değişken, Epoch'tan bu yana geçen saniye sayısı veya -1 (geçerli saat) veya -2 (kabuk başlangıç ​​saati). Karşılık gelen bir argüman sağlanmazsa, geçerli saat varsayılan olarak kullanılır . Bu ezoterik. Bu durumda, %sverildiği gibi strftime(3)"çağdan bu yana geçen saniye sayısı" dır.
David Tonhofer

35

İşte nasıl yaptım:

START=$(date +%s);
sleep 1; # Your stuff
END=$(date +%s);
echo $((END-START)) | awk '{print int($1/60)":"int($1%60)}'

Gerçekten basit, başlangıçtaki saniye sayısını alın, sonra sondaki saniye sayısını alın ve farkı dakika cinsinden yazdırın: saniye.


6
Bunun benim çözümümden farkı nedir? awkBash tamsayı aritmetiği eşit derecede iyi idare ettiğinden, bu durumda çağırmanın faydasını gerçekten göremiyorum .
Daniel Kamil Kozar

1
Cevabınız da doğru. Benim gibi bazı insanlar bash tutarsızlıklarından daha çok awk ile çalışmayı tercih ediyor.
Dorian

2
Bash'te tamsayı aritmetiği ile ilgili tutarsızlıklar hakkında biraz daha bilgi verebilir misiniz? Bunun hakkında daha fazla bilgi edinmek istiyorum, çünkü hiçbirinin farkında değildim.
Daniel Kamil Kozar

12
Sadece bunu arıyordum. Bu cevaba yönelik eleştiriyi anlamıyorum. Bir soruna birden fazla çözüm görmek hoşuma gidiyor. Ve ben awkbash komutlarını tercih eden biriyim (başka bir şey için değilse, awk diğer kabuklarda çalışır). Bu çözümü daha çok sevdim. Ama bu benim kişisel görüşüm.
rpsml

4
Baştaki sıfırlar: echo $ ((END-START)) | awk '{printf "% 02d:% 02d \ n", int (1/60 $), int (1% 60 $)}'
Jon Strayer

17

Başka bir seçenek kullanıma etmektir datediffdan dateutils( http://www.fresse.org/dateutils/#datediff ):

$ datediff 10:33:56 10:36:10
134s
$ datediff 10:33:56 10:36:10 -f%H:%M:%S
0:2:14
$ datediff 10:33:56 10:36:10 -f%0H:%0M:%0S
00:02:14

Ayrıca kullanabilirsiniz gawk. mawk1.3.4 ayrıca strftimeve mktimeeski sürümlerine sahiptir mawkve nawkyoktur.

$ TZ=UTC0 awk 'BEGIN{print strftime("%T",mktime("1970 1 1 10 36 10")-mktime("1970 1 1 10 33 56"))}'
00:02:14

Veya GNU ile yapmanın başka bir yolu date:

$ date -ud@$(($(date -ud'1970-01-01 10:36:10' +%s)-$(date -ud'1970-01-01 10:33:56' +%s))) +%T
00:02:14

1
GNU date yönteminiz gibi bir şey arıyordum . Cin.
Haohmaru

Lütfen
Bill Gale

dateutilsApt aracılığıyla yükledikten sonra , datediffkomut yoktu - kullanmak zorunda kaldı dateutils.ddiff.
Suzana

12

dateKomutu hatırlamaktan kaçınmak için başka bir yol önermek istiyorum . Zaten %Ttarih biçiminde zaman damgaları topladıysanız yardımcı olabilir :

ts_get_sec()
{
  read -r h m s <<< $(echo $1 | tr ':' ' ' )
  echo $(((h*60*60)+(m*60)+s))
}

start_ts=10:33:56
stop_ts=10:36:10

START=$(ts_get_sec $start_ts)
STOP=$(ts_get_sec $stop_ts)
DIFF=$((STOP-START))

echo "$((DIFF/60))m $((DIFF%60))s"

milisaniyeyi de aynı şekilde halledebiliriz.

ts_get_msec()
{
  read -r h m s ms <<< $(echo $1 | tr '.:' ' ' )
  echo $(((h*60*60*1000)+(m*60*1000)+(s*1000)+ms))
}

start_ts=10:33:56.104
stop_ts=10:36:10.102

START=$(ts_get_msec $start_ts)
STOP=$(ts_get_msec $stop_ts)
DIFF=$((STOP-START))

min=$((DIFF/(60*1000)))
sec=$(((DIFF%(60*1000))/1000))
ms=$(((DIFF%(60*1000))%1000))

echo "${min}:${sec}.$ms"

1
Milisaniyeleri idare etmenin bir yolu var mı, örneğin 10: 33: 56.104
Umesh Rajbhandari

Milisaniye alanı sıfırla başlarsa, milisaniye işleme hatalı davranır. Örneğin ms = "033", ms alanı sekizli olarak yorumlandığından "027" nin sonundaki rakamları verecektir. "echo" ... "+ ms))" olarak ... "+ $ {ms ## + (0)}))" "," shopt -s extglob "ifadesi, senaryo. Muhtemelen h, m ve s'den de önde gelen sıfırları çıkarmalı ...
Eric Towers

3
echo $(((60*60*$((10#$h)))+(60*$((10#$m)))+$((10#$s))))aldığımdan beri benim için çalıştıvalue too great for base (error token is "08")
Niklas

6

İşte biraz sihir:

time1=14:30
time2=$( date +%H:%M ) # 16:00
diff=$(  echo "$time2 - $time1"  | sed 's%:%+(1/60)*%g' | bc -l )
echo $diff hours
# outputs 1.5 hours

seda yerine :1/60 biçimine dönüştürmek için bir formül kullanır. Sonra tarafından yapılan zaman hesaplamasıbc


5

Tarih itibarıyla (GNU coreutils) 7.4 artık aritmetik işlem yapmak için -d'yi kullanabilirsiniz:

$ date -d -30days
Sat Jun 28 13:36:35 UTC 2014

$ date -d tomorrow
Tue Jul 29 13:40:55 UTC 2014

Kullanabileceğiniz birimler gün, yıl, ay, saat, dakika ve saniyedir:

$ date -d tomorrow+2days-10minutes
Thu Jul 31 13:33:02 UTC 2014

3

Daniel Kamil Kozar'ın cevabından sonra saat / dakika / saniye göstermek için:

echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"

Komut dosyasının tamamı şöyle olur:

date1=$(date +"%s")
date2=$(date +"%s")
diff=$(($date2-$date1))
echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"

3

Veya biraz sarın

alias timerstart='starttime=$(date +"%s")'
alias timerstop='echo seconds=$(($(date +"%s")-$starttime))'

Sonra bu işe yarar.

timerstart; sleep 2; timerstop
seconds=2

3

dateBitirme süresini saklamak için ikinci bir değişken kullanmayan ve yalnızca "ago" kullanan komutların yeteneklerini kullanan bir çözüm :

#!/bin/bash

# save the current time
start_time=$( date +%s.%N )

# tested program
sleep 1

# the current time after the program has finished
# minus the time when we started, in seconds.nanoseconds
elapsed_time=$( date +%s.%N --date="$start_time seconds ago" )

echo elapsed_time: $elapsed_time

bu verir:

$ ./time_elapsed.sh 
elapsed_time: 1.002257120

2
% start=$(date +%s)
% echo "Diff: $(date -d @$(($(date +%s)-$start)) +"%M minutes %S seconds")"
Diff: 00 minutes 11 seconds

6
Bu cevabın diğer cevapların yapmadığını ne yaptığını bilmek faydalı olacaktır.
Louis

Süreyi izin verdiği herhangi bir formatta kolayca vermenizi sağlar date.
Ryan

2

date size fark verebilir ve sizin için biçimlendirebilir (OS X seçenekleri gösterilmiştir)

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) +%T
# 00:02:14

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) \
    +'%-Hh %-Mm %-Ss'
# 0h 2m 14s

Bazı dize işlemleri bu boş değerleri kaldırabilir

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) \
    +'%-Hh %-Mm %-Ss' | sed "s/[[:<:]]0[hms] *//g"
# 2m 14s

Daha önce ilk kez yerleştirirseniz bu çalışmaz. Bunu, bu değişikliği kaldırabilecek gerekiyorsa $(($(date ...) - $(date ...)))etmek$(echo $(date ...) - $(date ...) | bc | tr -d -)


1

mencoder(Onun --endposgöreceli) ile kullanmak için bir zaman farkı komut dosyası gerekiyordu ve benim çözüm bir Python komut dosyası çağırmaktır:

$ ./timediff.py 1:10:15 2:12:44
1:02:29

saniye kesirleri de desteklenir:

$ echo "diff is `./timediff.py 10:51.6 12:44` (in hh:mm:ss format)"
diff is 0:01:52.4 (in hh:mm:ss format)

ve 200 ile 120 arasındaki farkın 1h 20m olduğunu söyleyebilir:

$ ./timediff.py 120:0 200:0
1:20:0

ve herhangi bir (muhtemelen kesirli) saniye veya dakika veya saati ss: dd: ss'ye dönüştürebilir

$ ./timediff.py 0 3600
1:00:0
$ ./timediff.py 0 3.25:0:0
3:15:0

timediff.py:

#!/usr/bin/python

import sys

def x60(h,m):
    return 60*float(h)+float(m)

def seconds(time):
    try:
       h,m,s = time.split(':')
       return x60(x60(h,m),s)
    except ValueError:
       try:
          m,s = time.split(':')
          return x60(m,s)
       except ValueError:
          return float(time)

def difftime(start, end):
    d = seconds(end) - seconds(start)
    print '%d:%02d:%s' % (d/3600,d/60%60,('%02f' % (d%60)).rstrip('0').rstrip('.'))

if __name__ == "__main__":
   difftime(sys.argv[1],sys.argv[2])

1

GNU ile units:

$ units
2411 units, 71 prefixes, 33 nonlinear units
You have: (10hr+36min+10s)-(10hr+33min+56s)
You want: s
    * 134
    / 0.0074626866
You have: (10hr+36min+10s)-(10hr+33min+56s)
You want: min
    * 2.2333333
    / 0.44776119

1

@ Nisetama'nın çözümünü GNU tarihi kullanarak genelleştirme (güvenilir Ubuntu 14.04 LTS):

start=`date`
# <processing code>
stop=`date`
duration=`date -ud@$(($(date -ud"$stop" +%s)-$(date -ud"$start" +%s))) +%T`

echo $start
echo $stop
echo $duration

getirili:

Wed Feb 7 12:31:16 CST 2018
Wed Feb 7 12:32:25 CST 2018
00:01:09

1
#!/bin/bash

START_TIME=$(date +%s)

sleep 4

echo "Total time elapsed: $(date -ud "@$(($(date +%s) - $START_TIME))" +%T) (HH:MM:SS)"
$ ./total_time_elapsed.sh 
Total time elapsed: 00:00:04 (HH:MM:SS)

1

Bu işlevi tanımlayın (örneğin ~ / .bashrc içinde):

time::clock() {
    [ -z "$ts" ]&&{ ts=`date +%s%N`;return;}||te=`date +%s%N`
    printf "%6.4f" $(echo $((te-ts))/1000000000 | bc -l)
    unset ts te
}

Artık komut dosyalarınızın bölümlerinin zamanını ölçebilirsiniz:

$ cat script.sh
# ... code ...
time::clock
sleep 0.5
echo "Total time: ${time::clock}"
# ... more code ...

$ ./script.sh
Total time: 0.5060

yürütme darboğazlarını bulmak için çok yararlı.


0

Bunun daha eski bir gönderi olduğunu anlıyorum, ancak bugün bir günlük dosyasından tarihler ve saatler alan ve deltayı hesaplayan bir komut dosyası üzerinde çalışırken ona rastladım. Aşağıdaki senaryo kesinlikle abartılı ve mantığımı ve matematiği kontrol etmenizi şiddetle tavsiye ederim.

#!/bin/bash

dTime=""
tmp=""

#firstEntry="$(head -n 1 "$LOG" | sed 's/.*] \([0-9: -]\+\).*/\1/')"
firstEntry="2013-01-16 01:56:37"
#lastEntry="$(tac "$LOG" | head -n 1 | sed 's/.*] \([0-9: -]\+\).*/\1/')"
lastEntry="2014-09-17 18:24:02"

# I like to make the variables easier to parse
firstEntry="${firstEntry//-/ }"
lastEntry="${lastEntry//-/ }"
firstEntry="${firstEntry//:/ }"
lastEntry="${lastEntry//:/ }"

# remove the following lines in production
echo "$lastEntry"
echo "$firstEntry"

# compute days in last entry
for i in `seq 1 $(echo $lastEntry|awk '{print $2}')`; do {
  case "$i" in
   1|3|5|7|8|10|12 )
    dTime=$(($dTime+31))
    ;;
   4|6|9|11 )
    dTime=$(($dTime+30))
    ;;
   2 )
    dTime=$(($dTime+28))
    ;;
  esac
} done

# do leap year calculations for all years between first and last entry
for i in `seq $(echo $firstEntry|awk '{print $1}') $(echo $lastEntry|awk '{print $1}')`; do {
  if [ $(($i%4)) -eq 0 ] && [ $(($i%100)) -eq 0 ] && [ $(($i%400)) -eq 0 ]; then {
    if [ "$i" = "$(echo $firstEntry|awk '{print $1}')" ] && [ $(echo $firstEntry|awk '{print $2}') -lt 2 ]; then {
      dTime=$(($dTime+1))
    } elif [ $(echo $firstEntry|awk '{print $2}') -eq 2 ] && [ $(echo $firstEntry|awk '{print $3}') -lt 29 ]; then {
      dTime=$(($dTime+1))
    } fi
  } elif [ $(($i%4)) -eq 0 ] && [ $(($i%100)) -ne 0 ]; then {
    if [ "$i" = "$(echo $lastEntry|awk '{print $1}')" ] && [ $(echo $lastEntry|awk '{print $2}') -gt 2 ]; then {
      dTime=$(($dTime+1))
    } elif [ $(echo $lastEntry|awk '{print $2}') -eq 2 ] && [ $(echo $lastEntry|awk '{print $3}') -ne 29 ]; then {
      dTime=$(($dTime+1))
    } fi
  } fi
} done

# substract days in first entry
for i in `seq 1 $(echo $firstEntry|awk '{print $2}')`; do {
  case "$i" in
   1|3|5|7|8|10|12 )
    dTime=$(($dTime-31))
    ;;
   4|6|9|11 )
    dTime=$(($dTime-30))
    ;;
   2 )
    dTime=$(($dTime-28))
    ;;
  esac
} done

dTime=$(($dTime+$(echo $lastEntry|awk '{print $3}')-$(echo $firstEntry|awk '{print $3}')))

# The above gives number of days for sample. Now we need hours, minutes, and seconds
# As a bit of hackery I just put the stuff in the best order for use in a for loop
dTime="$(($(echo $lastEntry|awk '{print $6}')-$(echo $firstEntry|awk '{print $6}'))) $(($(echo $lastEntry|awk '{print $5}')-$(echo $firstEntry|awk '{print $5}'))) $(($(echo $lastEntry|awk '{print $4}')-$(echo $firstEntry|awk '{print $4}'))) $dTime"
tmp=1
for i in $dTime; do {
  if [ $i -lt 0 ]; then {
    case "$tmp" in
     1 )
      tmp="$(($(echo $dTime|awk '{print $1}')+60)) $(($(echo $dTime|awk '{print $2}')-1))"
      dTime="$tmp $(echo $dTime|awk '{print $3" "$4}')"
      tmp=1
      ;;
     2 )
      tmp="$(($(echo $dTime|awk '{print $2}')+60)) $(($(echo $dTime|awk '{print $3}')-1))"
      dTime="$(echo $dTime|awk '{print $1}') $tmp $(echo $dTime|awk '{print $4}')"
      tmp=2
      ;;
     3 )
      tmp="$(($(echo $dTime|awk '{print $3}')+24)) $(($(echo $dTime|awk '{print $4}')-1))"
      dTime="$(echo $dTime|awk '{print $1" "$2}') $tmp"
      tmp=3
      ;;
    esac
  } fi
  tmp=$(($tmp+1))
} done

echo "The sample time is $(echo $dTime|awk '{print $4}') days, $(echo $dTime|awk '{print $3}') hours, $(echo $dTime|awk '{print $2}') minutes, and $(echo $dTime|awk '{print $1}') seconds."

Aşağıdaki gibi çıktı alacaksınız.

2012 10 16 01 56 37
2014 09 17 18 24 02
The sample time is 700 days, 16 hours, 27 minutes, and 25 seconds.

Betiği bağımsız hale getirmek için biraz değiştirdim (yani sadece değişken değerleri ayarlayın), ancak belki de genel fikir de karşımıza çıkıyor. Negatif değerler için ek hata kontrolü yapmak isteyebilirsiniz.


Gee, bir sürü iş !! Lütfen

0

Mcaleaa'nın cevabı hakkında yorum yapamam, bu yüzden buraya gönderiyorum. "Diff" değişkeni küçük durumda olmalıdır. İşte bir örnek.

[root@test ~]# date1=$(date +"%s"); date
Wed Feb 21 23:00:20 MYT 2018
[root@test ~]# 
[root@test ~]# date2=$(date +"%s"); date
Wed Feb 21 23:00:33 MYT 2018
[root@test ~]# 
[root@test ~]# diff=$(($date2-$date1))
[root@test ~]# 

Önceki değişken küçük harflerle bildirildi. Büyük harf kullanıldığında olan şey budur.

[root@test ~]# echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"
-bash: / 3600 : syntax error: operand expected (error token is "/ 3600 ")
[root@test ~]# 

Yani, hızlı düzeltme böyle olurdu

[root@test ~]# echo "Duration: $(($diff / 3600 )) hours $((($diff % 3600) / 60)) minutes $(($diff % 60)) seconds"
Duration: 0 hours 0 minutes 13 seconds
[root@test ~]# 

-1

İşte benim bash uygulaması (diğer SO ;-) alınan bit ile

function countTimeDiff() {
    timeA=$1 # 09:59:35
    timeB=$2 # 17:32:55

    # feeding variables by using read and splitting with IFS
    IFS=: read ah am as <<< "$timeA"
    IFS=: read bh bm bs <<< "$timeB"

    # Convert hours to minutes.
    # The 10# is there to avoid errors with leading zeros
    # by telling bash that we use base 10
    secondsA=$((10#$ah*60*60 + 10#$am*60 + 10#$as))
    secondsB=$((10#$bh*60*60 + 10#$bm*60 + 10#$bs))
    DIFF_SEC=$((secondsB - secondsA))
    echo "The difference is $DIFF_SEC seconds.";

    SEC=$(($DIFF_SEC%60))
    MIN=$((($DIFF_SEC-$SEC)%3600/60))
    HRS=$((($DIFF_SEC-$MIN*60)/3600))
    TIME_DIFF="$HRS:$MIN:$SEC";
    echo $TIME_DIFF;
}

$ countTimeDiff 2:15:55 2:55:16
The difference is 2361 seconds.
0:39:21

Test edilmedi, buggy olabilir.

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.