項目中需要使用shell編程對日期進行處理,shell使用的是ksh,系統爲AIX。
需求是:輸入一個yyyymmdd日期格式的日期,獲得這個日期的前N天的日期或者後N天的日期。
main查詢下來ksh裏面的date命令沒有-d這個參數,所以我自己寫了該shell:
#!/bin/ksh
# get date 截取日期
# input yyyymmdd
# output $year $month $day $yearNum $monthNum $dayNum
getDate(){
# the input length is 8
if [ `expr length $1` -ne 8 ]; then
exit -1;
fi
# All the input values are numbers
if [ ! -z "$(echo $1 | sed 's#[0-9]##g')" ]; then
exit -1;
fi
# get year
year=`echo $1 | cut -c 1-4`
# get month
month=`echo $1 | cut -c 5-6`
# get day
day=`echo $1 | cut -c 7-8`
}
# get YearFlg 驗證是否爲閏年
# input yyyy
# output $yearFlg
getYearFlg(){
local year=`expr $1 + 0`
if [ $? -ne 0 ]; then
exit -1
fi
if [[ `expr $year % 4` -eq 0 && `expr $year % 100` -ne 0 ]]; then
yearFlg=1;
elif [ `expr $year % 400` -eq 0 ]; then
yearFlg=1;
else
yearFlg=0;
fi
}
# get MaxDay 獲得每個月的最大天數
# input yyyy mm
# output $maxDay
getMaxDay(){
local month=`expr $2 + 0`
if [ $? -ne 0 ]; then
exit -1
fi
getYearFlg $1
case $month in
1|3|5|7|8|10|12) maxDay=31;;
4|6|9|11) maxDay=30;;
2)
if [ $yearFlg -eq 1 ]; then
maxDay=29
else
maxDay=28
fi
;;
esac
}
# check date 檢查日期是否爲yyyymmdd格式
# input yyyymmdd
checkDate(){
getDate $1
if [ $? -ne 0 ]; then
exit -1
fi
# check year
if [[ `expr $year + 0` -lt 0 || `expr $year + 0` -gt 9999 ]]; then
exit -1
fi
# check month
if [[ `expr $month + 0` -lt 1 || `expr $month + 0` -gt 12 ]]; then
exit -1
fi
getMaxDay $year $month
# check day
if [[ `expr $day + 0` -lt 1 || `expr $day + 0` -gt $maxDay ]]; then
exit -1
fi
}
# formmat yyyymmdd
# input yyyy($1) mm($2) dd($2)
# output $yyyymmdd
formtYYYYMMDD(){
local yearlen=`expr length $1`
if [ $yearlen -lt 4 ]; then
case $yearlen in
1) year=000$1;;
2) year=00$1;;
3) year=0$1;;
esac
fi
local monthlen=`expr length $2`
if [ $monthlen -lt 2 ]; then
month=0$2
fi
local daylen=`expr length $3`
if [ $daylen -lt 2 ]; then
day=0$3
fi
yyyymmdd=$year$month$day
}
#
# input yyyymmdd($1) num($2)
# output yyyymmdd
dateBefore(){
getDate $1
if [ $? -ne 0 ]; then
exit -1
fi
num=$2
# 4year =1461day
if [ $num -gt 1461 ]; then
num=`expr $2 % 1461`
year=`expr $year - $2 / 1461 \* 4`
fi
dayNum=`expr $day + 0`
if [ $dayNum -gt $num ]; then
day=`expr $dayNum - $num`
formtYYYYMMDD $year $month $day
echo $yyyymmdd
else
month=`expr $month - 1`
if [ $month -eq 0 ]; then
month=12
year=`expr $year - 1`
fi
getMaxDay $year $month
dayNum=`expr $dayNum + $maxDay`
if [ $dayNum -gt $num ]; then
day=`expr $dayNum - $num`
num=0
else
num=`expr $num - $maxDay`
fi
formtYYYYMMDD $year $month $day
dateBefore $yyyymmdd $num
fi
}
#
# input yyyymmdd($1) num($2)
# output yyyymmdd
dateAfter(){
getDate $1
if [ $? -ne 0 ]; then
exit -1
fi
num=$2
# 4year =1461day
if [ $num -gt 1461 ]; then
num=`expr $2 % 1461`
year=`expr $year + $2 / 1461 \* 4`
fi
getMaxDay $year $month
dayNum=`expr $day + $num`
if [ $dayNum -lt $maxDay ]; then
day=`expr $day + $num`
formtYYYYMMDD $year $month $day
echo $yyyymmdd
else
if [ $num -ge $maxDay ]; then
num=`expr $num - $maxDay`
else
day=`expr $num + $day - $maxDay`
num=0
fi
month=`expr $month + 1`
if [ $month -eq 13 ]; then
month=1
year=`expr $year + 1`
fi
formtYYYYMMDD $year $month $day
dateAfter $yyyymmdd $num
fi
}
# input yyyymmdd($1) num($2)
# example dateType 20180321 -32
# example dateType 20180321 +32
# output yyyymmdd
dateType(){
# input not null
if [ $# -ne 2 ]; then
exit -1
fi
# check input yyyymmdd
checkDate $1
if [ $? -ne 0 ]; then
exit -1
fi
# check days
if [ -z "$(echo $2 | sed 's#^[+|-][1-9][0-9]*##g')" ]; then
flg=`echo $2 | cut -c 1`
Num=`echo $2 | cut -c 2-`
# recursion
if [ "$flg" = "-" ]; then
dateBefore $1 $Num
elif [ "$flg" = "+" ];then
dateAfter $1 $Num
else
exit -1
fi
fi
}
echo "###############################"
dateType 20180321 -41 # output 20180208
dateType 20180208 +41 # output 20180321
echo "###############################"
程序主要的知識點包括遞歸的運用、正則表達式的運用、shell函數的調用,awk與sed命令的運用等。。。。
本程序截取了與日期相關的處理,還有些地方需要完善:如只可以處理公元后的日期、處理日期也只是yyyymmdd格式的日期,
遞歸並沒有採用尾遞歸,性能也有待提升。
注意點:
shell裏面的變量如果沒有使用local聲明,一般都是全局變量;