Nhanh chóng tính toán chênh lệch ngày


84

Tôi thường muốn thực hiện một số tính toán ngày nhanh, chẳng hạn như:

  • Sự khác biệt giữa hai ngày này là gì?
  • Ngày n tuần sau ngày khác này là ngày nào?

Tôi thường mở lịch và đếm ngày, nhưng tôi nghĩ nên có một chương trình / kịch bản mà tôi có thể sử dụng để thực hiện các loại tính toán này. Bất kỳ đề xuất?


3
Xem thêm Công cụ trong UNIX để trừ ngày khi ngày GNU không có sẵn.
Gilles

Câu trả lời:


107

"N tuần sau một ngày" thật dễ dàng với ngày GNU (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

Tôi không biết một cách đơn giản để tính toán sự khác biệt giữa hai ngày, nhưng bạn có thể gói một chút logic xung quanh ngày (1) bằng hàm shell.

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

Hoán đổi d1d2nếu bạn muốn tính toán ngày theo cách khác, hoặc nhận được một chút fancier để làm cho nó không quan trọng. Hơn nữa, trong trường hợp có sự chuyển đổi không DST sang DST trong khoảng thời gian, một trong những ngày sẽ chỉ kéo dài 23 giờ; bạn có thể bù bằng cách thêm ½ ngày vào tổng.

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

41

Đối với một bộ công cụ di động hãy thử dateutils của riêng tôi . Hai ví dụ của bạn sẽ sôi sùng sục xuống một lớp:

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

hoặc trong tuần và ngày:

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

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

3
+1 công cụ của bạn hoạt động (mặc dù dateadd -i '%m%d%Y' 01012015 +1ddường như không hoạt động, nó chỉ treo ở đó vô thời hạn ... nó hoạt động nếu thông số ngày được phân tách bằng char, bất kỳ char nào ... có ý kiến ​​gì không?)
don_crissti

2
@don_crissti Trình phân tích cú pháp không thể phân biệt giữa ngày và thời lượng chỉ bằng số, nó đã được sửa trong bản gốc hiện tại (d0008f98)
hroptatyr

Bạn có phân phối nhị phân của cửa sổ ngày?
mosh

@mosh không, và tôi không có phương tiện để thử.
hroptatyr

Đến bữa tiệc muộn ở đây, nhưng dateutils có trên cygwin nếu bạn cần sử dụng trên Windows.
gregb212

30

Một ví dụ về con trăn để tính số ngày tôi đã đi trên hành tinh này:

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

2
Chỉ trong trường hợp ai đó muốn điều này hoạt động giống như một lệnh duy nhất, thay vì nhập một trình thông dịch tương tác: 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
điều này hoạt động với tôi python3 -c "từ ngày nhập dữ liệu theo thời gian là d; print (d.today () - d (2016, 1, 9))" ngày kết thúc là không bắt buộc
Kiran K Telukunta

13

Tôi thường thích có thời gian / ngày ở định dạng unix utime (số giây kể từ kỷ nguyên, khi những năm bảy mươi bắt đầu, UTC). Bằng cách đó, nó luôn sôi sục đến phép trừ hoặc thêm giây.

Vấn đề thường trở thành biến đổi ngày / giờ thành định dạng này.

Trong môi trường shell / script bạn có thể lấy nó date '+%s' tại thời điểm viết, thời điểm hiện tại là 1321358027.

Để so sánh với 2011-11-04 (sinh nhật của tôi) date '+%s' -d 2011-11-04, năng suất 1320361200. Trừ: expr 1321358027 - 1320361200cho 996827giây, tức là expr 996827 / 86400= 11 ngày trước.

Chuyển đổi từ utime (định dạng 1320361200) thành một ngày rất đơn giản để thực hiện trong ví dụ C, PHP hoặc perl, sử dụng các thư viện chuẩn. Với GNU date, -dđối số có thể được thêm vào @để chỉ ra định dạng "Giây kể từ thời đại".


7
Với ngày GNU, date -d @1234567890chuyển đổi từ giây kể từ kỷ nguyên sang bất kỳ định dạng ngày nào bạn chỉ định.
Gilles

1
@Gilles: thật là tuyệt vời. Không thể tìm thấy trên trang. Bạn học cái đó ở đâu?
MattBianco

1
@MattBianco, xem info date, đặc biệt là các giây kể từ nút Epoch : Tháng Nếu bạn đứng trước một số với `@ ', nó biểu thị một dấu thời gian nội bộ dưới dạng đếm giây.
Nghiêm

8

Nếu một công cụ đồ họa phù hợp với bạn, tôi chân thành khuyên bạn qalculate(một máy tính chú trọng vào chuyển đổi đơn vị, nó đi kèm với giao diện GTK và KDE, IIRC). Ở đó bạn có thể nói ví dụ

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

để có được số ngày (140, kể từ năm 1900 không phải là năm nhuận) giữa các ngày, nhưng tất nhiên bạn cũng có thể làm tương tự cho các lần:

17:12:45 − 08:45:12

mang lại 8.4591667giờ hoặc, nếu bạn đặt đầu ra thành định dạng thời gian , 8:27:33.


3
Điều đó thật tuyệt, và thậm chí còn hơn thế bởi vì qalculate không có CLI. Hãy thử qalc, sau đó help days.
Sparhawk

qcalcví dụ: qalc -t 'days(1900-05-21, 1900-01-01)'-> 140
sierrasdetandil

8

Điều này xuất hiện khi sử dụng date -d "$death_date - $y years - $m months - $d days"để có được ngày sinh (đối với phả hệ). Lệnh đó là SAI. Tháng không phải là tất cả cùng một chiều dài, vì vậy (date + offset) - offset != date. Lứa tuổi, tính theo năm / tháng / ngày, là các biện pháp chuyển tiếp kể từ ngày sinh.

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

Ngày cung cấp đầu ra chính xác trong cả hai trường hợp, nhưng trong trường hợp thứ hai, bạn đã hỏi sai câu hỏi. Vấn đề là 11 tháng trong năm, bao gồm +/- 11, trước khi cộng / trừ ngày. Ví dụ:

$ 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

Để trừ đi là hoạt động nghịch đảo của việc thêm, thứ tự các hoạt động sẽ phải được đảo ngược. Thêm năm, THEN tháng, THEN ngày. Nếu phép trừ được sử dụng theo thứ tự ngược lại, thì bạn sẽ quay trở lại điểm xuất phát của mình. Không, vì vậy bạn không, nếu ngày bù vượt qua ranh giới tháng trong một tháng có độ dài khác nhau.

Nếu bạn cần phải làm việc ngược từ ngày kết thúc và tuổi, bạn có thể làm điều đó với nhiều yêu cầu date. Đầu tiên trừ đi ngày, rồi tháng, rồi năm. (Tôi không nghĩ sẽ an toàn khi kết hợp năm và tháng trong một lần dategọi, vì năm nhuận thay đổi độ dài của tháng hai.)


3

Tôi thường xuyên sử dụng SQL để tính toán ngày. Ví dụ: MySQL , PostgreSQL hoặc 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

Những lần khác, tôi chỉ cảm thấy có tâm trạng với JavaScript. Ví dụ: SpiderMonkey , WebKit , Seed hoặc 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

(Coi chừng khi chuyển tháng cho hàm tạo Datecủa đối tượng JavaScript . Bắt đầu bằng 0.)


3

Một cách khác để tính chênh lệch giữa hai ngày trong cùng một năm dương lịch bạn có thể sử dụng:

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"
  • Dòng 1 khai báo shell mà trình thông dịch sẽ sử dụng.
  • Dòng 2 gán giá trị từ ngoài ra datecho biến DATEfirstnum. Các -dlá cờ sẽ hiển thị chuỗi trong một định dạng thời gian trong trường hợp này 14 tháng năm 2014 và +"%j"kể dateđể định dạng đầu ra để chỉ các ngày trong năm (1-365).
  • Dòng 3 giống như Dòng 2 nhưng có ngày khác và định dạng khác cho chuỗi, ngày 31 tháng 12 năm 2014.
  • Dòng 4 gán giá trị DAYSdifcho chênh lệch của hai ngày.
  • Dòng 5 hiển thị giá trị của DAYSdif.

Điều này hoạt động với phiên bản GNU của date, nhưng không phải trên phiên bản PC-BSD / FreeBSD. Tôi đã cài đặt coreutilstừ cây cổng và sử dụng lệnh /usr/local/bin/gdatethay thế.


1
Kịch bản này sẽ không chạy. Có một lỗi đánh máy trên dòng cuối cùng và khoảng trắng xung quanh các phép gán biến, vì vậy bash đang cố chạy một chương trình có tên DATEfirst với hai đối số. Hãy thử điều này: DATEfirstnum=$(date -d "$1" +%s) DATElastnum=$(date -d "$2" +%s) Ngoài ra, tập lệnh này sẽ không thể tính được sự khác biệt giữa hai năm khác nhau. +%jđề cập đến ngày trong năm (001..366) vì vậy ./date_difference.sh 12/31/2001 12/30/2014đầu ra -1. Như các câu trả lời khác đã lưu ý, bạn cần chuyển đổi cả hai ngày thành giây kể từ 1970-01-01 00:00:00 UTC.
Sáu

Bạn không cần $bên trong biểu thức số học: $((DATElastnum - DATEfirstnum))cũng sẽ hoạt động.
Ruslan

3

Với sự trợ giúp của các giải pháp dannas, điều này có thể được thực hiện trong một dòng với mã sau:

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

(Hoạt động trong cả Python 2.x và Python 3.)


1
Bạn có thể chỉnh sửa bài đăng của mình thay vì bình luận
Stephen Rauch

3

datevà bash có thể làm khác biệt ngày (tùy chọn OS X được hiển thị). Đặt ngày sau trước.

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

1

Ngoài ra còn có các tính toán thời gian của đơn vị GNU kết hợp với ngày 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 là đơn vị trong Linux, gdate là ngày)


1

dateiff.sh trên github: ý chính

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

Câu trả lời của camh quan tâm đến hầu hết câu hỏi, nhưng chúng tôi có thể cải thiện để xử lý làm tròn, múi giờ, v.v., cộng với chúng tôi có thêm một số độ chính xác và khả năng chọn đơn vị của chúng tôi:

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

-dnói daterằng chúng tôi đang cung cấp thời gian để chuyển đổi. +%s.%Nthay đổi thời gian ngày thành giây.nanoseconds kể từ 1970-01-01 00:00:00 UTC. bctính toán sự khác biệt giữa hai số và hệ số chia cho chúng ta các đơn vị khác nhau. printfthêm 0 trước vị trí thập phân nếu cần ( bckhông) và đảm bảo làm tròn đến gần nhất ( bcchỉ cắt ngắn). Bạn cũng có thể sử dụng awk.

Bây giờ chúng ta hãy chạy nó với một trường hợp thử nghiệm thú vị,

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

Lưu ý rằng vì độ dài của một tháng hoặc một năm không phải lúc nào cũng giống nhau, nên không có câu trả lời "đúng" nào ở đó, mặc dù hiện tại người ta có thể sử dụng 365,24 ngày như một xấp xỉ hợp lý.


0

Bạn có thể sử dụng thư viện Velk awk :

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

Hoặc là:

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

Kết quả:

7
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.