计算天数并将它们显示为两个日期shell脚本之间的年,月,日

时间:2014-11-24 01:09:25

标签: shell date-arithmetic

我正在尝试编写一个shell脚本,用于确定用户输入年份当前日期与复活节之间的年,月和日之间的差异。例如,用户输入1995并且脚本应该计算自那时以来经过了多少年并将这些天转换为年,月和日并显示结果。 我正在粘贴我的所有代码

#!/bin/bash

echo "This script will show you on which day is Easter for the chosen year of the Gregorian calendar!"

x=0
read x
A=$((x % 19))
B=$((x / 100))
C=$((x % 100))
D=$((B / 4))
E=$((B % 4))
G=$(((8 * B + 13) / (25)))
H=$(((19 * A + B - D - G + 15) % (30)))
M=$(((A + 11 * H) / (319)))
J=$((C / 4))
K=$((C % 4))
L=$(((2 * E + 2 * J - K - H + M + 32) % (7)))
N=$(((H - M + L + 90) / (25)))
P=$(((H - M + L + N + 19) % (32)))

Z=$(date --date="$x-$N-$P" +%A)
echo
echo "Easter is `date --date="$x-$N-$P"`"

([ "`cal 2 $x | grep 29`" != "" ] &&  echo -e "\n$x is a leap year\n")
([ "`cal 2 $x | grep 29`" = "" ] &&  echo -e "\n$x is not a leap year\n")

yearnow=$(date +%Y)
year=$(($x - $yearnow))
year1=$(($yearnow - $x))

if (($x > $yearnow))
then
echo "There are $year years until Easter in $x."
else
echo "$year1 years have passed since Easter in $x."
fi

pmonths=0
if (($x > $yearnow))
then
pmonths=$(($year * 12))
echo "There are $pmonths months until Easter."
else
pmonths=$(($year1 * 12))
echo "$pmonths months have passed since Easter in $x."
fi


#checking and counting how many leap and normal years are there between the present year and the chosen one
counter=1
leapycounter=0
nycounter=0
if (($x > $yearnow))
then
while (($counter < $year))
do
leapy=$(($x + $counter))
if (($leapy == (($leapy / 4) - ($leapy / 100) + ($leapy / 400))))
then leapycounter=$(($leapycounter + 1))
else nycounter=$(($nycounter + 1))
fi
counter=$(($counter + 1))
done
fi

#checking if the present year is leap so the days left from this year can be calculated
if (($x > $yearnow))
then
datenow=$(date +%j)
datenow=`echo $datenow|sed 's/^0*//'`
        if (($yearnow == (($yearnow / 4) - ($yearnow / 100) + ($yearnow / 400))))
          then
                datenow=$((366 - $datenow))
        else
                datenow=$((365 - $datenow))
        fi
datethen=$(date --date="$x-$N-$P" +%j)
datethen=`echo $datethen|sed 's/^0*//'`
days=$(($datethen + $datenow))
lyc=$((($leapycounter * 366) + ($nycounter * 365)))
dayspassed=$(($lyc + $days))
echo "There are $dayspassed days until Easter."
else
                datethen=$(date --date="$x-$N-$P" +%j)
                datethen=`echo $datethen|sed 's/^0*//'`
        if (($yearnow == (($yearnow / 4) - ($yearnow / 100) + ($yearnow / 400))))
        then
                datethen=$((366 - $datethen))
        else
                datethen=$((365 - $datethen))
        fi
datenow=$(date +%j)
datenow=`echo $datenow|sed 's/^0*//'`
days=$(($datethen + $datenow))
lyc=$((($leapycounter * 366) + ($nycounter * 365)))
dayspassed=$(($lyc + $days))
echo "$dayspassed days have passed since Easter in $x."
fi

#this should be converting the days into years, months and days
dtomconst=$(((((365/12)*3)+(366/12))/4))
months=$(($dayspassed / $dtomconst))
monthsleft=$(($months % 12))
years=$(($months / 12))
daysleft=$((($dayspassed - ($monthsleft * $dtomconst)) - (365*$years)))
echo "months are $months"
echo "daysleft are $daysleft"
echo $years
months=$(($months + $monthsleft))
echo $monthsleft
echo "months after calculations: $months"

所以问题是它不能正确计算日期,特别是过去几年。此外,如果用户输入像1888这样的年份,脚本会显示错误,我不知道为什么。 如果有人可以就我的问题说一两句话,我会非常感激。提前谢谢。

1 个答案:

答案 0 :(得分:1)

正如评论中所指出的,脚本的挑战将是确定某一年复活节发生的日期,因为每年的日期不同,每年的日期不同。进一步使差异计算复杂化的是月份的概念,因为闰年​​会改变2月的长度。还有偶尔会leap-second投入使用。

但是,如评论中所示,一旦您到达复活节某一年,您可以让date功能为您完成剩余的大部分工作。给定任何日期,您可以将该值传递给date函数,并将该值转换为自纪元以来的秒数。请注意,这在1970之前的数年内并没有直接帮助,但您可以进一步延伸,以近似的方式操纵每年的秒数。

一旦你有复活节,获得当前时间,以纪元为单位的秒数表示,date是微不足道的。然后,您可以使用不同的工具,转换表示所选复活节时间的秒数,然后可以用years, days, hours, minutes, seconds表示。当然,这些必须加以考虑leap效果,具体取决于所需的准确程度。

以下是解决时差问题的简单示例。包含的函数,以秒为单位给出时间差异,然后声明(根据需要)作为参数给出的时间所代表的年,日,小时,分钟和秒。这并不能解决您的所有问题,但希望它能够帮助您以更轻松的方式处理该信息。如果您对内容有任何疑问,请与我们联系:

#!/bin/bash

## time difference function takes an argument in seconds, and then calculates,
#  declares and fills variables 'years' 'days' 'hours' 'minutes' and 'seconds'
#  representing the time in seconds given as the argument. The values are only
#  declared as necessary allowing a test for their presence.
function sec2ydhms {

    [ -n $1 ] || { printf "%s() error: insufficient arguments\n" "$FUNCNAME"; return 1; }

    local secperday=$((24 * 3600))
    local secperyr=$((365 * secperday))
    local remain=$1

    # printf "\nremain: %s\n\n" "$remain"

    if [ "$remain" -ge "$secperyr" ]; then
        declare -g years=$((remain/secperyr))
        remain=$((remain - (years * secperyr)))
    fi

    if [ "$remain" -ge "$secperday" ]; then
        declare -g days=$((remain/secperday))
        remain=$((remain - (days * secperday)))
    fi

    if [ "$remain" -ge 3600 ]; then
        declare -g hours=$((remain/3600))
        remain=$((remain - (hours * 3600)))
    fi

    if [ "$remain" -ge 60 ]; then
        declare -g minutes=$((remain/60))
    fi

    declare -g seconds=$((remain - (minutes * 60))) 
}

oifs=$IFS                       # save old IFS, and set to only break on newline
IFS=$'\n'                       # allowing date formats containing whitespace
printf "\n Enter the date for Easter (in past): "
read edate                      # read date entered

eepoch=$(date -d "$edate" +%s)  # convert Easter date to seconds since epoch
now=$(date +%s)                 # get current time since epoch

sec2ydhms $((now-eepoch))       # compute time from Easter in Y,D,H,M,S

## print time since Easter
printf "\n Time since %s:\n\n" "$(date -d @"${eepoch}")"
[ -n "$years"   ] && printf "  %4s  years\n" "$years"
[ -n "$days"    ] && printf "  %4s  days\n" "$days"
[ -n "$hours"   ] && printf "  %4s  hours\n" "$hours"
[ -n "$minutes" ] && printf "  %4s  minutes\n" "$minutes"
[ -n "$seconds" ] && printf "  %4s  seconds\n\n" "$seconds"

exit 0

<强>输出:

$ bash easter.sh

 Enter the date for Easter (in past): 03/21/1985

 Time since Thu Mar 21 00:00:00 CST 1985:

    29  years
   254  days
    21  hours
    12  minutes
    16  seconds