쉘 스크립트에서 일부 날짜 계산과 변환을 수행해야 합니다. 예를 들어, 오늘과 같은 형식의 날짜 사이의 일차를 계산합니다 Nov 28 20:27:19 2012 GMT
.
date
여러 가지 가능성(GNU , gawk
, 등) 이 있지만 perl
GNU 도구 없이 시스템(예: BSD, Solaris 등)에서 실행할 수 있기를 바랍니다.
현재 제가 가지고 있는 가장 이식성이 뛰어난 솔루션은 Perl이지만 날짜 변환을 위해 추가 모듈을 설치해야 할 수도 있습니다.
무엇을 선택해야 합니까?
편집하다: 댓글을 보면서 제가 작성하고 있는 도구가 공개적으로 배포된다는 점을 명확히 해야 한다는 것을 깨달았습니다. 나는 내 컴퓨터에서 계산을 수행하는 방법이나 GNU 도구를 설치하는 방법을 찾고 있지 않습니다.
나는 대부분의 기계에서 찾을 수 있는 도구를 사용하고 싶습니다.
답변1
내 솔루션은 짧으며 이전에 C, Lua, Python, Perl, Awk, Ksh 및 Csh 버전을 작성했습니다(아래 버전은 ksh입니다.) ansi_dn, nd_isna 및 dow로 답변할 수 있는 몇 가지 구체적인 질문은 다음과 같습니다. date YYYY MM DD, 100일 전후의 날짜는 언제입니까? 날짜가 주어지면 요일은 언제입니까? 두 개의 날짜가 주어지면 그 사이에 며칠이 있습니까?
#==========================================================================
# Calendar operations: Valid for dates in [1601-01-01, 3112-12-30].
# Adapted from a paper by B.E. Rodden, Mathematics Magazine, January, 1985.
#==========================================================================
# ANSI day number, given y=$1, m=$2, d=$3, starting from day number 1 ==
# (1601,1,1). This is similar to COBOL's INTEGER-OF-DATE function.
function ansi_dn {
integer y=$1 ys=$(($1-1601)) m=$2 d=$3 s dn1 isleap h
((isleap = y%4==0 && (y%100!=0 || y%400==0) ))
((dn1 = 365*ys + ys/4 - ys/100 + ys/400)) # first day number of given year.
((s = (214*(m-1) + 3)/7 + d - 1)) # standardized day number within year.
((h = -2*(!isleap && s>58) - (isleap && s>59) )) # correction to actual dn.
print -- $((1 + dn1 + s + h))
}
# Inverse of ansi_dn, for adn=$1 in [1, 552245], with returned dates in
# [1601-01-01, 3112-12-30]. Similar to COBOL's DATE-OF-INTEGER function.
function nd_isna {
integer y a s ys d0=$(($1-1)) dn1 isleap h
typeset -Z2 m d
((ys = d0/365))
((365*ys + ys/4 - ys/100 + ys/400 >= $1)) && ((ys -= 1))
((dn1 = 365*ys + ys/4 - ys/100 + ys/400))
((y = 1601 + ys))
((a = d0 - dn1))
((isleap = y%4==0 && (y%100!=0 || y%400==0) ))
((h = -2*(!isleap && a>58) - (isleap && a>59) ))
((s = a - h))
((m = (7*s + 3)/214 + 1))
((d = s - (214*(m-1) + 3)/7 + 1))
print -- $y $m $d
}
# Day of the week, given $1=ansi_dn. 0==Sunday, 6==Saturday.
function dow { print -- $(($1%7)); }
답변2
과거에는 쉘 스크립트에서 무차별 구문 분석 및 계산을 사용하여 이 작업을 수행해야 했습니다.
쉘 스크립트에서 수동으로 수행하면 오류가 발생하기 쉽고 속도가 느립니다. 월별 날짜, 윤년, 시간대를 고려해야 합니다. 다른 로캘과 다른 언어에서는 실패합니다.
이전 스크립트 중 하나를 수정했습니다. 아마도 다른 쉘 환경에 맞게 수정해야 할 것입니다. 그러나 어떤 쉘에서도 작동하도록 변경할 수 없는 것이 있어서는 안 됩니다.
#!/bin/sh
datestring="Nov 28 20:27:19 2012 GMT"
today=`date`
function date2epoch {
month=$1
day=$2
time=$3
year=$4
zone=$5
# assume 365 day years
epochtime=$(( (year-1970) * 365 * 24 * 60 * 60 ))
# adjust for leap days
i=1970
while [[ $i -lt $4 ]]
do
if [[ 0 -eq $(( i % 400 )) ]]
then
#echo $i is a leap year
# divisible by 400 is a leap year
epochtime=$((epochtime+24*60*60))
elif [[ 0 -eq $(( i % 100 )) ]]
then
#echo $i is not a leap year
epochtime=$epochtime
elif [[ 0 -eq $(( i % 4 )) ]]
then
#echo $i is a leap year
# divisible by 4 is a leap year
epochtime=$((epochtime+24*60*60))
# epoch=`expr $epoch + 24 * 60 * 60`
fi
i=$((i+1))
done
dayofyear=0
for imonth in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
do
if [[ $month == $imonth ]]
then
break
fi
case $imonth in
'Feb')
if [[ 0 -eq $(( year % 400 )) ]]
then
days=29
elif [[ 0 -eq $(( year % 100 )) ]]
then
days=28
elif [[ 0 -eq $(( year % 4 )) ]]
then
days=29
fi
;;
Jan|Mar|May|Jul|Aug|Oct|Dec) days=31 ;;
*) days=30 ;;
esac
#echo $imonth has $days days
dayofyear=$((dayofyear + days))
done
## add the day of the month
dayofyear=$((dayofyear+day))
#echo $dayofyear
########## Add the day fo year (-1) to the epochtime
#(-1, since eg. Jan 1 is not 24 hours into Jan1 )
epochtime=$((epochtime + (dayofyear -1) * 24*60*60))
#echo $epochtime
################## hours, minutes, seconds
OFS=$IFS
IFS=":"
set -- $time
hours=$1
minutes=$2
seconds=$3
epochtime=$((epochtime + (hours * 60 * 60) + (minutes * 60) + seconds))
IFS=$OFS
################## Time zone
case $zone in
'GMT') zonenumber=0
break;;
'EST') zonenumber=-5
break;;
'EDT') zonenumber=-4
break;;
esac
epochtime=$((epochtime + zonenumber * 60 * 60 ))
echo $epochtime
}
result=`date2epoch $datestring`
echo $result
아마도 어딘가에서 실수를 했을 수도 있고 더 좋은 방법이 있을 수도 있습니다. 버그나 더 나은 방법을 발견하면 알려주세요.
에포크 시간이 확보되면 몇 가지 유용한 계산을 수행할 수 있습니다. 비록 gnu 유틸리티 없이 이를 다시 날짜로 변환하려면... 위의 작업을 반대로 수행해야 하지만...
답변3
당신은 내dateutils
. 이식 가능하지만 어디에도 설치되지 않는다고 가정하는 것이 오히려 안전합니다. 그러나 소스 코드(일반 C)나 바이너리만 제공하세요.
답변4
나에게 가장 논리적인 것은 쉘의 날짜 및 시간 기능을 사용하는 것입니까? 예를 들어 bash의 경우 :http://ss64.com/bash/date.html