快速计算日期差异


84

我经常想进行一些快速的日期计算,例如:

  • 这两个日期有什么区别?
  • 这个日期之后的n周是几号?

我通常打开日历并计算天数,但是我认为应该有一个程序/脚本可以用来进行这些类型的计算。有什么建议么?


3
另请参见UNIX中的工具,以在GNU日期不可用时减去日期
吉尔斯

Answers:


106

对于GNU date(1),“日期后的n周”很容易:

$ 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

我不知道一种简单的方法来计算两个日期之间的差,但是您可以使用shell函数在date(1)周围包裹一些逻辑。

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

交换一下d1d2如果您想以其他方式进行日期计算,或者有点幻想,那就不要紧了。此外,如果在此间隔中有从非DST到DST的过渡,则其中一天只有23小时长;您可以通过加总½天来补偿。

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

40

对于一组便携式工具,请尝试我自己的dateutils。您的两个例子可以归结为一类:

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

或数周和数天:

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

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

3
为您的工具+1 +1(尽管dateadd -i '%m%d%Y' 01012015 +1d似乎没有用,它只是无限期地挂在那里...如果日期规范由一个字符,任何一个字符分隔……任何想法出了什么问题,它都可以工作)
don_crissti

2
@don_crissti解析器无法区分仅数字的日期和持续时间,已在当前主文档(d0008f98)中进行了固定
-hroptatyr

您是否有窗口的dateutils二进制分配?
mosh

@mosh不,我没有尝试的设施。
hroptatyr

在这里晚了聚会,但是如果您需要在Windows上使用,则dateutils在cygwin上。
gregb212

30

一个用于计算我在地球上行走的天数的python示例:

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

2
以防万一有人希望它的行为就像单个命令一样,而不是键入交互式解释器: ychaouche@ychaouche-PC ~ $ python -c "from datetime import date as d; print (d.today() - d(1980, 6, 14)).days" 12813 ychaouche@ychaouche-PC ~ $
ychaouche15年

1
这对我有用python3 -c“从datetime导入日期起d; print(d.today()-d(2016,1,9))”不需要最后天
Kiran K Telukunta

13

我通常更喜欢将时间/日期设置为unix utime格式(自纪元开始,以七十年代开始的秒数,UTC)。这样,它总是归结为简单的减法或秒的加法。

问题通常变成将日期/时间转换为这种格式。

在shell环境/脚本中,您可以使用。date '+%s' 在撰写本文时,当前时间是1321358027

与2011-11-04(我的生日)比较date '+%s' -d 2011-11-04,产生1320361200。减:expr 1321358027 - 1320361200给出996827秒,即expr 996827 / 86400= 11天前。

从utime(1320361200格式)到日期的转换非常容易,例如使用标准库在C,PHP或perl中进行。使用GNU时date-d可以在参数前面加上@“自大纪元以来的秒数”格式。


7
使用GNU日期,date -d @1234567890从纪元以来的秒数转换为您指定的任何日期格式。
吉尔斯

1
@吉尔斯:太好了。在联机帮助页上找不到。你从哪里学到的?
MattBianco

1
@MattBianco,请参见info date,尤其是自大纪元节点以来秒数:“如果在数字前加上'@',则表示内部时间戳(以秒为单位)。”
manatwork

8

这是在date -d "$death_date - $y years - $m months - $d days"用于获取出生日期(用于家谱)时出现的。该命令是错误的。月的长度不尽相同,因此(date + offset) - offset != date。年龄(以年/月/日为单位)是指从出生日期算起的指标。

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

在两种情况下,Date都能给出正确的输出,但是在第二种情况下,您输入的是错误的问题。重要的是一年中的11个月(+/- 11)的封面,然后加上/减去天数。例如:

$ 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

为了使减法成为加法的逆运算,必须颠倒运算的顺序。加数会增加年数,然后增加数月,然后增加数天。如果减法使用相反的顺序,那么您将回到起点。如果天数偏移跨过另一个长度月份中的月份边界,则不会,所以您不会。

如果您需要从结束日期和年龄开始倒退,可以通过多次调用来完成date。首先减去日期,然后减去月份,然后减去年份。(由于think date年改变了2月的长度,我认为一次调用中将年和月相结合是不安全的。)


7

如果图形工具适合您,我衷心推荐qalculate(一个强调单位转换的计算器,它带有GTK和KDE接口IIRC)。在那里你可以说例如

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

获取日期之间的天数(140,因为1900年不是a年),但是当然您也可以对时间进行相同的操作:

17:12:45 − 08:45:12

产生8.4591667小时,或者,如果将输出设置为时间格式,则产生8:27:33


3
很好,甚至更多,因为qalculate 确实具有CLI。qalc然后尝试help days
Sparhawk

qcalc例如:qalc -t 'days(1900-05-21, 1900-01-01)'-> 140
sierrasdetandil

3

我经常使用SQL进行日期计算。例如MySQLPostgreSQLSQLite

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

有时候我只是对JavaScript有心情。例如SpiderMonkeyWebKitSeedNode.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

(将月份传递给JavaScript Date对象的构造函数时要小心。从0开始。)


3

另一种计算同一日历年的两个日期之间的差异的方法是:

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"
  • 第1行向Shell声明要使用的解释器。
  • 第2 date行将值out分配给变量DATEfirstnum。该-d标志在这种情况下以2014年5月14日的时间格式显示字符串,并+"%j"告知date将输出格式设置为仅一年中的某一天(1-365)。
  • 第3行与第2行相同,但字符串的日期和格式不同(2014年12月31日)。
  • 第4行将值分配给DAYSdif两天的差额。
  • 第5行显示的值DAYSdif

这适用于的GNU版本date,但不适用于PC-BSD / FreeBSD版本。我coreutils从端口树安装,并改用命令/usr/local/bin/gdate


1
该脚本将无法运行。最后一行有一个错字,变量赋值周围有空格,因此bash试图运行一个带有两个参数的名为DATEfirst name的程序。尝试以下操作: DATEfirstnum=$(date -d "$1" +%s) DATElastnum=$(date -d "$2" +%s) 此外,此脚本将无法计算两个不同年份之间的差额。+%j指的是一年中的某一天(001..366),因此./date_difference.sh 12/31/2001 12/30/2014输出-1。正如其他答案所指出的那样,您需要将两个日期都转换为自1970-01-01 00:00:00 UTC以来的秒数。

您不需要$内部算术表达式:$((DATElastnum - DATEfirstnum))也可以。
Ruslan


3

date和bash可以进行日期差异(显示OS X选项)。将后一个日期放在第一位。

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

1

还有GNU单位的时间计算与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是Linux中的单位,gdate是日期)


1

github:gist上的datediff.sh

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

camh的答案可以解决大部分问题,但是我们可以进行改进以处理舍入,时区等问题,此外,我们还可以提供一些额外的精度以及选择单位的能力:

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

-d告诉date我们我们提供了要转换的日期时间。 +%s.%N将日期时间更改为seconds.nanoseconds自1970-01-01 00:00:00 UTC。 bc计算两个数字之间的差,除法因子得到不同的单位。 printf如果需要,则在小数点前添加0(bc不需要),并确保舍入到最接近的值(bc仅截断)。您也可以使用awk

现在,我们用一个时髦的测试用例来运行它,

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

请注意,由于一个月或一年的长度并不总是相同的,所以那里没有一个“正确”的答案,尽管现在可以使用365.24天作为合理的近似值。


0

您可以使用awk Velor库

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

要么:

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

结果:

7
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.