With bash version 4.3, the builtin printf
can format a datetime. Still need GNU date to parse a timestamp though. Here, I'm using %j
to get the day of the year: that makes it easy to get the days remaining.
days_remaining() {
local day month year doy renew_day=8
read day month year doy < <(printf '%(%-d %-m %Y %j)T\n' -1)
if ((day >= renew_day)) && ((++month > 12)); then
month=1
((year++))
fi
echo $(( $(date -d "$year-$month-$renew_day" '+%j') - doy ))
}
echo "There are $(days_remaining) days until next renewal"
Today, the 16th of November, it's 22 days remaining. If today is November 8, it will output 30 days.
Here's another implementation that does not require any external tools:
days_remaining() {
local timestamp days renew_day=8
for ((
timestamp=$(printf '%(%s)T' -1), days = 0;
$(printf '%(%-d)T' $timestamp) != renew_day;
days++, timestamp += 86400
)); do :; done
echo $days
}
If today is November 8, it will output 0 days.
And the days since the last renewal:
days_since() {
local timestamp days renew_day=8
for ((
timestamp=$(printf '%(%s)T' -1) - 86400, days=1;
$(printf '%(%-d)T' $timestamp) != renew_day;
days++, timestamp -= 86400
)); do :; done
echo $days
}
or combine them:
dom() { printf '%(%-d)T' "$1"; }
days() {
local now=$(printf '%(%s)T' -1)
local renew_day=8
local used=1 left=0 ts
for ((ts = now - 86400; $(dom $ts) != renew_day; used++, ts -= 86400)); do :; done
for ((ts = now; $(dom $ts) != renew_day; left++, ts += 86400)); do :; done
echo "$used $left"
}
Today, that function outputs 8 22
If the renew_day is 16, it will output 31 0