GNU 도구 없이 날짜 계산

GNU 도구 없이 날짜 계산

쉘 스크립트에서 일부 날짜 계산 및 변환을 수행해야 합니다. 예를 들어, 오늘 형식의 날짜 와 오늘 Nov 28 20:27:19 2012 GMT간의 일수 차이를 계산합니다.

여러 가지 가능성(GNU, ,, 등)이 있지만 dateGNU 도구 없이 시스템(예: BSD, Solaris 등)에서 실행할 수 있기를 바랍니다.gawkperl

현재 제가 가지고 있는 가장 이식성이 뛰어난 솔루션은 Perl이지만 날짜 변환을 위해 추가 모듈을 설치해야 할 수도 있습니다.

무엇을 선택해야 합니까?

편집하다: 댓글을 보니 제가 작성 중인 도구가 공개적으로 배포된다는 점을 명확히 해야 한다는 것을 깨달았습니다. 나는 내 컴퓨터에서 계산을 수행하는 방법이나 GNU 도구를 설치하는 방법을 찾고 있지 않습니다.

대부분의 컴퓨터에서 찾을 수 있는 도구를 사용하고 싶습니다.

답변1

내 솔루션은 짧습니다. 이전에 C, Lua, Python, Perl, Awk, Ksh 및 Csh 버전을 작성했습니다(아래 버전은 ksh입니다.) ansi_dn, nd_isna 및 dow를 사용하여 몇 가지 특정 질문에 답할 수 있습니다. 날짜 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

관련 정보