dom = dia do mês
dom=6 ; \
days=$[ ${dom}-$(date +%-d) ] ; \
[ ${days} -lt 0 ] && days=$[ ${days} + $(date +%d -d "$(date +%Y%m01 -d 'next month') yesterday") ] ; \
echo ${days} days
30 days
Eu preciso mostrar um número de dias para o próximo dia de pagamento (digamos que seja sempre no dia 10 de qualquer mês).
Como faço isso no bash?
Embora bash
agora tenha recursos de formatação de data, ele não tem análise de data ou cálculo, portanto, convém usar outro shell como ksh93
ou zsh
ou uma linguagem de programação adequada como perl
ou python
.
Com ksh93
, a parte difícil é descobrir que formato de data é suportado, pois é dificilmente documentado (você sempre pode dar uma olhada em os dados do teste embora para exemplos).
Por exemplo, ele suporta a especificação do horário do crontab e, em seguida, fornece a próxima vez que corresponde à especificação, para que você possa fazer:
now=$(printf '%(%s)T')
next_10th=$(printf '%(%s)T' '* * 10 * *')
echo "Pay day is in $(((next_10th - now) / 86400)) days"
Agora, com os utilitários padrão, não é tão difícil de implementar:
eval "$(date '+day=%d month=%m year=%Y')"
day=${day#0} month=${month#0}
if [ "$day" -le 10 ]; then
delta=$((10 - day))
else
case $month in
[13578]|10|12) D=31;;
2) D=$((28 + (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))));;
*) D=30;;
esac
delta=$((D - day + 10))
fi
echo "Pay day is in $delta days"
echo $(expr '(' $(date -d 2017/03/10 +%s) - $(date +%s) + 86399 ')' / 86400) "days for my payment"
3 days for my payment
Precisamos apenas subtrair o dia atual (hoje dia $td
) do PayDay esperado selecionado.
Se o PayDay for maior que o dia atual, o resultado será positivo e correto.
Por exemplo, td = 8 e pd = 15:
$ td=8; pd=15
$ echo "The next PayDay will be in $((pd-td)) days"
7
Se o resultado for negativo, basta adicionar o número de dias do mês atual.
O script para executar isso pode ser:
#!/bin/bash
pd=${1:-10} # Pay day selected
td=$( date -u +'%-d' ) # Today day of the month.
# To calculate the number of days in the present month.
MonthDays=$( date +'%-d' -ud "$(date +"%Y-%m-01T00:00:00UTC") next month last day" )
# Maybe a simpler alternative for current month last day:
# echo $(cal) | awk '{print $NF}' # $(cal) is unquoted on purpose.
# Make the next PayDay fit within the available days in the month.
# If the selected PayDay given to the script is 31 and the month
# only has 30 days, the next PayDay should be 30,
# not an un-existent and impossible 31.
pd=$(( (pd>MonthDays)?MonthDays:pd ))
res=$(( pd-td ))
# If the value of res is negative, just add the number of days in present month.
echo "Pay Day is in $(( res+=(res<0)?MonthDays:0 )) days"
Observe que o comando exclusivo date
precisa usar apenas o mês atual, portanto, nenhum limite de mês / ano foi ultrapassado. Isso evita quase todos os problemas. A única suposição é que o mês atual começa no dia 01
. Além disso, o cálculo é feito em UTC + 0, o que evita possíveis problemas com o horário de verão (DST) ou alterações locais.
Se o PayDay selecionado (Say 31) for maior que o número de dias possíveis no mês (Say February with 28), o programa assume que 28 é o PayDay em vez de um existente (para fevereiro) 31.
Chamando o roteiro (se hoje for o dia 9):
$ ./script 7
Pay Day is in 29 days
$ ./script 16
Pay Day is in 7 days
$ ./script 31
Pay Day is in 19 days
Mas se hoje for dia 28 de fevereiro:
$ ./script 8
Pay Day is in 8 days
$ ./script 28
Pay Day is in 0 days
$ ./script 31
Pay Day is in 0 days
Tags bash date shell-script