GNU 도구 없이 날짜 계산

GNU 도구 없이 날짜 계산

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

date여러 가지 가능성(GNU , gawk, 등) 이 있지만 perlGNU 도구 없이 시스템(예: 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

관련 정보