Если у вас есть дата Gnu, вы можете использовать следующую функцию:
next_day_of_month () {
date -d "${1:0:4}-01-01 $((10#${1:4:3})) days" +%-d
}
, которая принимает юлианскую дату в указанном вами формате и вычисляет день месяца следующего дня.
Если вам нужно чистое bash решение (которое, несмотря на то, что оно не вызывает никаких внешних утилит, серьезно медленное), вы можете использовать следующее.
Существует не очень сложная формула, которая может быть использована для вычисления месяца, соответствующего номеру дня, но требует подсчета дней, начиная с предыдущего 1 марта, а не с 1 января. Корректировка номера дня таким образом требует знания того, является ли год високосный год или нет, что делает все немного сложнее.
В любом случае, вот оно. [Примечание 1]
# Compute the month number (1-12) corresponding to a day in
# in "Julian" format YYYYJJJ (001 == Jan. 1; 365/6 == Dec. 31)
# Yes, I know it's full of magic numbers.
month_for_julian_day () {
local -i y=${1:0:4} j=10#${1:4:3} march1=60
if (( y % 400 == 0 )) || ! (( y % 100 == 0 )) && (( y % 4 == 0)); then
march1=61
fi
if (( j >= march1 )); then j=j-march1; else j=j+305; fi
local -i m=(j*5+461)/153
if (( m > 12 )); then m=m-12; fi
echo $m
}
Чтобы использовать это, было бы удобно иметь
# Advance a Julian date to the next day.
# Note: this one doesn't wrap around. Because of the way it's used
# here, that doesn't matter.
julian_next() { printf "%s%03d" ${1:0:4} $((10#${1:4:3})); }
Тогда вы можете определить
# Succeeds if the Julian date is the last day of a month
is_last_day_of_month() {
(( $(month_for_julian_day $j) != $(month_for_julian_day $(next_julian $j)) ))
}
Примечания:
- После того, как я вставил это, я заметил, что используемая здесь подсветка синтаксиса думает, что
#
запускает комментарий. Это не правда. #
только начинает комментарий, если это первый символ в слове. В этом случае присвоение j=10#${1:4:3}
заставляет интерпретировать последние три цифры предоставленной даты в базе 10.