Datumsberechnungen ohne GNU-Tools

7

Ich muss einige Datumsberechnungen und Konvertierungen in einem Shell-Skript durchführen. Berechnen Sie beispielsweise die Tagesdifferenz zwischen einem als Nov 28 20:27:19 2012 GMTund heute formatierten Datum .

Es gibt mehrere Möglichkeiten (GNU date, gawk, perletc.) , aber ich möchte in der Lage sein , es ohne GNU - Tools auf Systemen zu laufen (zB BSDs, Solaris, etc.)

Im Moment ist Perl die portabelste Lösung, die ich habe, aber es könnte die Installation zusätzlicher Module für die Datumskonvertierung erfordern.

Was soll ich wählen?

Bearbeiten : Als ich mir die Kommentare ansah, wurde mir klar, dass ich klarstellen muss, dass das Tool, das ich schreibe, öffentlich verbreitet wird. Ich suche nicht nach einer Möglichkeit, Berechnungen auf meinem Computer durchzuführen oder GNU-Tools zu installieren.

Ich möchte Werkzeuge verwenden, die wahrscheinlich auf den meisten Maschinen zu finden sind.

Matteo
quelle
1
Verwenden Sie, was immer Sie am besten können - ich persönlich mag das Python-Datum / Uhrzeit-Modul, obwohl es ein bisschen rau ist. Zeitzonen - docs.python.org/library/datetime.html
Ulrich Dangel
Python wäre kein Problem, aber ich glaube nicht, dass ich davon ausgehen kann, dass es immer installiert ist. Ich werde prüfen, ob es in Standardinstallationen von Solaris und Mac OS X vorhanden ist.
Matteo
jftr perl ist afaik nicht auf Minimal-Centos-6-Installationen installiert. Wenn Sie jedoch die Perl-Route wählen möchten, schauen Sie sich den Karton an, der im Grunde genommen ein Bundler für Perl ist und die angegebenen Module in Ihrem lokalen Verzeichnis installiert.
Ulrich Dangel
Ok, der Standardweg ist also, eine Programmiersprache wie Perl / Ruby / Python zu verwenden und nur die Anforderungen anzugeben. Diese Sprachen haben einen Paketmanager, und die meisten Systeme haben auch einen.
Ulrich Dangel
1
Nicht eingebettete Unices ohne Perl sind wirklich selten. Ich würde Perl ohne zusätzliches Modul wählen. Es ist gut genug, um ein Datum zu analysieren, das einem bekannten Format folgt.
Gilles 'SO - hör auf böse zu sein'

Antworten:

3

Ich musste dies in der Vergangenheit mit Brute-Force-Analyse und Berechnung in Shell-Skripten tun.

Die manuelle Ausführung in Shell-Skripten ist sehr fehleranfällig und langsam. Sie müssen Tage pro Monat, Schaltjahre und Zeitzonen berücksichtigen. Es wird mit verschiedenen Gebietsschemas und verschiedenen Sprachen fehlschlagen.

Ich habe eines meiner alten Skripte repariert. Dies müsste wahrscheinlich für verschiedene Shell-Umgebungen geändert werden, aber es sollte nichts geben, das nicht geändert werden kann, um in einer Shell zu funktionieren.

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

Ich habe wahrscheinlich irgendwo einen Fehler gemacht und es könnte einen besseren Weg geben. Lassen Sie mich wissen, wenn Sie einen Fehler oder einen besseren Weg finden.

Sobald Sie die Epochenzeit haben, können Sie einige nützliche Berechnungen durchführen ... obwohl das Umwandeln in ein Datum ohne die Gnu-Utensilien ... das Umgekehrte erfordert ...

mgjk
quelle
a case .... sollte immer den Standardfall enthalten, wie zum Beispiel *) output_error ; exit 1 ;;. Hier wird jede Zeitzone, die sich von einem Ihrer drei Fälle unterscheidet, stillschweigend ignoriert.
Olivier Dulac
4

Meine Lösung ist kurz und ich habe zuvor Versionen in C, Lua, Python, Perl, Awk, Ksh und Csh geschrieben (die folgende Version ist ksh). Einige spezifische Fragen, die Sie mit ansi_dn, nd_isna und dow beantworten können: Gegeben a Datum JJJJ MM TT, was ist das Datum 100 Tage vorher oder nachher? Was ist der Wochentag bei einem bestimmten Datum? Wie viele Tage liegen bei zwei Daten dazwischen?

#==========================================================================
# 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)); }
Lee Busby
quelle
2

Du könntest meine benutzen dateutils. Sie sind tragbar, aber es ist ziemlich sicher anzunehmen, dass sie nirgendwo installiert sind. Versenden Sie jedoch nur den Quellcode (einfaches altes C) oder die Binärdateien.

hroptatyr
quelle
-3

Am logischsten wäre es für mich, die Datums- und Uhrzeitfunktionen der Shells zu verwenden? zB für Bash: http://ss64.com/bash/date.html

schiffsratte
quelle
2
Ich habe Sie nicht abgelehnt, aber das Problem ist, dass sich das GNU-Datum anders verhält als das BSD-Datum und sie, soweit ich weiß, nicht den gleichen Funktionsumfang haben.
Ulrich Dangel
Ja, das Problem ist, dass das benötigte "-d" eine GNU-Datumsoption ist
Matteo
Ich habe Sie auch nicht abgelehnt, aber diese Ressource ist umständlich. Nur durch den Besuch der übergeordneten Seite ss64.com/bash identifiziert der Autor von ss64.com, welche Bash-Einbauten vorhanden sind. Die Webseitenverzeichnisstruktur ist irreführend.
Lloyd Dewolf