• Datediff script (2/2)

    From castAway@21:1/5 to All on Thu Nov 10 19:33:21 2022
    [continued from previous message]

    auto r, s
    if(0 > x) {
    return -r(-x, d)
    }
    r = x + 0.5*10^-d
    s = scale
    scale = d
    r = r*10/10
    scale = s
    return r
    };
    scale = ($SCL + 1);
    r( (${years_between:-0} + ( (${range:-0} - ( (${daycount_years:-0} + ${daycount_leap_years:-0}) * 3600 * 24) ) / (${date1_year_days_adj:-0} * 3600 * 24) ) ) , $SCL); /** YEARS **/
    r( (${monthcount:-0} + ( (${range:-0} - (${fullmonth_days_save:-0} * 3600 * 24) ) / (${date1_month_max_day:-0} * 3600 * 24) ) ) , $SCL); /** MONTHS **/
    r( (${range:-0} / 604800) , $SCL); /** WEEKS **/
    r( (${range:-0} / 86400) , $SCL); /** DAYS **/
    r( (${range:-0} / 3600) , $SCL); /** HOURS **/
    r( (${range:-0} / 60) , $SCL); /** MINUTES **/") )
    #ARRAY: 0=YEARS 1=MONTHS 2=WEEKS 3=DAYS 4=HOURS 5=MINUTES
    then #choose layout of single units
    if ((OPTT || !OPTLAYOUT))
    then #layout one
    prHelpf ${OPTTy:+${bc[0]}} && range_pr="${bc[0]} year$SS"
    prHelpf ${OPTTmo:+${bc[1]}} && range_pr="$range_pr | ${bc[1]} month$SS"
    prHelpf ${OPTTw:+${bc[2]}} && range_pr="$range_pr | ${bc[2]} week$SS"
    prHelpf ${OPTTd:+${bc[3]}} && range_pr="$range_pr | ${bc[3]} day$SS"
    prHelpf ${OPTTh:+${bc[4]}} && range_pr="$range_pr | ${bc[4]} hour$SS"
    prHelpf ${OPTTm:+${bc[5]}} && range_pr="$range_pr | ${bc[5]} min$SS"
    prHelpf $range ;((!OPTT||OPTTs)) && range_pr="$range_pr | $range sec$SS"
    range_pr="${range_pr# | }" ;((OPTT&&OPTV)) && range_pr="${range_pr% *}"
    else #layout two
    ((n = ${#range}+SCL+1)) #range in seconds is the longest string
    prHelpf ${bc[0]} $n && range_pr=Year$SS$'\t'$SSS${bc[0]}
    prHelpf ${bc[1]} $n && range_pr="$range_pr"$'\n'Month$SS$'\t'$SSS${bc[1]}
    prHelpf ${bc[2]} $n && range_pr="$range_pr"$'\n'Week$SS$'\t'$SSS${bc[2]}
    prHelpf ${bc[3]} $n && range_pr="$range_pr"$'\n'Day$SS$'\t'$SSS${bc[3]}
    prHelpf ${bc[4]} $n && range_pr="$range_pr"$'\n'Hour$SS$'\t'$SSS${bc[4]}
    prHelpf ${bc[5]} $n && range_pr="$range_pr"$'\n'Min$SS$'\t'$SSS${bc[5]}
    prHelpf $range $((n - (SCL>0 ? (SCL+1) : 0) ))
    range_pr="$range_pr"$'\n'Sec$SS$'\t'$SSS$range
    range_pr="${range_pr#[$IFS]}"
    #https://www.themathdoctors.org/should-we-put-zero-before-a-decimal-point/
    ((OPTLAYOUT>1)) && { p= q=. ;for ((p=0;p<SCL;++p)) ;do q="${q}0" ;done
    range_pr="${range_pr// ./0.}" range_pr="${range_pr}${q}" ;}
    fi
    fi

    #set printing array with shell results
    sh=("$y" "$mo" "$w" "$d" "$h" "$m" "$s")
    ((y<0||mo<0||w<0||d<0||h<0||m<0||s<0)) && ret=${ret:-1} #negative unit error

    # Debugging
    if ((DEBUG))
    then
    #!#
    debugf "$@"
    fi

    #print results
    if ((!OPTVERBOSE))
    then if [[ ! $date1_iso8601_pr$date1_iso8601 ]]
    then date1_iso8601=$(printf \
    %04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d:%02d\\n \
    "$yearA" "$monthA" "$dayA" \
    "$hourA" "$minA" "$secA" \
    "${neg_tzA%1}" "$tzAh" "$tzAm" "$tzAs")
    date1_iso8601=${date1_iso8601%%*(:00)}
    else date1_iso8601_pr=${date1_iso8601_pr%%*(:00)} #remove excess zeroes
    fi
    if [[ ! $date2_iso8601_pr$date2_iso8601 ]]
    then date2_iso8601=$(printf \
    %04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d:%02d\\n \
    "$yearB" "$monthB" "$dayB" \
    "$hourB" "$minB" "$secB" \
    "${neg_tzB%1}" "$tzBh" "$tzBm" "$tzBs")
    date2_iso8601=${date2_iso8601%%*(:00)}
    else date2_iso8601_pr=${date2_iso8601_pr%%*(:00)}
    fi

    printf '%s%s\n%s%s%s\n%s%s%s\n%s\n' \
    DATES "${OPTDD+#}${badges}${neg_range%1}" \
    "${date1_iso8601_pr:-${date1_iso8601:-$inputA}}" ''${unix1:+$'\t'} "$unix1" \
    "${date2_iso8601_pr:-${date2_iso8601:-$inputB}}" ''${unix2:+$'\t'} "$unix2" \
    RANGES
    fi
    ((OPTVERBOSE<2 || OPTVERBOSE>2)) && printf '%dY %02dM %02dW %02dD %02dh %02dm %02ds\n' "${sh[@]}"
    ((OPTVERBOSE<3)) && printf '%s\n' "${range_pr:-$range secs}"

    return ${ret:-0}
    }

    #execute result checks against `datediff' and `date'
    debugf()
    {
    local iA iB tA tB dd ddout y_dd mo_dd w_dd d_dd h_dd m_dd s_dd range_check unix1t unix2t checkA_pr checkB_pr checkA_pr_dow checkB_pr_dow checkA_utc checkB_utc date_cmd_save TZ_save
    date_cmd_save=$DATE_CMD DATE_CMD=date TZ_save=$TZ TZ=UTC${TZ##*$GLOBUTC}

    [[ $2 = *[Tt:]*[+-]$GLOBTZ && $1 = *[Tt:]*[+-]$GLOBTZ ]] || echo warning: input dates are missing offset/tz bits! >&2
    iB="${2:-${inputB}}" iA="${1:-${inputA}}"
    iB="${iB:0:25}" iA="${iA:0:25}"
    ((${#iB}==10)) && iB=${iB}T00:00:00
    ((${#iA}==10)) && iA=${iA}T00:00:00
    ((${#iB}==19)) && iB="${iB}+00:00"
    ((${#iA}==19)) && iA="${iA}+00:00"
    iB=${iB/-00:00/+00:00} iA=${iA/-00:00/+00:00}

    #utc time strings
    tB=$(printf \
    %04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d\\n \
    "$yearB" "$monthB" "$dayB" \
    "$hourB" "$minB" "$secB" \
    "${neg_tzB%1}" $tzBh $tzBm)
    tA=$(printf \
    %04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d\\n \
    "$yearA" "$monthA" "$dayA" \
    "$hourA" "$minA" "$secA" \
    "${neg_tzA%1}" $tzAh $tzAm)
    tB=${tB:0:25} tA=${tA:0:25}
    tB=${tB/-00:00/+00:00} tA=${tA/-00:00/+00:00}

    if [[ $date_cmd_save = false ]]
    then
    if ((TZs)) || [[ $TZ = *:*:*:* ]] || [[ $tzA = *:*:*:* ]] || [[ $tzB = *:*:*:* ]]
    then echo "warning: \`datediff' and \`date' may not take offsets with seconds" >&2
    ((ret+=230))
    fi

    if ((TZh||TZm))
    then checkB_pr=$(datefun -Iseconds $iB)
    checkA_pr=$(datefun -Iseconds $iA)
    else checkB_pr=$date2_iso8601_pr checkA_pr=$date1_iso8601_pr
    fi
    if ((OPTRR))
    then checkB_pr_dow=$(datefun "$iB")
    checkA_pr_dow=$(datefun "$iA")
    fi

    checkB_utc=$(TZ=UTC datefun -Iseconds $iB)
    checkA_utc=$(TZ=UTC datefun -Iseconds $iA)
    #`date' iso offset must not exceed minute precision [+-]XX:XX !

    #check generated unix times against `date'
    unix2t=$(datefun "$iB" +%s)
    unix1t=$(datefun "$iA" +%s)
    range_check=$((unix2t-unix1t))
    fi
    if ((OPTRR))
    then checkB_pr_dow="${checkB_pr_dow:-$date2_iso8601_pr}"
    checkA_pr_dow="${checkA_pr_dow:-$date1_iso8601_pr}"
    fi

    #compound range check against `datediff'
    #`datediff' offset range is between -14h and +14h!
    ddout=$(datediff -f'%Y %m %w %d %H %M %S' "$tA" "$tB") || ((ret+=250))
    read y_dd mo_dd w_dd d_dd h_dd m_dd s_dd <<<"$ddout"
    dd=(${y_dd#-} $mo_dd $w_dd $d_dd $h_dd $m_dd $s_dd)

    DATE_CMD=$date_cmd_save TZ=$TZ_save
    {
    {
    { [[ ${date2_iso8601_pr:0:25} = $checkB_pr ]] &&
    [[ ${date1_iso8601_pr:0:25} = $checkA_pr ]] ;} ||
    { [[ ${date2_iso8601_pr:0:3} = ${checkB_pr_dow:0:3} ]] &&
    [[ ${date1_iso8601_pr:0:3} = ${checkA_pr_dow:0:3} ]] ;}
    } &&

    [[ $tB = ${checkB_utc:-$tB} ]] &&
    [[ $tA = ${checkA_utc:-$tA} ]] &&

    [[ $unix1 = ${unix1t:-$unix1} && $unix2 = ${unix2t:-$unix2} ]] &&
    [[ $range = "${range_check:-$range}" ]] &&

    [[ ${sh[*]} = "${dd[*]:-${sh[*]}}" ]]
    } || { echo -ne "\033[2K" >&2
    echo "\
    sh=${sh[*]} dd=${dd[*]} | "\
    "$iA $iB | "\
    "${range:-unavail} ${range_check:-unavail} | "\
    "${date1_iso8601_pr:0:25} $checkA_pr | "\
    "${date2_iso8601_pr:0:25} $checkB_pr | "\
    "${date1_iso8601_pr:0:3} ${checkA_pr_dow:0:3} | "\
    "${date2_iso8601_pr:0:3} ${checkB_pr_dow:0:3} | "\
    "$tB $checkB_utc | "\
    "$tA $checkA_utc | "\
    "${date_cmd_save%date}"

    ((ret+=1))
    }

    #((DEBUG>1)) && return ${ret:-0} #!#
    ((DEBUG>1)) && exit ${ret:-0} #!#
    return 0
    }

    #swap $varA/$varB or $var1/$var2 values
    pairSwapf()
    {
    local varname buf p q
    for varname
    do [[ $varname = *A* ]] && p=A q=B || p=1 q=2
    eval "buf=\"\$$varname\""
    eval "$varname=\"\$${varname/$p/$q}\" ${varname/$p/$q}=\"\$buf\""
    done
    }

    #printing helper
    #(A). check if floating point in $1 is `>0', set return signal and $SS to `s' when `>1.0'.
    #usage: prHelpf 1.23
    #(B). set padding of $1 length until [max] chars and set $SSS.
    #usage: prHelpf 1.23 [max]
    prHelpf()
    {
    local val valx int dec x z

    #(B)
    if (($#>1))
    then SSS= x=$(( ${2} - ${#1} ))
    for ((z=0;z<x;++z))
    do SSS="$SSS "
    done
    fi

    #(A)
    SS= val=${1#-} val=${val#0} valx=${val//[0.]} int=${val%.*}
    [[ $val = *.* ]] && dec=${val#*.} dec=${dec//0}
    [[ $1 && $OPTT ]] || ((valx)) || return
    (( int>1 || ( (int==1) && (dec) ) )) && SS=s
    return 0
    }


    ## Parse options
    while getopts 01234567890Ddef:hlRr@Vtuv opt
    do case $opt in
    [0-9]) SCL="$SCL$opt"
    ;;
    d) ((++DEBUG))
    ;;
    D) [[ $DATE_CMD = false ]] && OPTDD=false ;DATE_CMD=false
    ;;
    e) OPTE=1 OPTL=
    ;;
    f) INPUT_FMT="$OPTARG" OPTF=1 #input format string for `BSD date'
    ;;
    h) while read
    do [[ "$REPLY" = \#\ v* ]] && echo "$REPLY" && break
    done <"$0"
    echo "$HELP" ;exit
    ;;
    l) OPTL=1 OPTE=
    ;;
    R) OPTRR=1
    ;;
    r|@) OPTR=1
    ;;
    t|V) ((++OPTLAYOUT)) #option -V is deprecated
    ;;
    u) OPTU=1
    ;;
    v) ((++OPTVERBOSE, ++OPTV))
    ;;
    \?) exit 1
    ;;
    esac
    done
    shift $((OPTIND -1)); unset opt

    #set proper environment!
    SCL="${SCL:-1}" #scale defaults
    ((OPTU)) && TZ=UTC #set UTC time zone
    export TZ

    #stdin input
    [[ ${1//[$IFS]} = $GLOBOPT ]] && opt="$1" && shift
    if [[ $# -eq 0 && ! -t 0 ]]
    then
    globtest="*([$IFS])@($GLOBDATE?(+([$SEP])$GLOBTIME)|$GLOBTIME)*([$IFS])@($GLOBDATE?(+([$SEP])$GLOBTIME)|$GLOBTIME)?(+([$IFS])$GLOBOPT)*([$IFS])" #glob for two ISO8601 dates and possibly pos arg option for single unit range
    while IFS= read -r || [[ $REPLY ]]
    do ar=($REPLY) ;((${#ar[@]})) || continue
    if ((!$#))
    then set -- "$REPLY" ;((OPTL)) && break
    #check if arg contains TWO ISO8601 dates and break
    if [[ (${#ar[@]} -eq 3 || ${#ar[@]} -eq 2) && \ $REPLY = @(*[$IFS]$GLOBOPT*|$globtest) ]]
    then set -- $REPLY ;[[ $1 = $GLOBOPT ]] || break
    fi
    else if [[ ${#ar[@]} -eq 2 && \ $REPLY = @(*[$IFS]$GLOBOPT|$globtest) ]]
    then set -- "$@" $REPLY
    else set -- "$@" "$REPLY"
    fi ;break
    fi
    done ;unset ar globtest REPLY
    [[ ${1//[$IFS]} = $GLOBOPT ]] && opt="$1" && shift
    fi
    [[ $opt ]] && set -- "$@" "$opt"

    #print single time unit?
    opt="${opt:-${@: -1}}" opt="${opt//[$IFS]}"
    if [[ $opt = $GLOBOPT ]]
    then OPTT=1 OPTVERBOSE=2 OPTLAYOUT=
    case $opt in
    [yY]) OPTTy=1;;
    [mM][oO]) OPTTmo=1;;
    [wW]) OPTTw=1;;
    [dD]) OPTTd=1;;
    [hH]) OPTTh=1;;
    [mM]) OPTTm=1;;
    [sS]) OPTTs=1;;
    esac ;set -- "${@:1:$#-1}"
    else OPTTy=1 OPTTmo=1 OPTTw=1 OPTTd=1 OPTTh=1 OPTTm=1 OPTTs=1
    fi ;unset opt
    #caveat: `gnu date' understands `-d[a-z]', do `-d[a-z]0' to pass.
    [[ $1 = [a-zA-Z] || $2 = [a-zA-Z] ]] && { echo "err: illegal user input" >&2 ;exit 2 ;}

    #whitespace trimming
    if (($#>1))
    then set -- "${1#"${1%%[!$IFS]*}"}" "${2#"${2%%[!$IFS]*}"}" "${@:3}"
    set -- "${1%"${1##*[!$IFS]}"}" "${2%"${2##*[!$IFS]}"}" "${@:3}"
    elif (($#))
    then set -- "${1#"${1%%[!$IFS]*}"}" ;set -- "${1%"${1##*[!$IFS]}"}"
    fi

    #-r, unix times
    if ((OPTR && $#>1))
    then set -- @"${1#@}" @"${2#@}" "${@:3}"
    elif ((OPTR && $#))
    then set -- @"${1#@}"
    fi

    if ((OPTL || OPTE))
    then [[ $* ]] || set -- $($OPTDD printf '%(%Y)T' -1) || set -- 1970
    for year in $*
    do if ((OPTL))
    then isleap $year
    else easterf $year
    fi
    done
    else mainf "$@" #datediff fun
    fi

    ###
    Cheers!
    JSN

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From castAway@21:1/5 to All on Sat Nov 19 09:42:28 2022
    [continued from previous message]

    #secAtz secBtz secAprtz secBprtz
    ((sec${v}${p}tz=sec${v}-(tz${v}s*neg_tz${v}) )) #neg_tzA neg_tzB
    if ((sec${v}${p}tz<0))
    then ((min${v}${p}tz+=((sec${v}${p}tz-59)/60) , sec${v}${p}tz=(sec${v}${p}tz%60+60)%60))
    elif ((sec${v}${p}tz>59))
    then ((min${v}${p}tz+=(sec${v}${p}tz/60) , sec${v}${p}tz%=60))
    fi

    #minAtz minBtz minAprtz minBprtz
    ((min${v}${p}tz+=min${v}-(tz${v}m*neg_tz${v}) ))
    if ((min${v}${p}tz<0))
    then ((hour${v}${p}tz+=((min${v}${p}tz-59)/60) , min${v}${p}tz=(min${v}${p}tz%60+60)%60))
    elif ((min${v}${p}tz>59))
    then ((hour${v}${p}tz+=(min${v}${p}tz/60) , min${v}${p}tz%=60))
    fi

    #hourAtz hourBtz hourAprtz hourBprtz
    ((hour${v}${p}tz+=hour${v}-(tz${v}h*neg_tz${v}) ))
    if ((hour${v}${p}tz<0))
    then ((day${v}${p}tz+=((hour${v}${p}tz-23)/24) , hour${v}${p}tz=(hour${v}${p}tz%24+24)%24))
    elif ((hour${v}${p}tz>23))
    then ((day${v}${p}tz+=(hour${v}${p}tz/24) , hour${v}${p}tz%=24))
    fi

    #dayAtz dayBtz dayAprtz dayBprtz
    ((day${v}${p}tz+=day${v}))
    if ((day${v}${p}tz<1))
    then var=$(month_maxday "$((month${v}==1 ? 12 : month${v}-1))" "$((year${v}))")
    ((day${v}${p}tz+=var))
    if ((month${v}>1))
    then ((--month${v}${p}tz))
    else ((month${v}${p}tz-=month${v}))
    fi
    elif var=$(month_maxday "$((month${v}))" "$((year${v}))")
    ((day${v}${p}tz>var))
    then ((++month${v}${p}tz))
    ((day${v}${p}tz%=var))
    fi

    #monthAtz monthBtz monthAprtz monthBprtz
    ((month${v}${p}tz+=month${v}))
    if ((month${v}${p}tz<1))
    then ((--year${v}${p}tz))
    ((month${v}${p}tz+=12))
    elif ((month${v}${p}tz>12))
    then ((++year${v}${p}tz))
    ((month${v}${p}tz%=12))
    fi

    ((year${v}${p}tz+=year${v})) #yearAtz yearBtz yearAprtz yearBprtz
    done
    #modulus as (a%b + b)%b to avoid negative remainder.
    #<https://www.geeksforgeeks.org/modulus-on-negative-numbers/>

    if [[ $yearAtz ]]
    then (( yearA=yearAtz , monthA=monthAtz , dayA=dayAtz,
    hourA=hourAtz , minA=minAtz , secA=secAtz ,
    tzAh=0 , tzAm=0 , tzAs=0
    ))
    fi
    if [[ $yearBtz ]]
    then (( yearB=yearBtz , monthB=monthBtz , dayB=dayBtz,
    hourB=hourBtz , minB=minBtz , secB=secBtz ,
    tzBh=0 , tzBm=0 , tzBs=0
    ))
    fi

    if [[ $yearAprtz ]]
    then date1_iso8601_pr=$(printf \
    %04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d:%02d\\n \
    "$yearAprtz" "$monthAprtz" "${dayAprtz}" \
    "${hourAprtz}" "${minAprtz}" "${secAprtz}" \
    "${TZ_pos%1}" "$TZh" "$TZm" "$TZs")
    fi
    if [[ $yearBprtz ]]
    then date2_iso8601_pr=$(printf \
    %04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d:%02d\\n \
    "$yearBprtz" "$monthBprtz" "${dayBprtz}" \
    "${hourBprtz}" "${minBprtz}" "${secBprtz}" \
    "${TZ_pos%1}" "$TZh" "$TZm" "$TZs")
    fi

    elif [[ ! $unix2$OPTVERBOSE && $tzA$tzB$TZ = *+([A-Za-z_])* ]]
    then #echo "warning: input DATE or \$TZ contains timezone ID or name. Support requires package \`date'" >&2
    unset tzA tzB tzAh tzBh tzAm tzBm tzAs tzBs TZh TZm TZs
    else unset tzA tzB tzAh tzBh tzAm tzBm tzAs tzBs TZh TZm TZs
    fi #Offset is *from* UTC, while $TZ is *to* UTC.


    #sort `UTC' dates (if no `date' package)
    if [[ ! $unix2 ]] && ((
    (yearA>yearB)
    || ( (yearA==yearB) && (monthA>monthB) )
    || ( (yearA==yearB) && (monthA==monthB) && (dayA>dayB) )
    || ( (yearA==yearB) && (monthA==monthB) && (dayA==dayB) && (hourA>hourB) )
    || ( (yearA==yearB) && (monthA==monthB) && (dayA==dayB) && (hourA==hourB) && (minA>minB) )
    || ( (yearA==yearB) && (monthA==monthB) && (dayA==dayB) && (hourA==hourB) && (minA==minB) && (secA>secB) )
    ))
    then neg_range=-1
    for varname in inputA yearA monthA dayA hourA minA secA \
    yearAtz monthAtz dayAtz hourAtz minAtz secAtz \
    yearAprtz monthAprtz dayAprtz hourAprtz minAprtz secAprtz \
    tzA tzAh tzAm tzAs neg_tzA date1_iso8601 date1_iso8601_pr
    do #swap $varA/$varB or $var1/$var2 values
    [[ $varname = *A* ]] && p=A q=B || p=1 q=2
    eval "buf=\"\$$varname\""
    eval "$varname=\"\$${varname/$p/$q}\" ${varname/$p/$q}=\"\$buf\""
    done
    unset varname p q
    set -- "$2" "$1" "${@:3}"
    fi


    ##Count leap years and sum leap and non leap years days,
    for ((y_test=(yearA+1);y_test<yearB;++y_test))
    do
    #((y_test==0)) && continue #ISO8601 counts year zero, proleptic gregorian/julian do not
    is_leapyear $y_test && ((++leapcount))
    ((++years_between))
    ((monthcount += 12))
    done
    ##count days in non and leap years
    (( daycount_leap_years = (366 * leapcount) ))
    (( daycount_years = (365 * (years_between - leapcount) ) ))

    #date2 days so far this year (this month)
    #days in prior months `this' year
    ((month_tgt = (yearA==yearB ? monthA : 0) ))
    for ((month_test=(monthB-1);month_test>month_tgt;--month_test))
    do
    if ((month_test==2)) && is_leapyear $yearB
    then (( fullmonth_days += 29 ))
    else (( fullmonth_days += ${YEAR_MONTH_DAYS[month_test-1]} ))
    fi
    ((++monthcount))
    done

    #date1 days until end of `that' year
    #days in prior months `that' year
    ((yearA==yearB)) ||
    for ((month_test=(monthA+1);month_test<13;++month_test))
    do
    if ((month_test==2)) && is_leapyear $yearA
    then (( fullmonth_days += 29 ))
    else (( fullmonth_days += ${YEAR_MONTH_DAYS[month_test-1]} ))
    fi
    ((++monthcount))
    done
    ((fullmonth_days_save = fullmonth_days))

    #some info about input dates and their context..
    date3_month_max_day=$(month_maxday "$((monthB==1 ? 12 : monthB-1))" "$yearB")
    date1_month_max_day=$(month_maxday "$monthA" "$yearA")
    date1_year_days_adj=$(year_days_adj "$monthA" "$yearA")


    #set years and months
    (( y = years_between ))
    (( mo = ( monthcount - ( (years_between) ? (years_between * 12) : 0) ) ))

    #days left
    if ((yearA==yearB && monthA==monthB))
    then
    ((d_left = (dayB - dayA) ))
    ((d_left_save = d_left))
    elif ((dayA<dayB))
    then
    ((++mo))
    ((fullmonth_days += date1_month_max_day))
    ((d_left = (dayB - dayA) ))
    ((d_left_save = d_left))
    elif ((dayA>dayB))
    then #refinement rules (or hacks)
    ((d_left = ( (date3_month_max_day>=dayA) ? (date3_month_max_day-dayA) : (date1_month_max_day-dayA) ) + dayB ))
    ((d_left_save = (date1_month_max_day-dayA) + dayB ))
    if ((dayA>date3_month_max_day && date3_month_max_day<date1_month_max_day && dayB>1))
    then
    ((dayB>=dayA-date3_month_max_day)) && ##addon2 -- prevents negative days
    ((d_left -= date1_month_max_day-date3_month_max_day))
    ((d_left==0 && ( (24-hourA)+hourB<24 || ( (24-hourA)+hourB==24 && (60-minA)+minB<60 ) || ( (24-hourA)+hourB==24 && (60-minA)+minB==60 && (60-secA)+secB<60 ) ) && (++d_left) )) ##addon3 -- prevents breaking down a full month
    if ((d_left < 0))
    then if ((w))
    then ((--w , d_left+=7))
    elif ((mo))
    then ((--mo , w=date3_month_max_day/7 , d_left+=date3_month_max_day%7))
    elif ((y))
    then ((--y , mo+=11 , w=date3_month_max_day/7 , d_left+=date3_month_max_day%7))
    fi
    fi
    elif ((dayA>date3_month_max_day)) #dayB==1
    then
    ((d_left = (date1_month_max_day - dayA + date3_month_max_day + dayB) ))
    ((w = d_left/7 , d_left%=7))
    if ((mo))
    then ((--mo))
    elif ((y))
    then ((--y , mo+=11))
    fi
    fi
    else #`dayA' equals `dayB'
    ((++mo))
    ((fullmonth_days += date1_month_max_day))
    #((d_left_save = d_left)) #set to 0
    fi


    ((h += (24-hourA)+hourB))
    if ((h && h<24))
    then if ((d_left))
    then ((--d_left , ++ok))
    elif ((mo))
    then ((--mo , d_left+=date3_month_max_day-1 , ++ok))
    elif ((y))
    then ((--y , mo+=11 , d_left+=date3_month_max_day-1 , ++ok))
    fi
    fi
    ((h %= 24))

    ((m += (60-minA)+minB))
    if ((m && m<60))
    then if ((h))
    then ((--h))
    elif ((d_left))
    then ((--d_left , h+=23 , ++ok))
    elif ((mo))
    then ((--mo , d_left+=date3_month_max_day-1 , h+=23 , ++ok))
    elif ((y))
    then ((--y , mo+=11 , d_left+=date3_month_max_day-1 , h+=23 , ++ok))
    fi
    fi
    ((m %= 60))

    ((s = (60-secA)+secB))
    if ((s && s<60))
    then if ((m))
    then ((--m))
    elif ((h))
    then ((--h , m+=59))
    elif ((d_left))
    then ((--d_left , h+=23 , m+=59 , ++ok))
    elif ((mo))
    then ((--mo , d_left+=date3_month_max_day-1 , h+=23 , m+=59 , ++ok))
    elif ((y))
    then ((--y , mo+=11 , d_left+=date3_month_max_day-1 , h+=23 , m+=59 , ++ok))
    fi
    fi
    ((s %= 60))
    ((ok && (--d_left_save) ))

    ((m += s/60 , s %= 60))
    ((h += m/60 , m %= 60))
    ((d_left_save += h/24))
    ((d_left += h/24 , h %= 24))
    ((y += mo/12 , mo %= 12))
    ((w += d_left/7))
    ((d = d_left%7))


    #total sum of full days { range = unix2-unix1 }
    ((d_sum = ( (d_left_save) + (fullmonth_days + daycount_years + daycount_leap_years) ) ))
    ((range = (d_sum * 3600 * 24) + (h * 3600) + (m * 60) + s))

    #generate unix times arithmetically?
    ((GETUNIX)) && { echo ${neg_range%1}${range} ;unset GETUNIX ;return ${ret:-0} ;}
    if [[ ! $unix2 ]]
    then badges="$badges#"
    if ((
    (yearA>1970 ? yearA-1970 : 1970-yearA)
    > (yearB>1970 ? yearB-1970 : 1970-yearB)
    ))
    then var=$yearB-$monthB-${dayB}T$hourB:$minB:$secB varname=B #utc times
    else var=$yearA-$monthA-${dayA}T$hourA:$minA:$secA varname=A
    fi

    var=$(GETUNIX=1 DATE_CMD=false OPTVERBOSE=1 OPTRR= TZ= \
    mainf $EPOCH $var) || ((ret+=$?))

    if [[ $varname = B ]]
    then ((unix2=var , unix1=unix2-range))
    else ((unix1=var , unix2=unix1+range))
    fi

    if ((OPTRR)) #make RFC-5322 format string
    then if ! { date2_iso8601_pr=$(get_timef "$unix2" "$TIME_RFC5322_FMT") &&
    date1_iso8601_pr=$(get_timef "$unix1" "$TIME_RFC5322_FMT") ;}
    then #calculate Day Of Week (bash v<3.1)
    date2_diw=$(get_day_in_week $((unix2-( ( (TZh*60*60)+(TZm*60)+TZs)*TZ_neg) )) )
    date1_diw=$(get_day_in_week $((unix1-( ( (TZh*60*60)+(TZm*60)+TZs)*TZ_neg) )) )
    date2_iso8601_pr=$(printf \
    '%s, %02d %s %04d %02d:%02d:%02d %s%02d:%02d:%02d\n' \
    "${date2_diw:0:3}" "${dayBprtz:-${dayBtz:-$dayB}}" \
    "${MONTH_OF_YEAR[${monthBprtz:-${monthBtz:-$monthB}}-1]:0:3}" \
    "${yearBprtz:-${yearBtz:-$yearB}}" \
    "${hourBprtz:-${hourBtz:-$hourB}}" \
    "${minBprtz:-${minBtz:-$minB}}" \
    "${secBprtz:-${secBtz:-$secB}}" \
    "${TZ_pos%1}" "$TZh" "$TZm" "$TZs")
    date1_iso8601_pr=$(printf \
    '%s, %02d %s %04d %02d:%02d:%02d %s%02d:%02d:%02d\n' \
    "${date1_diw:0:3}" "${dayAprtz:-${dayAtz:-$dayA}}" \
    "${MONTH_OF_YEAR[${monthAprtz:-${monthAtz:-$monthA}}-1]:0:3}" \
    "${yearAprtz:-${yearAtz:-$yearA}}" \
    "${hourAprtz:-${hourAtz:-$hourA}}" \
    "${minAprtz:-${minAtz:-$minA}}" \
    "${secAprtz:-${secAtz:-$secA}}" \
    "${TZ_pos%1}" "$TZh" "$TZm" "$TZs")
    fi
    fi
    fi

    #single unit time durations (when `bc' is available)
    if ((OPTT || OPTVERBOSE<3))
    then if [[ $BASH_VERSION ]]
    then bc=( $(bc <<<" /* round argument 'x' to 'd' digits */
    define r(x, d) { auto r, s; if(0 > x) { return -r(-x, d); };
    r = x + 0.5*10^-d; s = scale; scale = d; r = r*10/10;
    scale = s; return r; }; scale = ($SCL + 1);
    r( (${years_between:-0} + ( (${range:-0} - ( (${daycount_years:-0} + ${daycount_leap_years:-0}) * 24 * 60 * 60) ) / (${date1_year_days_adj:-0} * 24 * 60 * 60) ) ) , $SCL); /** YEARS **/
    r( (${monthcount:-0} + ( (${range:-0} - (${fullmonth_days_save:-0} * 24 * 60 * 60) ) / (${date1_month_max_day:-0} * 24 * 60 * 60) ) ) , $SCL); /** MONTHS **/
    r( (${range:-0} / ( 7 * 24 * 60 * 60)) , $SCL); /** WEEKS **/
    r( (${range:-0} / (24 * 60 * 60)) , $SCL); /** DAYS **/
    r( (${range:-0} / (60 * 60)) , $SCL); /** HOURS **/
    r( (${range:-0} / 60) , $SCL); /** MINUTES **/")
    )
    bcy=${bc[0]} bcmo=${bc[1]} bcw=${bc[2]} bcd=${bc[3]} bch=${bc[4]} bcm=${bc[5]}
    #ARRAY: 0=YEARS 1=MONTHS 2=WEEKS 3=DAYS 4=HOURS 5=MINUTES
    else typeset -F $SCL bcy bcmo bcw bcd bch bcm
    bcy="${years_between:-0} + ( (${range:-0} - ( (${daycount_years:-0} + ${daycount_leap_years:-0}) * 24 * 60 * 60.) ) / (${date1_year_days_adj:-0} * 24 * 60 * 60.) )" #YEARS
    bcmo="${monthcount:-0} + ( (${range:-0} - (${fullmonth_days_save:-0} * 24 * 60 * 60.) ) / (${date1_month_max_day:-0} * 24 * 60 * 60.) )" #MONTHS
    bcw="${range:-0} / ( 7 * 24 * 60 * 60.)" #WEEKS
    bcd="${range:-0} / (24 * 60 * 60.)" #DAYS
    bch="${range:-0} / (60 * 60.)" #HOURS
    bcm="${range:-0} / 60." #MINUTES
    fi

    #choose layout of single units
    if ((OPTT || !OPTLAYOUT))
    then #layout one
    spcr=' | ' #spacer
    prHelpf ${OPTTy:+${bcy}} && range_pr="${bcy} year$SS"
    prHelpf ${OPTTmo:+${bcmo}} && range_pr="${range_pr}${range_pr:+$spcr}${bcmo} month$SS"
    prHelpf ${OPTTw:+${bcw}} && range_pr="${range_pr}${range_pr:+$spcr}${bcw} week$SS"
    prHelpf ${OPTTd:+${bcd}} && range_pr="${range_pr}${range_pr:+$spcr}${bcd} day$SS"
    prHelpf ${OPTTh:+${bch}} && range_pr="${range_pr}${range_pr:+$spcr}${bch} hour$SS"
    prHelpf ${OPTTm:+${bcm}} && range_pr="${range_pr}${range_pr:+$spcr}${bcm} min$SS"
    prHelpf $range ;((!OPTT||OPTTs)) && range_pr="$range_pr${range_pr:+$spcr}$range sec$SS"
    ((OPTT&&OPTV)) && range_pr="${range_pr%%*([$IFS])}" #bug in ksh93u+ ${var% *}
    else #layout two
    ((n = ${#range}+SCL+1)) #range in seconds is the longest string
    prHelpf ${bcy} $n && range_pr=Year$SS$'\t'$SSS${bcy}
    prHelpf ${bcmo} $n && range_pr="$range_pr"$'\n'Month$SS$'\t'$SSS${bcmo}
    prHelpf ${bcw} $n && range_pr="$range_pr"$'\n'Week$SS$'\t'$SSS${bcw}
    prHelpf ${bcd} $n && range_pr="$range_pr"$'\n'Day$SS$'\t'$SSS${bcd}
    prHelpf ${bch} $n && range_pr="$range_pr"$'\n'Hour$SS$'\t'$SSS${bch}
    prHelpf ${bcm} $n && range_pr="$range_pr"$'\n'Min$SS$'\t'$SSS${bcm}
    prHelpf $range $((n - (SCL>0 ? (SCL+1) : 0) ))
    range_pr="$range_pr"$'\n'Sec$SS$'\t'$SSS$range
    range_pr="${range_pr#*([$IFS])}"
    #https://www.themathdoctors.org/should-we-put-zero-before-a-decimal-point/
    ((OPTLAYOUT>1)) && { p= q=. ;for ((p=0;p<SCL;++p)) ;do q="${q}0" ;done
    range_pr="${range_pr// ./0.}" range_pr="${range_pr}${q}" ;}
    fi
    unset SS SSS
    fi

    #set printing array with shell results
    sh=("$y" "$mo" "$w" "$d" "$h" "$m" "$s")
    ((y<0||mo<0||w<0||d<0||h<0||m<0||s<0)) && ret=${ret:-1} #negative unit error

    # Debugging
    if ((DEBUG))
    then
    #!#
    debugf "$@"
    fi

    #print results
    if ((!OPTVERBOSE))
    then if [[ ! $date1_iso8601_pr$date1_iso8601 ]]
    then date1_iso8601=$(printf \
    %04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d:%02d\\n \
    "$yearA" "$monthA" "$dayA" \
    "$hourA" "$minA" "$secA" \
    "${neg_tzA%1}" "$tzAh" "$tzAm" "$tzAs")
    date1_iso8601=${date1_iso8601%%*(:00)}
    else date1_iso8601_pr=${date1_iso8601_pr%%*(:00)} #remove excess zeroes
    fi
    if [[ ! $date2_iso8601_pr$date2_iso8601 ]]
    then date2_iso8601=$(printf \
    %04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d:%02d\\n \
    "$yearB" "$monthB" "$dayB" \
    "$hourB" "$minB" "$secB" \
    "${neg_tzB%1}" "$tzBh" "$tzBm" "$tzBs")
    date2_iso8601=${date2_iso8601%%*(:00)}
    else date2_iso8601_pr=${date2_iso8601_pr%%*(:00)}
    fi

    printf '%s%s\n%s%s%s\n%s%s%s\n%s\n' \
    DATES "${OPTDD+#}${badges}${neg_range%1}" \
    "${date1_iso8601_pr:-${date1_iso8601:-$inputA}}" ''${unix1:+$'\t'} "$unix1" \
    "${date2_iso8601_pr:-${date2_iso8601:-$inputB}}" ''${unix2:+$'\t'} "$unix2" \
    RANGES
    fi
    prfmt='%dY %02dM %02dW %02dD %02dh %02dm %02ds' #print format for the compound range
    ((OPTVERBOSE>3)) && prfmt='%dY%02dM%02dW%02dD%02dh%02dm%02ds' #AST `date -E' style
    ((OPTVERBOSE<2 || OPTVERBOSE>2)) && printf "${prfmt}\n" "${sh[@]}"
    ((OPTVERBOSE<3)) && printf '%s\n' "${range_pr:-$range secs}"

    return ${ret:-0}
    }

    #execute result checks against `datediff' and `date'
    #check manually in case of divergence as this function is overloaded
    #beware of opt -R and unset $TZ and offsets (we defaults to UTC while `date' may set random offsets)
    function debugf
    {
    unset iA iB tA tB dd ddout y_dd mo_dd w_dd d_dd h_dd m_dd s_dd range_check unix1t unix2t checkA_pr checkB_pr checkA_pr_dow checkB_pr_dow checkA_utc checkB_utc date_cmd_save TZ_save brk
    date_cmd_save="${DATE_CMD}" DATE_CMD=date TZ_save=$TZ TZ=UTC${TZ##*$GLOBUTC}

    [[ $2 = *[Tt:]*[+-]$GLOBTZ && $1 = *[Tt:]*[+-]$GLOBTZ ]] || echo warning: input dates are missing offset/tz bits! >&2
    iB="${2:-${inputB}}" iA="${1:-${inputA}}"
    iB="${iB:0:25}" iA="${iA:0:25}"
    ((${#iB}==10)) && iB=${iB}T00:00:00
    ((${#iA}==10)) && iA=${iA}T00:00:00
    ((${#iB}==19)) && iB="${iB}+00:00"
    ((${#iA}==19)) && iA="${iA}+00:00"
    iB=${iB/-00:00/+00:00} iA=${iA/-00:00/+00:00}

    #utc time strings
    tB=$(printf \
    %04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d\\n \
    "$yearB" "$monthB" "$dayB" \
    "$hourB" "$minB" "$secB" \
    "${neg_tzB%1}" $tzBh $tzBm)
    tA=$(printf \
    %04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d\\n \
    "$yearA" "$monthA" "$dayA" \
    "$hourA" "$minA" "$secA" \
    "${neg_tzA%1}" $tzAh $tzAm)
    tB=${tB:0:25} tA=${tA:0:25}
    tB=${tB/-00:00/+00:00} tA=${tA/-00:00/+00:00}

    if [[ $date_cmd_save = false ]]
    then
    if ((TZs)) || [[ $TZ = *:*:*:* ]] || [[ $tzA = *:*:*:* ]] || [[ $tzB = *:*:*:* ]]
    then echo "warning: \`datediff' and \`date' may not take offsets with seconds" >&2
    ((ret+=230))
    fi

    if ((TZh||TZm))
    then checkB_pr=$(datefun -Iseconds $iB)
    checkA_pr=$(datefun -Iseconds $iA)
    else checkB_pr=$date2_iso8601_pr checkA_pr=$date1_iso8601_pr
    fi
    if ((OPTRR))
    then checkB_pr_dow=$(datefun "$iB")
    checkA_pr_dow=$(datefun "$iA")
    fi

    checkB_utc=$(TZ=UTC datefun -Iseconds $iB)
    checkA_utc=$(TZ=UTC datefun -Iseconds $iA)
    #`date' iso offset must not exceed minute precision [+-]XX:XX !

    #check generated unix times against `date'
    unix2t=$(datefun "$iB" +%s)
    unix1t=$(datefun "$iA" +%s)
    range_check=$((unix2t-unix1t))
    fi
    if ((OPTRR))
    then checkB_pr_dow="${checkB_pr_dow:-$date2_iso8601_pr}"
    checkA_pr_dow="${checkA_pr_dow:-$date1_iso8601_pr}"
    fi

    #compound range check against `datediff'
    #`datediff' offset range is between -14h and +14h!
    ddout=$(datediff -f'%Y %m %w %d %H %M %S' "$tA" "$tB") || ((ret+=250))
    read y_dd mo_dd w_dd d_dd h_dd m_dd s_dd <<<"$ddout"
    dd=(${y_dd#-} $mo_dd $w_dd $d_dd $h_dd $m_dd $s_dd)

    DATE_CMD="$date_cmd_save" TZ=$TZ_save
    {
    {
    { [[ ${date2_iso8601_pr:0:25} = $checkB_pr ]] &&
    [[ ${date1_iso8601_pr:0:25} = $checkA_pr ]] ;} ||
    { [[ ${date2_iso8601_pr:0:3} = ${checkB_pr_dow:0:3} ]] &&
    [[ ${date1_iso8601_pr:0:3} = ${checkA_pr_dow:0:3} ]] ;}
    } &&

    [[ $tB = ${checkB_utc:-$tB} ]] &&
    [[ $tA = ${checkA_utc:-$tA} ]] &&

    [[ $unix1 = ${unix1t:-$unix1} && $unix2 = ${unix2t:-$unix2} ]] &&
    [[ $range = "${range_check:-$range}" ]] &&

    [[ ${sh[*]} = "${dd[*]:-${sh[*]}}" ]]
    } || { #brk='\n'
    echo -ne "\033[2K" >&2
    echo ${brk+-e} "\
    sh=${sh[*]} dd=${dd[*]} | $brk"\
    "$iA $iB | $brk"\
    "${range:-unavail} ${range_check:-unavail} | $brk"\
    "${date1_iso8601_pr:0:25} $checkA_pr | $brk"\
    "${date2_iso8601_pr:0:25} $checkB_pr | $brk"\
    "${date1_iso8601_pr:0:3} ${checkA_pr_dow:0:3} | $brk"\
    "${date2_iso8601_pr:0:3} ${checkB_pr_dow:0:3} | $brk"\
    "$tB $checkB_utc | $brk"\
    "$tA $checkA_utc | $brk"\
    "$unix1 $unix1t | $brk"\
    "$unix2 $unix2t | $brk"\
    "${date_cmd_save%date}"

    ((ret+=1))
    }

    #((DEBUG>1)) && return ${ret:-0} #!#
    ((DEBUG>1)) && exit ${ret:-0} #!#
    return 0
    }


    ## Parse options
    while getopts 01234567890DdeFf:hlmRr@tuv opt
    do case $opt in
    [0-9]) SCL="$SCL$opt"
    ;;
    d) ((++DEBUG))
    ;;
    D) [[ ${DATE_CMD} = false ]] && OPTDD=1 ;DATE_CMD=false
    ;;
    e) OPTE=1 OPTL=
    ;;
    F) ((++OPTFF))
    ;;
    f) INPUT_FMT="$OPTARG" OPTF=1 #input format string for `BSD date'
    ;;
    h) while read
    do [[ "$REPLY" = \#\ v* ]] && echo "$REPLY $SHELL" && break
    done <"$0"
    echo "$HELP" ;exit
    ;;
    l) OPTL=1 OPTE=
    ;;
    m) OPTM=1
    ;;
    R) OPTRR=1
    ;;
    r|@) OPTR=1
    ;;
    t) ((++OPTLAYOUT))
    ;;
    u) OPTU=1
    ;;
    v) ((++OPTVERBOSE, ++OPTV))
    ;;
    \?) exit 1
    ;;
    esac
    done
    shift $((OPTIND -1)); unset opt

    #set proper environment!
    SCL="${SCL:-1}" #scale defaults
    ((OPTU)) && TZ=UTC #set UTC time zone
    export TZ

    #test for BSD or GNU date for datefun()
    [[ ${DATE_CMD} ]] ||
    if DATE_CMD=date;
    ! ${DATE_CMD} --version
    then if gdate --version
    then DATE_CMD=gdate
    elif command -v date
    then BSDDATE=1
    else DATE_CMD=false
    fi
    fi >/dev/null 2>&1

    #stdin input (skip it for option -F)
    [[ ${1//[$IFS]}$OPTFF = $GLOBOPT ]] && opt="$1" && shift
    if ((!($#+OPTFF) )) && [[ ! -t 0 ]]
    then
    globtest="*([$IFS])@($GLOBDATE?(+([$SEP])$GLOBTIME)|$GLOBTIME)*([$IFS])@($GLOBDATE?(+([$SEP])$GLOBTIME)|$GLOBTIME)?(+([$IFS])$GLOBOPT)*([$IFS])" #glob for two ISO8601 dates and possibly pos arg option for single unit range
    while IFS= read -r || [[ $REPLY ]]
    do ar=($REPLY) ;((${#ar[@]})) || continue
    if ((!$#))
    then set -- "$REPLY" ;((OPTL)) && break
    #check if arg contains TWO ISO8601 dates and break
    if ((${#ar[@]}==3||${#ar[@]}==2)) && [[ \ $REPLY = @(*[$IFS]$GLOBOPT*|$globtest) ]]
    then set -- $REPLY ;[[ $1 = $GLOBOPT ]] || break
    fi
    else if ((${#ar[@]}==2)) && [[ \ $REPLY = @(*[$IFS]$GLOBOPT|$globtest) ]]
    then set -- "$@" $REPLY
    else set -- "$@" "$REPLY"
    fi ;break
    fi
    done ;unset ar globtest REPLY
    [[ ${1//[$IFS]} = $GLOBOPT ]] && opt="$1" && shift
    fi
    [[ $opt ]] && set -- "$@" "$opt"

    #set single time unit
    opt="${opt:-${@: -1}}" opt="${opt//[$IFS]}"
    if [[ $opt$OPTFF = $GLOBOPT ]]
    then OPTT=1 OPTVERBOSE=2 OPTLAYOUT=
    case $opt in
    [yY]) OPTTy=1;;
    [mM][oO]) OPTTmo=1;;
    [wW]) OPTTw=1;;
    [dD]) OPTTd=1;;
    [hH]) OPTTh=1;;
    [mM]) OPTTm=1;;
    [sS]) OPTTs=1;;
    esac ;set -- "${@:1:$#-1}"
    else OPTTy=1 OPTTmo=1 OPTTw=1 OPTTd=1 OPTTh=1 OPTTm=1 OPTTs=1
    fi ;unset opt
    #caveat: `gnu date' understands `-d[a-z]', do `-d[a-z]0' to pass.

    #whitespace trimming
    if (($#>1))
    then set -- "${1#"${1%%[!$IFS]*}"}" "${2#"${2%%[!$IFS]*}"}" "${@:3}"
    set -- "${1%"${1##*[!$IFS]}"}" "${2%"${2##*[!$IFS]}"}" "${@:3}"
    elif (($#))
    then set -- "${1#"${1%%[!$IFS]*}"}" ;set -- "${1%"${1##*[!$IFS]}"}"
    fi

    if ((OPTL))
    then for YEAR
    do is_year "$YEAR" || continue
    if ! is_leapyear_verbose "$YEAR"
    then (($?>1)) && RET=2 ;RET="${RET:-$?}"
    fi
    done ;exit $RET
    elif ((OPTE))
    then for YEAR
    do is_year "$YEAR" || continue
    DATE=$(easterf "$YEAR") ;echo $DATE
    done
    elif ((OPTM))
    then for DATE_Y #fill in months and days
    do if [[ $DATE_Y = +([0-9]) ]]
    then set -- ;OPTM=2
    for ((M=1;M<=12;++M)) ;do set -- "$@" "${DATE_Y}-$M" ;done
    else set -- "$DATE_Y" ;PHASE_SKIP=
    fi
    for DATE_M
    do if [[ $DATE_M = +([0-9])[$SEP]+([0-9]) ]]
    then set -- ;OPTM=2
    DMAX=$(month_maxday "${DATE_M#*[$SEP]}" "${DATE_M%[$SEP]*}")
    for ((D=1;D<=DMAX;++D)) ;do set -- "$@" "${DATE_M}-$D" ;done
    else set -- "$DATE_M" ;PHASE_SKIP=
    fi
    for DATE
    do set -- ${DATE//[$SEP]/ } #input is ISO8601
    phase_of_the_moon "$3" "$2" "$1"
    done
    done
    done
    elif ((OPTFF))
    then friday_13th "$@"
    else
    #-r, unix times
    if ((OPTR && $#>1))
    then set -- @"${1#@}" @"${2#@}" "${@:3}"
    elif ((OPTR && $#))
    then set -- @"${1#@}"
    fi
    mainf "$@"
    fi

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)