Как рассчитать последний рабочий день месяца в Korn Shell? - PullRequest
1 голос
/ 01 октября 2009

Я видел ответ на этот вопрос на других языках, но не на Korn Shell. Мне нужно запретить запуск сценария в последний рабочий день месяца (мы можем предположить, что M-F - рабочие дни, игнорируем праздничные дни).

1 Ответ

2 голосов
/ 01 октября 2009

Эта функция работает в Bash, Korn shell и zsh, но для нее требуется команда date (например, GNU date), которая имеет опцию -d:

function lbdm { typeset lbdm ldm dwn m y; (( m = $1 + 1 )); if [[ $m = 13 ]]; then  m=1; (( y = $2 + 1 )); else y=$2; fi; ldm=$(date -d "$m/1/$y -1 day"); dwn=$(date -d "$ldm" +%u);if [[ $dwn = 6 || $dwn = 7 ]]; then ((offset = 5 - $dwn)); lbdm=$(date -d "$ldm $offset day"); else lbdm=$ldm; fi; echo $lbdm; }

Запустите это так:

$ lbdm 10 2009
Fri Oct 30 00:00:00 CDT 2009

Вот демонстрационный скрипт, разбитый на отдельные строки с лучшими именами переменных и некоторыми комментариями:

for Month in {1..12}   # demo a whole year 
do 
    Year=2009
    LastBusinessDay=""
    (( Month = $Month + 1 ))    # use the beginning of the next month to find the end of the one we're interested in
    if [[ $Month = 13 ]]
    then
        Month=1
        (( Year++ ))
    fi;
    # these two calls to date could be combined and then parsed out
    # this first call is in "American" order, but could be changed - everything else is localized - I think
    LastDayofMonth=$(date -d "$Month/1/$Year -1 day")    # get the day before the first of the month
    DayofWeek=$(date -d "$LastDayofMonth" +%u)    # the math is easier than Sun=0 (%w)
    if [[ $DayofWeek = 6 || $DayofWeek = 7 ]]    # if it's Sat or Sun
    then
        (( Offset = 5 - $DayofWeek ))    # then make it Fri
        LastBusinessDay=$(date -d "$LastDayofMonth $Offset day")
    else
        LastBusinessDay=$LastDayofMonth
    fi
    echo "$LastDayofMonth - $DayofWeek - $LastBusinessDay"
done

Выход:

Sat Jan 31 00:00:00 CST 2009 - 6 - Fri Jan 30 00:00:00 CST 2009
Sat Feb 28 00:00:00 CST 2009 - 6 - Fri Feb 27 00:00:00 CST 2009
Tue Mar 31 00:00:00 CDT 2009 - 2 - Tue Mar 31 00:00:00 CDT 2009
Thu Apr 30 00:00:00 CDT 2009 - 4 - Thu Apr 30 00:00:00 CDT 2009
Sun May 31 00:00:00 CDT 2009 - 7 - Fri May 29 00:00:00 CDT 2009
Tue Jun 30 00:00:00 CDT 2009 - 2 - Tue Jun 30 00:00:00 CDT 2009
Fri Jul 31 00:00:00 CDT 2009 - 5 - Fri Jul 31 00:00:00 CDT 2009
Mon Aug 31 00:00:00 CDT 2009 - 1 - Mon Aug 31 00:00:00 CDT 2009
Wed Sep 30 00:00:00 CDT 2009 - 3 - Wed Sep 30 00:00:00 CDT 2009
Sat Oct 31 00:00:00 CDT 2009 - 6 - Fri Oct 30 00:00:00 CDT 2009
Mon Nov 30 00:00:00 CST 2009 - 1 - Mon Nov 30 00:00:00 CST 2009
Thu Dec 31 00:00:00 CST 2009 - 4 - Thu Dec 31 00:00:00 CST 2009

Примечание. Во время тестирования я обнаружил, что, если вы попытаетесь использовать это для дат, связанных со Второй мировой войной, произойдет сбой из-за часовых поясов военного времени, таких как CWT и CPT.

Редактировать : Вот версия, которая должна работать в AIX и других системах, которые не могут использовать вышеуказанное. Он должен работать на Борне, Баше, Корне и Зш.

function lbdN { cal $1 $2 | awk 'NF == 0 {next} FNR > 2 {week = $0} END {num = split(week, days); lbdN = days[num]; if ( num == 1 ) { lbdN -= 2 }; if ( num == 7 ) { lbdN-- }; print lbdN }'; }

Возможно, вам придется внести коррективы, если ваш cal начнет недели в понедельник.

Вот как вы можете его использовать:

month=12; year=2009    # if these are unset or null, the current month/year will be used
if [[ $(date +%d) == $(lbdN $month $year) ]]; 
then
    echo "Don't do stuff today"
else
    echo "It's not the last business day of the month"
fi

внося соответствующие изменения в синтаксис if ... then вашей оболочки, конечно.

Редактировать : Исправление ошибки : Предыдущая версия lbdN не удалась, когда февраль заканчивается в субботу 28 числа из-за способа использования tail. Новая версия исправляет это. Используются только cal и awk.

Редактировать : Для полноты я подумал, что было бы удобно включить функции для первого рабочего дня месяца.

Требуется date с -d:

function fbdm { typeset dwn d; dwn=$(date -d "$1/1/$2" +%u); d=1; if [[ $dwn = 6 || $dwn = 7 ]]; then (( d = 9 - $dwn )); fi; echo $(date -d "$1/$d/$2"); }

за май 2010 года:

Mon May 3 00:00:00 CDT 2010

Требуется только cal и awk:

function fbdN { cal $1 $2 | awk 'FNR == 3 { week = $0 } END { num = split(week, days); fbdN = days[1]; if ( num == 1 ) { fbdN += 2 }; if ( num == 7 ) { fbdN++ }; print fbdN }'; }

за август 2010 г .:

2
...