Tính toán ngày không có công cụ GNU


7

Tôi phải thực hiện một số tính toán ngày và chuyển đổi trong một tập lệnh shell. Ví dụ, tính toán sự khác biệt trong ngày giữa một ngày được định dạng như Nov 28 20:27:19 2012 GMTngày hôm nay.

Có một số khả năng (GNU date, gawk, perl, vv) nhưng tôi muốn để có thể chạy nó trên hệ thống mà không cần công cụ GNU (ví dụ, BSDs, Solaris, vv)

Hiện tại, giải pháp di động nhất mà tôi có là Perl nhưng nó có thể yêu cầu cài đặt các mô-đun bổ sung để chuyển đổi ngày.

Tôi nên chọn cái gì?

Chỉnh sửa : nhìn vào các bình luận tôi nhận ra rằng tôi cần phải làm rõ rằng công cụ tôi đang viết sẽ được phân phối công khai. Tôi không tìm cách thực hiện các tính toán trên máy của mình hoặc về cách cài đặt các công cụ GNU.

Tôi muốn sử dụng các công cụ có khả năng được tìm thấy trên phần lớn các máy.


1
Sử dụng những gì bạn cảm thấy thoải mái nhất - cá nhân tôi thích mô-đun datetime python mặc dù nó hơi thô một chút. múi giờ - docs.python.org/library/datetime.html
Ulrich Dangel

Python sẽ không phải là một vấn đề nhưng tôi không nghĩ rằng tôi chỉ có thể giả sử nó luôn được cài đặt. Tôi sẽ kiểm tra xem nó có trên các bản cài đặt Solaris và Mac OS X tiêu chuẩn không.
Matteo

jftr perl không được cài đặt trên các cài đặt tối thiểu 6 centos - nhưng nếu bạn muốn đi theo tuyến perl, hãy xem thùng carton về cơ bản là gói cho perl và sẽ cài đặt các mô-đun được chỉ định trong thư mục cục bộ của bạn.
Ulrich Dangel

Ok, vì vậy cách tiêu chuẩn là sử dụng ngôn ngữ lập trình như perl / ruby ​​/ python và chỉ nêu các yêu cầu. Các ngôn ngữ này có trình quản lý gói cũng như hầu hết các hệ thống cũng có một ngôn ngữ.
Ulrich Dangel

1
Các đơn vị không nhúng mà không có perl là thực sự hiếm. Tôi muốn đi perl mà không có mô-đun bổ sung, nó đủ tốt để phân tích một ngày theo định dạng nổi tiếng.
Gilles 'SO- đừng trở nên xấu xa'

Câu trả lời:


3

Tôi đã phải làm điều này trong quá khứ với phân tích cú pháp và tính toán vũ phu trong kịch bản shell.

Làm thủ công trong shell script rất dễ bị lỗi và chậm. Bạn cần tính đến số ngày trong tháng, năm nhuận, múi giờ. Nó sẽ thất bại với các địa phương khác nhau và các ngôn ngữ khác nhau.

Tôi đã sửa một trong những tập lệnh cũ của mình, điều này có thể cần phải được sửa đổi cho các môi trường shell khác nhau, nhưng không nên có bất cứ điều gì không thể thay đổi để hoạt động trong bất kỳ shell nào.

#!/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

Tôi có lẽ đã phạm sai lầm ở đâu đó và có thể có một cách tốt hơn. Hãy cho tôi biết nếu bạn tìm thấy một lỗi, hoặc một cách tốt hơn.

Khi bạn có thời gian kỷ nguyên, bạn có thể thực hiện một số tính toán hữu ích ... mặc dù việc chuyển đổi lại thành một ngày mà không có tiện ích gnu ... yêu cầu thực hiện ngược lại ...


a case .... phải luôn luôn chứa trường hợp mặc định, chẳng hạn như *) output_error ; exit 1 ;;. Tại đây, bất kỳ múi giờ nào khác với một trong 3 trường hợp của bạn sẽ bị bỏ qua trong âm thầm .
Olivier Dulac

4

Giải pháp của tôi rất ngắn và tôi đã viết các phiên bản trước đó bằng C, Lua, Python, Perl, Awk, Ksh và Csh (phiên bản bên dưới là ksh.) Một số câu hỏi cụ thể bạn có thể trả lời với ansi_dn, nd_isna và dow: Đưa ra ngày YYYY MM DD, ngày 100 ngày trước hay sau là ngày nào? Cho một ngày, ngày trong tuần là gì? Cho hai ngày, có bao nhiêu ngày ở giữa?

#==========================================================================
# 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

Bạn có thể sử dụng của tôi dateutils. Chúng có thể mang theo nhưng khá an toàn khi cho rằng chúng không được cài đặt ở bất cứ đâu. Tuy nhiên, chỉ cần gửi mã nguồn (C cũ đơn giản) hoặc nhị phân.


-3

Điều hợp lý nhất với tôi sẽ là sử dụng khả năng ngày & giờ của shell? ví dụ: bash: http://ss64.com/bash/date.html


2
Tôi đã không đánh giá thấp bạn nhưng vấn đề là ngày GNU sẽ hoạt động khác với ngày BSD và theo như tôi biết, theo như tôi biết, cùng một bộ tính năng.
Ulrich Dangel

Có, vấn đề là "-d" cần thiết là một tùy chọn ngày GNU
Matteo

Tôi cũng không bỏ phiếu cho bạn, nhưng tài nguyên đó thật khó xử. Chỉ bằng cách truy cập trang cấp cao hơn ss64.com/bash , tác giả của ss64.com mới xác định được bash dựng sẵn. Cấu trúc thư mục bên web là sai lệch.
Lloyd Dewolf
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.