如何在外壳中比较两个日期?

这是我想如何使用它的一个示例,尽管它不能按原样工作:

todate=2013-07-18
cond=2013-07-15

if [ $todate -ge $cond ];
then
    break
fi                           


我怎样才能达到预期的效果?

评论

您想循环播放什么?您是说条件式而不是循环式吗?

为什么标记文件?这些日期实际上是文件时间吗?

在stackoverflow上查看此内容:stackoverflow.com/questions/8116503/…

#1 楼

正确的答案仍然缺失:

todate=$(date -d 2013-07-18 +%s)
cond=$(date -d 2014-08-19 +%s)

if [ $todate -ge $cond ];
then
    break
fi  


请注意,这需要GNU日期。 BSD date的等效date语法(如默认情况下在OSX中找到的)为date -j -f "%F" 2014-08-19 +"%s"

评论


Donno为什么有人不赞成这一点,它非常准确,并且不涉及串联丑陋的字符串。将您的日期转换为Unix时间戳(以秒为单位),比较Unix时间戳。

– Nicholi
2015年5月2日23:16

我认为,此解决方案的主要优点是,您可以轻松进行加法或减法。例如,我想有一个x秒的超时时间,但是我的第一个想法(超时= $(`日期+%Y%m%d%H%M%S` + 500))显然是错误的。

– iGEL
18年7月20日在9:59



您可以在日期-d 2013-07-18中加上毫微秒的精度+%s%N

–本征场
19年5月31日在9:43

#2 楼

可以使用标准的字符串比较来比较[以年,月,日格式的字符串的时间顺序]。

date_a=2013-07-18
date_b=2013-07-15

if [[ "$date_a" > "$date_b" ]] ;
then
    echo "break"
fi


,当[使用YYYY的字符串时-MM-DD格式]按字母顺序排序*,也按时间顺序排序*。

(*-排序或比较)

案件。耶!

评论


这里的else分支在哪里?

–弗拉基米尔·德斯波托维奇(Vladimir Despotovic)
16 Dec 15'在14:12

这是在Sierra MacOS上对我有用的唯一解决方案

–弗拉基米尔·德斯波托维奇(Vladimir Despotovic)
16 Dec 15'在14:22

这比我对if [$(sed -e s /-// g <<< $ todate)-ge $(sed -e s /-/// g <<< $ cond)]的解决方案更简单; …此日期格式旨在使用标准字母数字排序进行排序,或者用于-剥离和数字排序。

–ctrl-alt-delor
17年4月8日在17:59

这是这里最明智的答案

– Ed Randall
19年2月23日在11:34

使用此答案,由于它不依赖外部命令,因此它更加健壮。

–BitByteDog
20年6月13日在22:20

#3 楼

您缺少用于比较的日期格式:

#!/bin/bash

todate=$(date -d 2013-07-18 +"%Y%m%d")  # = 20130718
cond=$(date -d 2013-07-15 +"%Y%m%d")    # = 20130715

if [ $todate -ge $cond ]; #put the loop where you need it
then
 echo 'yes';
fi


您也缺少循环结构,您打算如何获得更多日期?

评论


这不适用于macOS附带的日期。

– Flimm
16 Dec 8'在9:49

date -d是非标准的,不适用于典型的UNIX。在BSD上,它甚至尝试设置龙骨值DST。

–schily
18/12/7在10:13

如果您在macOS上使用gdate,则上述方法有效,它可以在brew中使用。

–slm♦
20-2-11在7:37

#4 楼

这不是循环结构的问题,而是数据类型的问题。

那些日期(todatecond)是字符串,而不是数字,因此不能使用test的“ -ge”运算符。 (请记住,方括号表示法等效于命令test。)

您可以为日期使用不同的表示法,以便它们是整数。例如:

date +%Y%m%d


将在2013年7月15日产生一个像20130715的整数。然后,您可以将日期与“ -ge”和等效运算符进行比较。

更新:如果给出了日期(例如,您正在从2013-07-13格式的文件中读取日期),则可以使用tr轻松对其进行预处理。

$ echo "2013-07-15" | tr -d "-"
20130715


#5 楼

运算符-ge仅适用于整数,您的日期不是。

如果脚本是bash或ksh或zsh脚本,则可以使用<运算符。在破折号或其他不超过POSIX标准的shell中,此运算符不可用。

if [[ $cond < $todate ]]; then break; fi


在任何shell中,您都可以将字符串转换为数字,而只需删除破折号即可遵守日期顺序。

if [ "$(echo "$todate" | tr -d -)" -ge "$(echo "$cond" | tr -d -)" ]; then break; fi


或者,您可以改传统版本并使用expr实用程序。

if expr "$todate" ">=" "$cond" > /dev/null; then break; fi


由于在循环中调用子流程可能很慢,因此您可能更喜欢使用shell字符串处理结构进行转换。

todate_num=${todate%%-*}${todate#*-}; todate_num=${todate_num%%-*}${todate_num#*-}
cond_num=${cond%%-*}${cond#*-}; cond_num=${cond_num%%-*}${cond_num#*-}
if [ "$todate_num" -ge "$cond_num" ]; then break; fi


当然,如果您可以首先检索不带连字符的日期,则可以将其与-ge进行比较。

评论


这个bash中的else分支在哪里?

–弗拉基米尔·德斯波托维奇(Vladimir Despotovic)
16 Dec 15'在14:12

这似乎是最正确的答案。很有帮助!

– Mojtaba Rezaeian
18-10-21在0:50

#6 楼

我将字符串转换为unix时间戳(自1.1.1970 0:0:0以来的秒数)。
这些可以轻松比较

unix_todate=$(date -d "${todate}" "+%s")
unix_cond=$(date -d "${cond}" "+%s")
if [ ${unix_todate} -ge ${unix_cond} ]; then
   echo "over condition"
fi


评论


这不适用于macOS随附的日期。

– Flimm
16 Dec 8'在9:50

似乎与unix.stackexchange.com/a/170982/100397相同,该信息在您发布之前发布了一段时间

–roaima
17年4月8日在16:02

#7 楼

标题为文章的文章中也提供了这种方法:unix.com中BASH中的简单日期和时间计算。

这些功能摘自该线程中的脚本!

date2stamp () {
    date --utc --date "" +%s
}

dateDiff (){
    case  in
        -s)   sec=1;      shift;;
        -m)   sec=60;     shift;;
        -h)   sec=3600;   shift;;
        -d)   sec=86400;  shift;;
        *)    sec=86400;;
    esac
    dte1=$(date2stamp )
    dte2=$(date2stamp )
    diffSec=$((dte2-dte1))
    if ((diffSec < 0)); then abs=-1; else abs=1; fi
    echo $((diffSec/sec*abs))
}


用法

# calculate the number of days between 2 dates
    # -s in sec. | -m in min. | -h in hours  | -d in days (default)
    dateDiff -s "2006-10-01" "2006-10-31"
    dateDiff -m "2006-10-01" "2006-10-31"
    dateDiff -h "2006-10-01" "2006-10-31"
    dateDiff -d "2006-10-01" "2006-10-31"
    dateDiff  "2006-10-01" "2006-10-31"


评论


这不适用于macOS随附的日期。

– Flimm
16 Dec 8'在9:51

如果您通过brew在MacOS上安装gdate,则可以通过将date更改为gdate来轻松地修改其工作方式。

–slm♦
18-10-8在20:40

这个答案对我的案子很有帮助。它应该评分!

– Mojtaba Rezaeian
18-10-21在0:40

#8 楼


bash的具体答案

由于我喜欢减少叉子,bash确实允许很多技巧,所以我的目的是:

todate=2013-07-18
cond=2013-07-15


那么,现在:

{ read todate; read cond ;} < <(date -f - +%s <<<"$todate"$'\n'"$cond")


这将仅使用一个fork重新填充两个变量$todate$cond,其中date -f -的输出将以stdio读取一个日期

最后,您可以使用

((todate>=cond))&&break


或作为函数:

myfunc() {
    local todate cond
    { read todate
      read cond
    } < <(
      date -f - +%s <<<""$'\n'""
    )
    ((todate>=cond))&&return
    printf "%(%a %d %b %Y)T older than %(%a %d %b %Y)T...\n" $todate $cond
}
中断循环

使用bash内置的printf wich可以从日期开始以秒为单位呈现日期时间(请参阅man bash ;-)

此脚本仅使用一个fork。

具有有限的分叉和日期读取器功能的替代方法

这将创建一个专用的子进程(仅一个分叉):

mkfifo /tmp/fifo
exec 99> >(exec stdbuf -i 0 -o 0 date -f - +%s >/tmp/fifo 2>&1)
exec 98</tmp/fifo
rm /tmp/fifo


由于输入和输出是打开,可以删除fifo条目。

功能:

myDate() {
    local var="${@:$#}"
    shift
    echo >&99 "${@:1:$#-1}"
    read -t .01 -u 98 $var
}


Nota为了防止像todate=$(myDate 2013-07-18)这样的无用叉子,该变量由功能本身设置。为了允许自由语法(日期字符串带引号或不带引号),变量名称必须为最后一个参数。

然后进行日期比较:

myDate 2013-07-18        todate
myDate Mon Jul 15 2013   cond
(( todate >= cond )) && {
    printf "To: %(%c)T > Cond: %(%c)T\n" $todate $cond
    break
}


可以渲染:

To: Thu Jul 18 00:00:00 2013 > Cond: Mon Jul 15 00:00:00 2013
bash: break: only meaningful in a `for', `while', or `until' loop


如果在循环之外。

或使用shell-connector bash函数:

wget https://github.com/F-Hauri/Connector-bash/raw/master/shell_connector.bash




wget https://f-hauri.ch/vrac/shell_connector.sh


(并非完全相同:.sh包含完整的测试脚本,如果未提供)

source shell_connector.sh
newConnector /bin/date '-f - +%s' @0 0

myDate 2013-07-18        todate
myDate "Mon Jul 15 2013"   cond
(( todate >= cond )) && {
    printf "To: %(%c)T > Cond: %(%c)T\n" $todate $cond
    break
}


评论


分析bash很好地演示了如何使用date -f-可以减少资源需求。

– F. Hauri
17年2月13日在0:02



#9 楼

日期是字符串,不是整数;您不能将它们与标准算术运算符进行比较。

如果保证分隔符为-,则可以使用一种方法:

IFS=-
read -ra todate <<<"$todate"
read -ra cond <<<"$cond"
for ((idx=0;idx<=numfields;idx++)); do
  (( todate[idx] > cond[idx] )) && break
done
unset IFS


此功能可以追溯到bash 2.05b.0(1)-release

#10 楼

您还可以使用mysql的内置函数比较日期。结果以天为单位。

from_date="2015-01-02"
date_today="2015-03-10"
diff=$(mysql -u${DBUSER} -p${DBPASSWORD} -N -e"SELECT DATEDIFF('${date_today}','${from_date}');")


只需根据您的变量插入$DBUSER$DBPASSWORD的值。

#11 楼

FWIW:在Mac上,似乎只将“ 2017-05-05”之类的日期输入到日期中,会将当前时间附加到该日期上,并且每次将其转换为纪元时间时,都会得到不同的值。为了获得固定日期的更一致的纪元时间,包括小时,分钟和秒的虚拟值:

date -j -f "%Y-%m-%d %H:%M:%S" "2017-05-05 00:00:00" +"%s"


这可能是奇怪的,仅限于日期版本已随macOS一起提供。...已在macOS 10.12.6上进行了测试。

#12 楼

回显@Gilles答案(“ -ge运算符仅适用于整数”),这是一个示例。

curr_date=$(date +'%Y-%m-%d')
echo "$curr_date"
2019-06-20

old_date="2019-06-19"
echo "$old_date"
2019-06-19


datetime整数转换为epoch,根据@ mike-q的在https://stackoverflow.com/questions/10990949/convert-date-time-string-to-epoch-in-bash中回答:

curr_date_int=$(date -d "${curr_date}" +"%s")
echo "$curr_date_int"
1561014000

old_date_int=$(date -d "${old_date}" +"%s")
echo "$old_date_int"
1560927600


"$curr_date"更大大于"$old_date",但此表达式的计算错误为False

if [[ "$curr_date" -ge "$old_date" ]]; then echo 'foo'; fi


...在此处明确显示:

if [[ "$curr_date" -ge "$old_date" ]]; then echo 'foo'; else echo 'bar'; fi
bar


整数比较:

if [[ "$curr_date_int" -ge "$old_date_int" ]]; then echo 'foo'; fi
foo

if [[ "$curr_date_int" -gt "$old_date_int" ]]; then echo 'foo'; fi
foo

if [[ "$curr_date_int" -lt "$old_date_int" ]]; then echo 'foo'; fi

if [[ "$curr_date_int" -lt "$old_date_int" ]]; then echo 'foo'; else echo 'bar'; fi
bar


#13 楼

该比较不适用于带连字符的日期字符串的原因是shell假定前导0的数字是八进制,在这种情况下为月份“ 07”。已经提出了各种解决方案,但是最快和最简单的方法是删除连字符。 Bash具有字符串替换功能,可以快速简便地进行比较,然后可以将其作为算术表达式执行:

todate=2013-07-18
cond=2013-07-15

if (( ${todate//-/} > ${cond//-/} ));
then
    echo larger
fi


#14 楼

到目前为止,似乎所有或大多数答案都假设您可以指定日期格式(该问题未明确指定一种或另一种方式),或者假定您具有日期命令等功能丰富的版本。 br />有时候,您无法控制日期的格式(例如SSL证书的失效日期,其中日期以月份名称缩写表示),并且您无权访问更高级的date命令。因此,尽管此解决方案并不完全通用,但它确实可以在Linux,Solaris和FreeBSD上的香草bash外壳上运行,并且可以处理月份名称(从https://stackoverflow.com/questions/15252383/ Unix将月份名称转换为数字):
#!/usr/bin/bash

function monthnumber {
    month=$(echo ${1:0:3} | tr '[a-z]' '[A-Z]')
    MONTHS="JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"
    tmp=${MONTHS%%$month*}
    month=${#tmp}
    monthnumber=$((month/3+1))
    printf "%02d\n" $monthnumber
}

# Or at the expense of some flexibility and brevity, you get more readability:
function monthnumber2 {
    case $(echo ${1:0:3} | tr '[a-z]' '[A-Z]') in
        JAN) monthnumber="01" ;;
        FEB) monthnumber="02" ;;
        MAR) monthnumber="03" ;;
        APR) monthnumber="04" ;;
        MAY) monthnumber="05" ;;
        JUN) monthnumber="06" ;;
        JUL) monthnumber="07" ;;
        AUG) monthnumber="08" ;;
        SEP) monthnumber="09" ;;
        OCT) monthnumber="10" ;;
        NOV) monthnumber="11" ;;
        DEC) monthnumber="12" ;;
    esac
    printf "%02d\n" $monthnumber
}

TODAY=$( date "+%Y%m%d" )

echo "GET /" | openssl s_client -connect github.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > tmp.pem
cert_expiry_date=$(openssl x509 -in tmp.pem -noout -enddate | cut -d'=' -f2)
month_name=$(echo $cert_expiry_date | cut -d' ' -f1)
month_number=$( monthnumber $month_name )
cert_expiration_datestamp=$( echo $cert_expiry_date | awk "{printf \"%d%02d%02d\",$4,\"${month_number}\",$2}" )

echo "compare: [ $cert_expiration_datestamp -gt $TODAY ]"
if [ $cert_expiration_datestamp -gt $TODAY ] ; then
    echo "all ok, the cert expiration date is in the future."
else
    echo "WARNING: cert expiration date is in the past."
fi