diff --git a/lib/compatible_date.sh b/lib/compatible_date.sh index e2f0ac3..fd3e469 100755 --- a/lib/compatible_date.sh +++ b/lib/compatible_date.sh @@ -200,3 +200,230 @@ dateTime_secs2canonical () { echo $dateStr return 0 } + +####################################################################### +# Compute the elapsed time (in secs) between two dateTime values. +# +# Usage: tspan dateTime1 dateTime2 +# +# The arguments are given in "canonical dateTime string format" +# as described above. +# +# If dateTime1 < dateTime2, the function returns a positive integer. +# If dateTime1 > dateTime2, the function returns a negative integer. +# If dateTime1 == dateTime2, the function returns zero. +####################################################################### +tspan () { + + local dateTime1 + local dateTime2 + local secs1 + local secs2 + local dateTime1confirm + local dateTime2confirm + + # make sure there are two (and only two) command-line arguments + if [ $# -eq 2 ]; then + dateTime1="$1" + dateTime2="$2" + else + echo "ERROR: $FUNCNAME: incorrect number of arguments: $# (2 required)" >&2 + return 2 + fi + + # convert dateTime1 to seconds past the Epoch + secs1=$( dateTime_canonical2secs $dateTime1 ) + + # sanity check (for logging) + dateTime1confirm=$( dateTime_secs2canonical $secs1 ) + + # convert dateTime1 to seconds past the Epoch + secs2=$( dateTime_canonical2secs $dateTime2 ) + + # sanity check (for logging) + dateTime2confirm=$( dateTime_secs2canonical $secs2 ) + + # compute time difference (which may be negative) + echo $(( secs2 - secs1 )) + + return 0 +} + +####################################################################### +# Given a time instant, compute the time interval (in secs) between +# the current time (NOW) and the time instant. +# +# Usage: secsUntil [-b begDateTime] [endDateTime] +# +# The arguments begDateTime and endDateTime are expected to be +# in "canonical dateTime string format" as described above. +# +# If the -b option is omitted, the function computes the time +# interval between NOW and endDateTime. If the latter is omitted +# on the command line, its value is taken from stdin. +# +# Presumably endDateTime is a time instant in the future but the +# function does not check this. If endDateTime is in the past, +# and option -b is omitted, the output of this function will be +# negative. +# +# If the -b option is present, the time interval between +# begDateTime and endDateTime is computed. In this case, +# the output will be positive if (and only if) endDateTime is +# chronologically later than begDateTime. +####################################################################### +secsUntil () { + + local dateTime + local now + local d + + local opt + local OPTARG + local OPTIND + while getopts ":b:" opt; do + case $opt in + b) + now="$OPTARG" + ;; + \?) + echo "ERROR: $FUNCNAME: Unrecognized option: -$OPTARG" >&2 + return 2 + ;; + :) + echo "ERROR: $FUNCNAME: Option -$OPTARG requires an argument" >&2 + return 2 + ;; + esac + done + + # make sure there is at most one command-line argument + shift $(( OPTIND - 1 )) + if [ $# -eq 0 ]; then + # take input from stdin + dateTime=$( /bin/cat - ) + elif [ $# -eq 1 ]; then + dateTime="$1" + else + echo "ERROR: $FUNCNAME: incorrect number of arguments: $# (0 or 1 required)" >&2 + return 2 + fi + + # compute NOW if necessary + if [ -z "$now" ]; then + # compute dateTime NOW + now=$( /bin/date -u +%Y-%m-%dT%TZ ) + fi + + # compute time until the given time instant + d=$( tspan "$now" "$dateTime" ) + echo $d + + return 0 +} + +####################################################################### +# Given a time instant, compute the time interval (in secs) between +# the time instant and the current time (NOW). +# +# Usage: secsSince [-e endDateTime] [begDateTime] +# +# The arguments begDateTime and endDateTime are expected to be +# in "canonical dateTime string format" as described above. +# +# If the -e option is omitted, the function computes the time +# interval between begDateTime and NOW. If the former is omitted +# on the command line, its value is taken from stdin. +# +# Presumably begDateTime is a time instant in the past but the +# function does not check this. If begDateTime is in the future, +# and option -e is omitted, the output of this function will be +# negative. +# +# If the -e option is present, the time interval between +# begDateTime and endDateTime is computed. In this case, +# the output will be positive if (and only if) endDateTime is +# chronologically later than begDateTime. +####################################################################### +secsSince () { + + local dateTime + local now + local d + + local opt + local OPTARG + local OPTIND + while getopts ":e:" opt; do + case $opt in + e) + now="$OPTARG" + ;; + \?) + echo "ERROR: $FUNCNAME: Unrecognized option: -$OPTARG" >&2 + return 2 + ;; + :) + echo "ERROR: $FUNCNAME: Option -$OPTARG requires an argument" >&2 + return 2 + ;; + esac + done + + # make sure there is at most one command-line argument + shift $(( OPTIND - 1 )) + if [ $# -eq 0 ]; then + # take input from stdin + dateTime=$( /bin/cat - ) + elif [ $# -eq 1 ]; then + dateTime="$1" + else + echo "ERROR: $FUNCNAME: incorrect number of arguments: $# (0 or 1 required)" >&2 + return 2 + fi + + # compute NOW if necessary + if [ -z "$now" ]; then + # compute dateTime NOW + now=$( /bin/date -u +%Y-%m-%dT%TZ ) + fi + + # compute time until invalid in secs + d=$( tspan $dateTime $now ) + echo $d + + return 0 +} + +####################################################################### +# Convert seconds into an ISO 8061 duration. +# +# Usage: secs2duration SECONDS +# +####################################################################### +secs2duration () { + + local days + local hours + local mins + local secs + + # make sure there is exactly one command-line argument + if [ $# -eq 1 ]; then + secs="$1" + else + echo "ERROR: $FUNCNAME: incorrect number of arguments: $# (1 required)" >&2 + return 2 + fi + + # convert seconds to days, hours, minutes, seconds + days=$(( secs/86400 )) + hours=$(( secs%86400/3600 )) + mins=$(( secs%3600/60 )) + secs=$(( secs%60 )) + + # an ISO 8601 duration + printf 'P%dDT%dH%dM%dS\n' $days $hours $mins $secs + + return +} \ No newline at end of file