如何找到两个日期之间的天数差异?


82

A =“ 2002-20-10”
B =“ 2003-22-11”

如何找到两个日期之间的天数差异?


5
如下所述,但是您的约会日期有误:他们需要是yyyy-mm-dd
Peter Flynn

许多答案使用(GNU-)date的-d选项。我想补充一点,这不是POSIX日期的一部分,因此移植性较差。只要OP无法在像Solaris这样的Unix发行版上运行,而只能在“普通Linux”上运行,那么他(她)就应该很不错。相应的标签将有助于恕我直言。
卡多斯

Answers:


33

如果您有GNU date,它允许打印任意日期的表示形式(-d选项)。在这种情况下,将日期转换为自EPOCH以来的秒数,然后减去并除以24 * 3600。

还是您需要一种便携式方式?


1
@Tree:我相信没有一种简便的方法可以在您自己实现日期之前减去日期,这并不难,唯一有趣的是leap年。但是如今,每个人似乎都想减去日期:看看unix.stackexchange.com/questions/1825/… :)

14
为了lazyweb的缘故,我认为这是user332325的意思:echo "( `date -d $B +%s` - `date -d $A +%s`) / (24*3600)" | bc -l
Pablo Mendes

2
在这种情况下,可移植性是什么意思?
htellez

@htellez在多种平台(Windows,Linux等)上都可以使用的东西
Sumudu

对于基于此答案的bash一个衬板,请参见:stackoverflow.com/a/49728059/1485527
jschnasse

64

bash方式-将日期转换为%y%m%d格式,然后可以直接从命令行执行此操作:

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

9
这不一定需要日期采用%y%m%d格式。例如,gnu date将接受所%Y-%m-%d用OP的格式。通常,ISO-8601是一个不错的选择(OP的格式就是这样一种格式)。最好避免使用两位数年份的格式。
mc0e 2015年

2
对于与今天的区别,可能喜欢使用days_diff=$(( (`date -d $B +%s` - `date -d "00:00" +%s`) / (24*3600) ))。请注意,这$days_diff将是一个整数(即无小数)
朱利安

1
这取决于date安装的变体。它适用于GNU date。它不适用于POSIX date。对于大多数读者来说,这可能不是问题,但是值得注意。
mc0e '18

如果您确实想要POSIX可移植性,请检查以下内容: unix.stackexchange.com/a/7220/43835
mc0e,

1
只是为了治乱:OP的格式是没有 %Y-%m-%d,直到我们引进August 2.0October 2.0OP的格式%Y-%d-%m。相关xkcd:xkcd.com/1179
五彩纸屑

22

tl; dr

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

小心!这里的许多bash解决方案都打破了日期范围,该范围跨越了夏令时开始的日期(如果适用)。这是因为$((math))构造对结果值执行了“ floor” /截断运算,仅返回整数。让我说明一下:

DST从今年3月8日开始在美国开始,因此我们使用一个跨越以下日期范围的日期范围:

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

让我们看看使用双括号可以得到什么:

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

返回“ 5”。

使用'bc'更精确地执行此操作会给我们带来不同的结果:

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

返回“ 5.95”-缺少的0.05是DST切换的损失小时数。

那么应该如何正确完成呢?
我建议改用这个:

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

在这里,“ printf”舍入由“ bc”计算出的更准确的结果,从而为我们提供了正确的日期范围“ 6”。

编辑:在下面@ hank-schultz的评论中突出显示答案,我最近一直在使用它:

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

只要您总是从较晚的日期中减去较早的日期,这也应该是leap秒安全的,因为leap秒只会增加差异-截断有效地舍入为正确的结果。


6
相反,如果你指定的开始和结束两个时区(并确保它们是相同的),那么你也不再有这个问题,像这样: echo $(( ($(date --date="2015-03-11 UTC" +%s) - $(date --date="2015-03-05 UTC" +%s) )/(60*60*24) )),返回6,而不是5
汉克·舒尔茨

1
嗯,其中一个回答者想到了其他人在变体中重复的基本解决方案的不准确性。竖起大拇指!leap秒怎么样?leap秒安全吗?如果在特定时间运行,则有先例返回一天的结果的先例,这与with秒相关的问题有关
斯特凡纳·古里科

糟糕,您在示例中给出的“错误”计算正确地在此处返回6(Ubuntu 15.04 AMD64,GNU日期版本8.23)。也许您的示例取决于时区?我的时区是“欧洲/巴黎”。
斯特凡纳·古里科

在相同时区,在MSYS上使用GNU date 2.0进行“错误”计算的结果相同。
斯特凡纳·古里科

1
@StéphaneGourichon,您所在的时区的夏令时更改似乎发生在2015年3月29日-因此,请尝试使用包含该日期的日期范围。
evan_b 2015年

20

并在python中

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

2
@mouviciel并非如此。Python并不总是存在。
mc0e 2015年

1
在这种情况下,@ mc0e始终不存在
Sumudu

5
@Anubis OP将标签指定为bashshell,所以我认为我们可以假设我们正在谈论* nix系统。在这样的系统中,echo并且date确实存在,但是python没有。
mc0e '18

2
@ mc0e是的,这很有道理。尽管python2在大多数* nix系统中都可用,但是对于低端设备(嵌入式等),我们将只能使用主要工具生存。
Sumudu

11

这对我有用:

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

版画

398 days

怎么了?

  1. AB中提供有效的时间字符串
  2. 使用date -d处理时间字符串
  3. 使用date %s时间转换字符串秒自1970年以来(UNIX悬搁)
  4. 使用bash参数扩展减去秒
  5. 每天除以秒数(86400 = 60 * 60 * 24)得到的天数差
  6. !不考虑DST!在unix.stackexchange上查看此答案!

7

如果选项-d在您的系统中可用,则这是另一种方法。需要注意的是,自从我考虑过每年365天以来,这并不能说明leap年。

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

为方便起见,这是MAC OS X版本。

$ A="2002-20-10"; B="2003-22-11";
$ echo $(((`date -jf %Y-%d-%m $B +%s` - `date -jf %Y-%d-%m $A +%s`)/86400))

欢乐!


1
-bash:(-)/ 86400:语法错误:预期操作数(错误令牌为“)/ 86400”)
cavalcade'Nov

1
@cavalcade-那是因为您尚未完成A="2002-20-10"; B="2003-22-11"
RAM237 '17

谢谢,尽管它适用于这种特殊情况,但我还是建议使用echo $(((`date -jf "%Y-%d-%m" "$B" +%s` - `date -jf "%Y-%d-%m" "$A" +%s`)/86400)),即将格式和变量都包装在双引号中,否则可能在不同的日期格式(例如%b %d, %Y/ Jul 5, 2017)上失败
RAM237 '17

@ RAM237这就是我得到不重视headsmack嗯发现,谢谢
行列

5

即使您没有GNU日期,也可能会安装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";

398.041666666667由于夏令时,将返回398天零一个小时。


这个问题再次出现在我的提要上。这是使用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

这是我设法在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

这是我使用zsh的工作方法。在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

结果:

Your value:  1577

基本上,我们使用strftime反向(-r)功能将日期字符串转换回时间戳,然后进行计算。


1

我将在Ruby中提交另一个可能的解决方案。看起来到目前为止,它是最小,最干净的一种:

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

2
他正在寻找一种方法。
科琼斯

2
在外壳本身中没有可移植的方法来执行此操作。所有替代方案都使用特定的外部程序(即GNUdate或某些脚本语言),老实说,Ruby是进入此处的方法。该解决方案非常简短,并且不使用任何非标准库或其他依赖项。实际上,我认为安装Ruby的可能性比安装GNU日期的可能性高。
GreyCat 2012年

1

另一个Python版本:

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



1

在Unix上,您应该安装GNU日期。您无需偏离bash。这是考虑了几天的解决方案,只是为了展示步骤。它可以简化并扩展到完整日期。

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

echo $THEDAY 

1

假设一个月是一年的1/12:

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

假设我们手动将Oracle DB备份同步到第三磁盘。然后,我们要删除该磁盘上的旧备份。因此,这是一个小的bash脚本:

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

修改Dirs和保留期限(“ 30天”)以适合您的需求。


Oracle使用“ YYYY_MM_DD”之类的格式将备份集放入Flash Recovery Area中,因此我们删除了将其传递给“ date -d”的下划线
Alex Cherkas

0

对于MacOS sierra(可能来自Mac OS X yosemate),

要从文件获取纪元时间(1970年的秒数),然后将其保存到var中: old_dt=`date -j -r YOUR_FILE "+%s"`

获取当前时间的纪元时间 new_dt=`date -j "+%s"`

计算上述两个纪元时间的差 (( diff = new_dt - old_dt ))

检查差异是否超过23天 (( 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

用它来获取您今天的日期以及您想要的日期之后的秒数。如果您希望用几天,只需将其除以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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.