Làm thế nào để tìm thấy sự khác biệt về ngày giữa hai ngày?


82

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

Làm thế nào để tìm thấy sự khác biệt về ngày giữa hai ngày?


5
Như đã giải thích dưới đây, nhưng ngày của bạn là tròn một cách sai lầm: họ cần phải yyyy-mm-dd
Peter Flynn

Nhiều câu trả lời sử dụng tùy chọn -d của (GNU-) ngày. Tôi muốn nói thêm rằng đây KHÔNG phải là một phần của ngày POSIX, do đó ít di động hơn. Miễn là OP không hoạt động trên các bản phân phối Unix như Solaris, mà chỉ sử dụng "Linux thông thường", thì người đó sẽ tốt. Một thẻ tương ứng sẽ giúp imho.
Cadoiz

Câu trả lời:


33

Nếu bạn có GNU date, nó cho phép in biểu diễn của một ngày tùy ý ( -dtùy chọn). Trong trường hợp này, hãy chuyển đổi ngày thành giây kể từ EPOCH, trừ và chia cho 24 * 3600.

Hoặc bạn cần một cách di động?


1
@Tree: Tôi tin rằng không có cách nào dễ dàng để trừ ngày thiếu khi tự thực hiện nó - nó không khó lắm, điều quan tâm duy nhất là các năm nhuận. Nhưng ngày nay mọi người đều muốn trừ ngày tháng, vì có vẻ như: Hãy xem unix.stackexchange.com/questions/1825/… :)

14
Vì lợi ích của lazyweb, tôi nghĩ đây là ý của người dùng332325:echo "( `date -d $B +%s` - `date -d $A +%s`) / (24*3600)" | bc -l
Pablo Mendes

2
Portable nghĩa là gì trong bối cảnh này?
htellez

@htellez thứ gì đó hoạt động trên nhiều nền tảng (windows, linux, v.v.)
Sumudu

Đối với bash lót dựa trên câu trả lời thấy điều này: stackoverflow.com/a/49728059/1485527
jschnasse

64

Cách bash - chuyển đổi ngày tháng sang định dạng% y% m% d và sau đó bạn có thể thực hiện việc này ngay từ dòng lệnh:

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

9
Điều này không nhất thiết phải %y%m%dđịnh dạng ngày tháng . Ví dụ: gnu date sẽ chấp nhận %Y-%m-%dđịnh dạng OP đã sử dụng. Nói chung, ISO-8601 là một lựa chọn tốt (và định dạng của OP là một trong những định dạng như vậy). Tốt hơn nên tránh các định dạng có năm 2 chữ số.
mc0e

2
Đối với sự khác biệt từ hôm nay , một người có thể thích sử dụng days_diff=$(( (`date -d $B +%s` - `date -d "00:00" +%s`) / (24*3600) )). lưu ý rằng $days_diffsẽ là một số nguyên (tức là không có số thập phân)
Julien

1
Điều này phụ thuộc vào biến thể của datecài đặt. Nó hoạt động cho GNU date. Nó không hoạt động với POSIX date. Đây có lẽ không phải là vấn đề đối với hầu hết người đọc, nhưng nó đáng lưu ý.
mc0e

Nếu bạn thực sự muốn tính di động của POSIX, hãy kiểm tra điều này: unix.stackexchange.com/a/7220/43835
mc0e

1
Chỉ để chống nhầm lẫn: Định dạng của OP thì không %Y-%m-%d , cho đến khi chúng tôi giới thiệu August 2.0October 2.0định dạng của OP là %Y-%d-%m. Liên quan xkcd: xkcd.com/1179
confetti

22

tl; dr

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

Coi chừng! Nhiều giải pháp cơ bản ở đây bị hỏng cho các phạm vi ngày kéo dài ngày bắt đầu tiết kiệm ánh sáng ban ngày (nếu có). Điều này là do cấu trúc $ ((math)) thực hiện phép toán 'tầng' / cắt ngắn trên giá trị kết quả, chỉ trả về toàn bộ số. Hãy để tôi minh họa:

DST bắt đầu vào ngày 8 tháng 3 năm nay ở Hoa Kỳ, vì vậy hãy sử dụng phạm vi ngày bao gồm:

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

Hãy xem những gì chúng ta nhận được với dấu ngoặc kép:

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

Trả về '5'.

Thực hiện việc này bằng cách sử dụng 'bc' với độ chính xác cao hơn cho chúng ta một kết quả khác:

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

Trả về '5,95' - 0,05 còn thiếu là giờ bị mất từ ​​trình chuyển đổi DST.

Vậy điều này phải được thực hiện như thế nào cho đúng?
Tôi sẽ đề nghị sử dụng cái này thay thế:

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

Ở đây, 'printf' làm tròn kết quả chính xác hơn được tính bằng 'bc', cho chúng ta phạm vi ngày chính xác là '6'.

Chỉnh sửa: đánh dấu câu trả lời trong nhận xét từ @ hank-schultz bên dưới, mà tôi đã sử dụng gần đây:

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

Điều này cũng sẽ an toàn cho bước nhảy thứ hai miễn là bạn luôn trừ ngày trước đó cho ngày muộn hơn, vì số giây nhuận sẽ chỉ thêm vào sự khác biệt - việc cắt bớt có hiệu quả làm tròn xuống kết quả chính xác.


6
Nếu thay vào đó, bạn chỉ định múi giờ cho cả phần bắt đầu và phần kết thúc (và đảm bảo rằng chúng giống nhau), thì bạn cũng không còn gặp vấn đề này nữa, như sau echo $(( ($(date --date="2015-03-11 UTC" +%s) - $(date --date="2015-03-05 UTC" +%s) )/(60*60*24) )):, trả về 6, thay vì 5.
Hank Schultz

1
À, một trong những người trả lời đã nghĩ về sự không chính xác của giải pháp cơ bản được những người khác lặp lại trong các biến thể. Thích! Làm thế nào về giây nhuận? Nó là nhảy vọt-giây-an toàn? Đã có tiền lệ phần mềm trả về kết quả sau một ngày nếu chạy tại một số thời điểm nhất định, các vấn đề về ref liên quan đến giây nhảy vọt .
Stéphane Gourichon

Rất tiếc, phép tính "sai" mà bạn đưa ra như ví dụ trả về chính xác 6 ở đây (Ubuntu 15.04 AMD64, phiên bản ngày GNU 8.23). Có lẽ ví dụ của bạn là phụ thuộc múi giờ? Múi giờ của tôi là "Châu Âu / Paris".
Stéphane Gourichon

Kết quả tương tự cho phép tính "sai" với GNU date 2.0 trên MSYS, cùng múi giờ.
Stéphane Gourichon

1
@ StéphaneGourichon, thay đổi tiết kiệm ánh sáng ban ngày của múi giờ của bạn dường như đã xảy ra vào ngày 29 tháng 3 năm 2015 - vì vậy hãy thử một phạm vi ngày bao gồm điều đó.
evan_b

20

Và trong trăn

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

2
@mouviciel không thực sự. Python không phải lúc nào cũng có mặt.
mc0e

1
@ mc0e trong bối cảnh đó, không có gì luôn hiện hữu
Sumudu

5
@Anub là các thẻ OP chỉ định là bashshell, vì vậy tôi nghĩ chúng ta có thể giả định rằng chúng ta đang nói về hệ thống * nix. Trong các hệ thống như vậy echodatecó mặt đáng tin cậy, nhưng python thì không.
mc0e

2
@ mc0e vâng, có lý. Mặc dù python2 có sẵn trong hầu hết các hệ thống * nix, nhưng khi nói đến các thiết bị cấp thấp (nhúng, v.v.), chúng tôi sẽ chỉ tồn tại với các công cụ chính.
Sumudu

11

Điều này phù hợp với tôi:

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

Bản in

398 days

Điều gì đang xảy ra?

  1. Cung cấp chuỗi thời gian hợp lệ ở AB
  2. Sử dụng date -d để xử lý các chuỗi thời gian
  3. Sử dụng date %sđể chuyển đổi chuỗi thời gian thành giây kể từ năm 1970 (unix epoche)
  4. Sử dụng mở rộng tham số bash để trừ giây
  5. chia cho số giây mỗi ngày (86400 = 60 * 60 * 24) để nhận chênh lệch theo ngày
  6. ! DST không được tính đến! Xem câu trả lời này tại unix.stackexchange !

7

Nếu tùy chọn -d hoạt động trong hệ thống của bạn, đây là một cách khác để thực hiện. Có một cảnh báo rằng nó sẽ không tính đến những năm nhuận vì tôi đã coi 365 ngày mỗi năm.

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

Đây là phiên bản MAC OS X để bạn tiện theo dõi.

$ 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: lỗi cú pháp: toán hạng mong đợi (mã thông báo lỗi là ") / 86400")
cavalcade

1
@cavalcade - đó là bởi vì bạn đã không thực hiệnA="2002-20-10"; B="2003-22-11"
RAM237

Cảm ơn, mặc dù nó hoạt động đối với trường hợp đặc biệt này, tôi sẽ đề nghị sử dụng echo $(((`date -jf "%Y-%d-%m" "$B" +%s` - `date -jf "%Y-%d-%m" "$A" +%s`)/86400)), tức là gói cả hai định dạng và biến thành dấu ngoặc kép, nếu không có thể thất bại trên định dạng ngày tháng khác nhau (ví dụ %b %d, %Y/ Jul 5, 2017)
RAM237

@ RAM237 Thats những gì tôi get cho không chú ý headsmack Vâng phát hiện, nhờ
cuộc biểu diển

5

Ngay cả khi bạn không có ngày GNU, có thể bạn sẽ cài đặt 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";

Điều này trả về 398.041666666667- 398 ngày và một giờ do tiết kiệm ánh sáng ban ngày.


Câu hỏi đã xuất hiện trở lại trên nguồn cấp dữ liệu của tôi. Đây là một phương pháp ngắn gọn hơn bằng cách sử dụng một mô-đun đi kèm Perl

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

Đây là cách đơn giản nhất mà tôi quản lý để làm việc trên centos 7:

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

Đây là cách tiếp cận làm việc của tôi bằng cách sử dụng zsh. Đã thử nghiệm trên 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

Kết quả:

Your value:  1577

Về cơ bản, chúng tôi sử dụng tính năng strftimereverse ( -r) để chuyển đổi chuỗi ngày của chúng tôi trở lại dấu thời gian, sau đó chúng tôi thực hiện tính toán của mình.


1

Tôi sẽ gửi một giải pháp khả thi khác trong Ruby. Có vẻ như đây là chiếc nhỏ nhất và trông sạch sẽ nhất cho đến nay:

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

2
Anh ta đang tìm một cách để bash.
Cojones

2
Không có cách di động nào để làm điều đó trong chính shell. Tất cả các lựa chọn thay thế đều sử dụng các chương trình bên ngoài cụ thể (tức là GNU datehoặc một số ngôn ngữ kịch bản) và tôi thành thật nghĩ rằng Ruby là một cách tốt để đi đến đây. Giải pháp này rất ngắn gọn và không sử dụng bất kỳ thư viện không chuẩn hoặc các phần phụ thuộc khác. Trên thực tế, tôi nghĩ rằng có khả năng Ruby được cài đặt cao hơn so với việc cài đặt ngày GNU.
GreyCat

1

Một phiên bản Python khác:

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



1

trên unix, bạn nên cài đặt ngày GNU. bạn không cần phải đi lệch khỏi bash. đây là giải pháp được rút ra theo ngày, chỉ để hiển thị các bước. nó có thể được đơn giản hóa và mở rộng đến ngày tháng đầy đủ.

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

echo $THEDAY 

1

Điều này giả định rằng một tháng là 1/12 của một năm:

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

Giả sử chúng tôi rsync các bản sao lưu Oracle DB vào đĩa cấp ba theo cách thủ công. Sau đó, chúng tôi muốn xóa các bản sao lưu cũ trên đĩa đó. Vì vậy, đây là một tập lệnh bash nhỏ:

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

Sửa đổi lịch và thời gian lưu giữ ("30 ngày") để phù hợp với nhu cầu của bạn.


Puts Oracle backup sets trong Flash vùng khôi phục, sử dụng định dạng như 'YYYY_MM_DD' vì vậy chúng tôi xóa các dấu gạch dưới cho đi qua nó để 'ngày -d'
Alex Cherkas

0

Đối với MacOS sierra (có thể từ Mac OS X yosemate),

Để nhận thời gian kỷ nguyên (Giây từ năm 1970) từ tệp và lưu vào tệp var: old_dt=`date -j -r YOUR_FILE "+%s"`

Để nhận thời gian của thời gian hiện tại new_dt=`date -j "+%s"`

Để tính toán chênh lệch của hai thời gian trên (( diff = new_dt - old_dt ))

Để kiểm tra xem chênh lệch quá 23 ngày (( 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

sử dụng nó để lấy ngày hôm nay và giây của bạn kể từ ngày bạn muốn. nếu bạn muốn nó với ngày chỉ cần chia nó với 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
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.