Como comparar duas datas em um shell?

74

Como duas datas podem ser comparadas em um shell?

Aqui está um exemplo de como eu gostaria de usar isso, embora não funcione como está:

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

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

Como posso alcançar o resultado desejado?

    
por Abdul 25.07.2013 / 09:43

11 respostas

84

A resposta certa ainda está faltando:

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

if [ $todate -ge $cond ];
then
    break
fi  
    
por 02.12.2014 / 08:42
26

Está faltando o formato de data para a comparação:

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

Você também está perdendo estruturas em loop, como você planeja obter mais datas?

    
por 25.07.2013 / 10:35
22

Pode usar uma comparação de string padrão para comparar a ordem cronológica [de strings em um formato de ano, mês e dia].

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

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

Felizmente, quando as [strings que usam o formato YYYY-MM-DD] são classificadas * em ordem alfabética, elas também são classificadas * em ordem cronológica.

(* - classificado ou comparado)

Nada sofisticado necessário neste caso. yay!

    
por 22.07.2014 / 01:16
7

Este não é um problema de estruturas em loop, mas de tipos de dados.

Essas datas ( todate e cond ) são strings, não números, portanto, você não pode usar o operador "-ge" de test . (Lembre-se que a notação de colchetes é equivalente ao comando test .)

O que você pode fazer é usar uma notação diferente para as datas, para que sejam números inteiros. Por exemplo:

date +%Y%m%d

produzirá um inteiro como 20130715 para 15 de julho de 2013. Depois, você poderá comparar suas datas com "-ge" e operadores equivalentes.

Atualização: se suas datas forem informadas (por exemplo, você as está lendo em um arquivo no formato 2013-07-13), você poderá pré-processá-las facilmente com tr.

$ echo "2013-07-15" | tr -d "-"
20130715
    
por 25.07.2013 / 10:58
4

O operador -ge só funciona com números inteiros, o que não é o caso das suas datas.

Se o seu script for bash ou ksh ou zsh, você poderá usar o operador < . Este operador não está disponível em traços ou outros shells que não vão muito além do padrão POSIX.

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

Em qualquer shell, você pode converter as strings em números, respeitando a ordem das datas simplesmente removendo os traços.

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

Como alternativa, você pode se tornar tradicional e usar o utilitário expr .

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

Como invocar subprocessos em um loop pode ser lento, você pode preferir fazer a transformação usando construções de processamento de string do 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

Claro, se você puder recuperar as datas sem os hífens, poderá compará-las com -ge .

    
por 26.07.2013 / 02:05
1

Eu converto os Strings em unix-timestamps (segundos desde 1.1.1970 0: 0: 0). Estes podem comparar facilmente

unix_todate=$(date -d "${todate}" "+%s")
unix_cond=$(date -d "${cond}" "+%s")
if [ ${unix_todate} -ge ${unix_cond} ]; then
   echo "over condition"
fi
    
por 03.02.2015 / 14:12
1

Existe também este método no artigo intitulado: Data simples e calculo de tempo no BASH do unix.com.

Estas funções são um trecho de um script nesse tópico!

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

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

Uso

# 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"
    
por 25.07.2013 / 16:11
0

datas são strings, não inteiros; você não pode compará-los com operadores aritméticos padrão.

uma abordagem que você pode usar, se o separador for garantido como - :

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

Isso funciona desde bash 2.05b.0(1)-release .

    
por 25.07.2013 / 09:52
0

Você também pode usar a função interna do mysql para comparar as datas. Dá o resultado em 'dias'.

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

Basta inserir os valores das variáveis $DBUSER e $DBPASSWORD de acordo com o seu.

    
por 10.03.2015 / 07:42
0

resposta específica

Como gosto de reduzir garfos e permitem muitas truques, existe o meu propósito:

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

Bem, agora:

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

Isso irá repopular as duas variáveis $todate e $cond , usando apenas uma bifurcação, com a exceção de date -f - , o que leva stdio para ler uma data por linha.

Finalmente, você pode quebrar o seu loop com

((todate>=cond))&&break

Ou como uma função :

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

Usando embutiu printf , o que poderia renderizar a data com segundos de epoch (veja man bash ; -)

Esse script usa apenas um fork.

    
por 13.02.2017 / 00:56
0

FWIW: no Mac, parece que alimentar apenas uma data como "2017-05-05" em data adicionará a hora atual à data e você obterá um valor diferente a cada vez que convertê-la em hora de época. para obter um horário de época mais consistente para datas fixas, inclua valores fictícios para horas, minutos e segundos:

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

Isso pode ser uma peculiaridade limitada à versão da data fornecida com o macOS .... testado no macOS 10.12.6.

    
por 02.08.2017 / 17:03