You may play with this code...
t1="08/27/06 05:00:05"
t2="08/29/06 02:21:05"
dt=$(( $( printf "%(%s)T\n" "$t2") - $( printf "%(%s)T\n" "$t1") ))
print - "$dt seconds"
s=$(( dt % 60 ))
m=$(( dt % 3600 / 60 ))
h=$(( dt / 3600 ))
printf "%02d:%02d:%02d\n" $h $m $s
Which, BTW, results in 45:21:00.
Janis
You asked this and got answers a couple of days ago. What was inadequate
about the responses you got then that you're posting again?
Ed.
Interesting solutuion by why do I get the following, can this work with
ksh
()T - ()T : syntax error
The implication seems to be that no external utilites can be used (hence
the phrase "korn shell only".
Your solution used GNU date; mine used GNU AWK. I guess both are
verboten to the OP.
I don't think it is possible in pure shell - without some kind of
enhancement (e.g., the super-charged Bash that is frequently posted
about here)
--
I've already told you more than I know.
Here is a script that can do date calculations e.g.
# datecalc2 datediff 2006 8 29 2 21 5 2006 8 27 5 0 5
45:21:00
Last day of previous month
# datecalc2 dateadd $(date "+%Y %m 1 0 0 0 -1")
2006-08-31 23:59:59
seconds to hour:min:sec
# datecalc2 hms 63
00:01:03
But you'll need a ksh that support large integers
e.g. the below ksh does not work
# uname -psr
Linux 2.6.9-22.ELsmp i686
# strings /bin/ksh | grep -i ksh |tail -1
@(#)PD KSH v5.2.14 99/07/13.2
# ksh -c 'echo $((36524*24*60*60))'
-1139293696
# bash -c 'echo $((36524*24*60*60))'
3155673600
But is ok on
# uname -psr
SunOS 5.8 sparc
If you don't have GNU date use tr to translate date formats e.g.
# echo "20yY mM dD hH nN sS" | tr "mMxdDXyYqhHznNZsS" "08/27/06
05:00:05"
2006 08 27 05 00 05
# echo "mM/dD/yY hH:nN" | tr "cCyYxmMXdDqhHznNZsS" "1806-03-31
15:10:59"
03/31/06 15:10
## AM/PM is more tricky but still doable
#eval echo $( echo "20yY mM dD hHp nN sS" \
| tr "mMxdDXyYqhHznNZsSp" "08/27/06 05:00:05p" \
| sed 's/ \(..\)p/ $((\1+12))/;s/a//')
2006 08 27 17 00 05
#eval echo $( echo "20yY mM dD hHp nN sS" \
| tr "mMxdDXyYqhHznNZsSp" "08/27/06 05:00:05a" \
| sed 's/ \(..\)p/ $((\1+12))/;s/a//')
2006 08 27 05 00 05
And here is the script
-------------------
# cat datecalc2
#!/usr/bin/ksh
# Created by Petrus Dreyer
# Code is released under the GNU General Public Licence
#
syntaxmsg() {
cat <<EOF
# Syntax: datecalc <function> <args>
# function:
# datadiff <yr> <mt> <dy> <hr> <mn> <sc> <yr> <mt> <dy> <hr> <mn>
<sc>
# dateadd <yr> <mt> <dy> <hr> <mn> <sc> <sc>
EOF
exit 6
}
secs() { y=$1; m=$2; d=$3; H=${4:-0}; M=${5:-0}; S=${6:-0}
# Based on the proleptic Gregorian Calendar by projecting the current
# Gregorian dating system back beyond the time of its implementation
# The Rata Die number 1 is January 1 of the year 1
ty=$(( $y + 10000 )) # handle negative year up to -9999
p=$(( $ty - 1 ))
set -A odm 0 0 31 59 90 120 151 181 212 243 273 304 334
rd=$(( $p * 365 + $p / 4 - $p / 100 + $p / 400 + ${odm[$m]} + $d + \
( ( 1 - ( 14 - $m ) / 12 ) \
* ( $ty / 4 - $ty / 100 + $ty / 400 - ( $p / 4 - $p / 100 + $p / 400
) ) ) \
- 3652425 ))
#echo jd=$(( $rd + 1721425 )) # Julian day number
echo $(( $rd * 24 * 60 * 60 + $H * 60 * 60 + $M * 60 + $S ))
}
hms() {
hour=$(($1/60/60))
[ $hour -lt 10 ] && hour="0"$hour
min=$((($1-(($1/60/60)*60*60))/60))
[ $min -lt 10 ] && min="0"$min
sec=$(($1-($hour*60*60)-($min*60)))
[ $sec -lt 10 ] && sec="0"$sec
echo "$hour:$min:$sec"
}
todate() {
days=$(( $1 / ( 24 * 60 * 60 ) ))
secs=$(( $1 - $days * 24 * 60 * 60 ))
dayst=$(( $days + 3652425 )) # Handle negative days to GC year -9999
y=$(( ( (dayst * 10000000 / 3652425) + 999 ) / 1000 )) # years since 1
leapdays() {
echo $(( ($1/4) - ($1/100) + ($1/400) ))
}
p=$(( $y - 1 )) # Previous year
od=$(( $dayst - $p * 365 - $(leapdays $p) )) # Ordinal day
ld=$(( $(leapdays $y) - $(leapdays $p) )) # leap day
# ofsett 1 Mar as day 1 (365-59=306) and 1 Jan as day 307
d1=$(( ( $od + 306 ) - (365 + $ld) * ( ( $od + 306 ) / ( 366 + $ld ) )
))
# Now the months of 31 30 31 days have repeating patrn 10101
# month offset = days from 1 Mar - 0.5 days / 30.6 days per month
m1=$(( 3 + ( $d1 * 10 - 5 ) / 306 ))
#echo $y $od $d1 $m1 $ld
set -A odm 0 0 0 0 31 61 92 122 153 184 214 245 275 306 337
d=$(( $d1 - ${odm[$m1]} ))
[ $d -lt 10 ] && d="0$d"
m=$(( $m1 - 12 * ( $m1 / 13 ) )) # reverse ofset to 1 Mar
[ $m -lt 10 ] && m="0$m"
y=$(( $y - 10000 ))
echo $y'-'$m'-'$d $(hms $secs)
}
datediff() {
hms $(( $(secs $1 $2 $3 $4 $5 $6) - $(secs $7 $8 $9 ${10} ${11} ${12})
))
}
dateadd() {
todate $(( $(secs $1 $2 $3 $4 $5 $6) + $7 ))
}
[[ "$1" = "" ]] && syntaxmsg
datecmd=$1
shift
case $datecmd in
hms) hms $@ ;;
secs) secs $@ ;;
todate) todate $@ ;;
datediff) datediff $@ ;;
dateadd) dateadd $@ ;;
*) echo "ERR: Invalid arguments" >&2 ; exit 12;;
esac
-------------------
I'm sure it's possible if you're allowed to use the standard utilities
without GNU enhancements (sed and awk in particular). It would probably
have to be a fair amount of code, though.
--
Christopher Mattern
"Which one you figure tracked us?"
"The ugly one, sir."
"...Could you be more specific?"
I fear it's less than 13 years; I think it's a quite recent addition
to ksh93.
So if Stu works in an environment where is not allowed to install a
recent version of ksh93 he has to resort to other solutions.
Janis
Best is to convert the date strings to epoch time and then do the
calculations.
This function will convert any date (month, day, year, hour, minute, seconds)
into seconds since epoch:
ddiff()
{
typeset year month day hour minute second
typeset moff jday epoch lyears
set -A moff 0 0 31 59 90 120 151 181 212 243 273 304 334 365
year="$1"
month="$2"
day="$3"
hour="$4"
minute="$5"
second="$6"
# Calculate day of year (counting from 0)
jday=$(( (day - 1) + moff[$month] ))
# Number of leap years
lyears=$(( (year - 1968) / 4 ))
# Adjust if we are still in Jan/Feb of leap year
[[ $((year % 4)) == 0 && $month < 3 ]] && lyears=$((lyears - 1))
# calculate the time since epoch
epoch=$(( ((year - 1970) * 365 + jday + lyears) * 86400
+ hour * 3600 + minute * 60 + second ))
echo $epoch
}
This script should work between March 1st 1901 up to February 28th 2100
(because of the simplified leap year calculation). But on many systems
you'd have more problems with epoch time already. If you need this script
to work even outside this time period add
lyears=$(( lyears - year / 100 + year / 400 + 15))
at the appropriate position (hope the "correction factor" at the end is
right)
--
Daniel
Yes. The calculations up through day-of-year are trivial (even with leap days
and all that), but handling the large body of timezone rules, which in addition
are ever-changing (see e.g. upcoming US rule change)? You really want to leave
that up to a library that isn't maintained by *you*. Processing timestamps
without using a complex timezone library is easy only if the timezone offset is
known - perhaps as a result of the time being written explicitly in UTC, or
with a numeric offset given as part of the timestamp (the approach I usually
take when writing logfiles and such).
John
--
John DuBois spc...@armory.com KC6QKZ/AE http://www.armory.com/~spcecdt/
FYI: bc fix for large int calculations in script
# diff datecalc2 datecalc2.2
26c26
< echo $(( $rd * 24 * 60 * 60 + $H * 60 * 60 + $M * 60 + $S ))
---
> echo "$rd * 24 * 60 * 60 + $H * 60 * 60 + $M * 60 + $S" | bc # use bc for large int
38,39c38,39
< days=$(( $1 / ( 24 * 60 * 60 ) ))
< secs=$(( $1 - $days * 24 * 60 * 60 ))
---
> days=$( echo "$1 / ( 24 * 60 * 60 )" | bc )
> secs=$( echo "$1 - $days * 24 * 60 * 60" | bc )
41c41,42
< y=$(( ( (dayst * 10000000 / 3652425) + 999 ) / 1000 )) # years since
1
---
> yt=$( echo "$dayst * 10000000 / 3652425" | bc )
> y=$(echo "($yt + 999 ) / 1000" | bc) # years since 1
63c64
< hms $(( $(secs $1 $2 $3 $4 $5 $6) - $(secs $7 $8 $9 ${10} ${11}
${12}) ))
---
> hms $(echo "$(secs $1 $2 $3 $4 $5 $6) - $(secs $7 $8 $9 ${10} ${11} ${12})" | bc)
66c67
< todate $(( $(secs $1 $2 $3 $4 $5 $6) + $7 ))
---
> todate $(echo "$(secs $1 $2 $3 $4 $5 $6) + $7 " | bc)