#!/bin/bash # Desc: Template to indicate time duration in ISO-8601 format yell() { echo "$0: $*" >&2; } # Yell, Die, Try Three-Fingered Claw technique; # Ref/Attrib: https://stackoverflow.com/a/25515370 die() { yell "$*"; exit 111; } try() { "$@" || die "cannot $*"; } timeDuration(){ # Desc: Output approximate time duration string before given time (default:current date) # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2) # Usage: timeDuration [arg1] ([arg2]) # Version: 1.0.0 # Input: arg1: seconds as base 10 integer >= 0 (ex: 3601) # arg2: precision level (optional; default=2) # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S") # Example: 'timeDuration 111111 3' yields 'P1DT6H51M' # Depends: date 8 (gnucoreutils), yell, local returnState ARG1 ARG2 arg1Valid arg2Valid remainder precision witherPrecision local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds ARG1="$1"; ARG2="$2"; precision=2; # set default precision returnState="true"; # set default return state # Check that between one and two arguments is supplied if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then yell "ERROR:Invalid number of arguments:$# . Exiting."; returnState="ERROR_INPUT"; fi # Check that arg1 provided if [[ $# -ge 1 ]]; then # Check that arg1 is a positive integer if [[ "$ARG1" =~ ^[[:digit:]]+$ ]]; then arg1Valid="true"; else yell "ERROR:ARG1 not a digit."; returnState="ERROR_INPUT"; arg1Valid="false"; fi else yell "ERROR:No argument provided. Exiting."; exit 1; fi # Consider whether arg2 was provided if [[ $# -eq 2 ]]; then # Check that the second arg is a positive integer if [[ "$ARG2" =~ ^[[:digit:]]+$ ]] && [[ "$ARG2" -gt 0 ]]; then arg2Valid="true"; precision="$ARG2"; else yell "ERROR:ARG2 not a positive integer. (is $ARG2 ). Leaving early."; returnState="ERROR_INPUT"; arg2Valid="false"; fi; else arg2Valid="false"; fi; remainder="$ARG1" ; # seconds ## Calculate full years Y, update remainder fullYears=$(( remainder / (365*24*60*60) )); remainder=$(( remainder - (fullYears*365*24*60*60) )); ## Calculate full months M, update remainder fullMonths=$(( remainder / (30*24*60*60) )); remainder=$(( remainder - (fullMonths*30*24*60*60) )); ## Calculate full days D, update remainder fullDays=$(( remainder / (24*60*60) )); remainder=$(( remainder - (fullDays*24*60*60) )); ## Calculate full hours H, update remainder fullHours=$(( remainder / (60*60) )); remainder=$(( remainder - (fullHours*60*60) )); ## Calculate full minutes M, update remainder fullMinutes=$(( remainder / (60) )); remainder=$(( remainder - (fullMinutes*60) )); ## Calculate full seconds S, update remainder fullSeconds=$(( remainder / (1) )); remainder=$(( remainder - (remainder*1) )); ## Check which fields filled if [[ $fullYears -gt 0 ]]; then hasYears="true"; else hasYears="false"; fi if [[ $fullMonths -gt 0 ]]; then hasMonths="true"; else hasMonths="false"; fi if [[ $fullDays -gt 0 ]]; then hasDays="true"; else hasDays="false"; fi if [[ $fullHours -gt 0 ]]; then hasHours="true"; else hasHours="false"; fi if [[ $fullMinutes -gt 0 ]]; then hasMinutes="true"; else hasMinutes="false"; fi if [[ $fullSeconds -gt 0 ]]; then hasSeconds="true"; else hasSeconds="false"; fi ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2) witherPrecision="false" ### Years if $hasYears && [[ $precision -gt 0 ]]; then displayYears="true"; witherPrecision="true"; else displayYears="false"; fi; if $witherPrecision; then ((precision--)); fi; ### Months if $hasMonths && [[ $precision -gt 0 ]]; then displayMonths="true"; witherPrecision="true"; else displayMonths="false"; fi; if $witherPrecision && [[ $precision -gt 0 ]]; then displayMonths="true"; fi; if $witherPrecision; then ((precision--)); fi; ### Days if $hasDays && [[ $precision -gt 0 ]]; then displayDays="true"; witherPrecision="true"; else displayDays="false"; fi; if $witherPrecision && [[ $precision -gt 0 ]]; then displayDays="true"; fi; if $witherPrecision; then ((precision--)); fi; ### Hours if $hasHours && [[ $precision -gt 0 ]]; then displayHours="true"; witherPrecision="true"; else displayHours="false"; fi; if $witherPrecision && [[ $precision -gt 0 ]]; then displayHours="true"; fi; if $witherPrecision; then ((precision--)); fi; ### Minutes if $hasMinutes && [[ $precision -gt 0 ]]; then displayMinutes="true"; witherPrecision="true"; else displayMinutes="false"; fi; if $witherPrecision && [[ $precision -gt 0 ]]; then displayMinutes="true"; fi; if $witherPrecision; then ((precision--)); fi; ### Seconds if $hasSeconds && [[ $precision -gt 0 ]]; then displaySeconds="true"; witherPrecision="true"; else displaySeconds="false"; fi; if $witherPrecision && [[ $precision -gt 0 ]]; then displaySeconds="true"; fi; if $witherPrecision; then ((precision--)); fi; ## Determine whether or not the "T" separator is needed to separate date and time elements if ( $displayHours || $displayMinutes || $displaySeconds); then displayDateTime="true"; else displayDateTime="false"; fi ## Construct duration output string OUTPUT="P" if $displayYears; then OUTPUT=$OUTPUT$fullYears"Y"; fi if $displayMonths; then OUTPUT=$OUTPUT$fullMonths"M"; fi if $displayDays; then OUTPUT=$OUTPUT$fullDays"D"; fi if $displayDateTime; then OUTPUT=$OUTPUT"T"; fi if $displayHours; then OUTPUT=$OUTPUT$fullHours"H"; fi if $displayMinutes; then OUTPUT=$OUTPUT$fullMinutes"M"; fi if $displaySeconds; then OUTPUT=$OUTPUT$fullSeconds"S"; fi ## Output duration string to stdout if [[ "$returnState" = "true" ]]; then echo "$OUTPUT"; fi #===Determine function return code=== if [ "$returnState" = "true" ]; then return 0; else echo "$returnState" 1>&2; return 1; fi } # Get duration (ex: PT10M4S ) #==BEGIN sample code== echo "Precision 6 duration:$(timeDuration "$(date +%s)" 6)" echo "Precision 5 duration:$(timeDuration "$(date +%s)" 5)" echo "Precision 4 duration:$(timeDuration "$(date +%s)" 4)" echo "Precision 3 duration:$(timeDuration "$(date +%s)" 3)" echo "Precision 2 duration:$(timeDuration "$(date +%s)" 2)" echo "Precision 1 duration:$(timeDuration "$(date +%s)" 1)" echo "Precision 6 duration:$(timeDuration $((60+60*60+60*60*24+60*60*24*30+60*60*24*365 - (60+60*60+60*60*24+60*60*24*30) )) 6)" timeDuration "$@" #==END sample code== # Author: Steven Baltakatei Sandoval # License: GPLv3+