3 # Desc: Template to indicate time duration in ISO-8601 format 
   5 yell
() { echo "$0: $*" >&2; } # Yell, Die, Try Three-Fingered Claw technique; # Ref/Attrib: https://stackoverflow.com/a/25515370 
   6 die
() { yell 
"$*"; exit 111; } 
   7 try
() { "$@" || die 
"cannot $*"; } 
   9     # Desc: Output approximate time duration string before given time (default:current date) 
  10     # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information 
  11     # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2) 
  12     # Usage: timeDuration [arg1] ([arg2]) 
  14     # Input: arg1: seconds as base 10 integer >= 0  (ex: 3601) 
  15     #        arg2: precision level (optional; default=2) 
  16     # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S") 
  17     # Example: 'timeDuration 111111 3' yields 'P1DT6H51M' 
  18     # Depends: date 8 (gnucoreutils), yell,  
  19     local returnState ARG1 ARG2 arg1Valid arg2Valid remainder precision witherPrecision
 
  20     local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
 
  21     local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
 
  22     local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
 
  26     precision
=2; # set default precision 
  27     returnState
="true"; # set default return state 
  29     # Check that between one and two arguments is supplied 
  30     if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then 
  31         yell 
"ERROR:Invalid number of arguments:$# . Exiting."; 
  32         returnState
="ERROR_INPUT"; fi 
  34     # Check that arg1 provided 
  35     if [[ $# -ge 1 ]]; then 
  36         # Check that arg1 is a positive integer 
  37         if [[ "$ARG1" =~ ^
[[:digit
:]]+$ 
]]; then 
  40             yell 
"ERROR:ARG1 not a digit."; 
  41             returnState
="ERROR_INPUT"; 
  45         yell 
"ERROR:No argument provided. Exiting."; 
  49     # Consider whether arg2 was provided 
  50     if  [[ $# -eq 2 ]]; then 
  51         # Check that the second arg is a positive integer 
  52         if [[ "$ARG2" =~ ^
[[:digit
:]]+$ 
]] && [[ "$ARG2" -gt 0 ]]; then 
  56             yell 
"ERROR:ARG2 not a positive integer. (is $ARG2 ). Leaving early."; 
  57             returnState
="ERROR_INPUT"; 
  64     remainder
="$ARG1" ; # seconds 
  65     ## Calculate full years Y, update remainder 
  66     fullYears
=$
(( remainder 
/ (365*24*60*60) )); 
  67     remainder
=$
(( remainder 
- (fullYears
*365*24*60*60) )); 
  68     ## Calculate full months M, update remainder 
  69     fullMonths
=$
(( remainder 
/ (30*24*60*60) )); 
  70     remainder
=$
(( remainder 
- (fullMonths
*30*24*60*60) )); 
  71     ## Calculate full days D, update remainder 
  72     fullDays
=$
(( remainder 
/ (24*60*60) )); 
  73     remainder
=$
(( remainder 
- (fullDays
*24*60*60) )); 
  74     ## Calculate full hours H, update remainder 
  75     fullHours
=$
(( remainder 
/ (60*60) )); 
  76     remainder
=$
(( remainder 
- (fullHours
*60*60) )); 
  77     ## Calculate full minutes M, update remainder 
  78     fullMinutes
=$
(( remainder 
/ (60) )); 
  79     remainder
=$
(( remainder 
- (fullMinutes
*60) )); 
  80     ## Calculate full seconds S, update remainder 
  81     fullSeconds
=$
(( remainder 
/ (1) )); 
  82     remainder
=$
(( remainder 
- (remainder
*1) )); 
  83     ## Check which fields filled 
  84     if [[ $fullYears -gt 0 ]]; then hasYears
="true"; else hasYears
="false"; fi 
  85     if [[ $fullMonths -gt 0 ]]; then hasMonths
="true"; else hasMonths
="false"; fi 
  86     if [[ $fullDays -gt 0 ]]; then hasDays
="true"; else hasDays
="false"; fi 
  87     if [[ $fullHours -gt 0 ]]; then hasHours
="true"; else hasHours
="false"; fi 
  88     if [[ $fullMinutes -gt 0 ]]; then hasMinutes
="true"; else hasMinutes
="false"; fi 
  89     if [[ $fullSeconds -gt 0 ]]; then hasSeconds
="true"; else hasSeconds
="false"; fi 
  91     ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2) 
  92     witherPrecision
="false" 
  95     if $hasYears && [[ $precision -gt 0 ]]; then 
  97         witherPrecision
="true"; 
 101     if $witherPrecision; then ((precision--
)); fi; 
 104     if $hasMonths && [[ $precision -gt 0 ]]; then 
 105         displayMonths
="true"; 
 106         witherPrecision
="true"; 
 108         displayMonths
="false"; 
 110     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 111         displayMonths
="true"; 
 113     if $witherPrecision; then ((precision--
)); fi; 
 116     if $hasDays && [[ $precision -gt 0 ]]; then 
 118         witherPrecision
="true"; 
 122     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 125     if $witherPrecision; then ((precision--
)); fi; 
 128     if $hasHours && [[ $precision -gt 0 ]]; then 
 130         witherPrecision
="true"; 
 132         displayHours
="false"; 
 134     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 137     if $witherPrecision; then ((precision--
)); fi; 
 140     if $hasMinutes && [[ $precision -gt 0 ]]; then 
 141         displayMinutes
="true"; 
 142         witherPrecision
="true"; 
 144         displayMinutes
="false"; 
 146     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 147         displayMinutes
="true"; 
 149     if $witherPrecision; then ((precision--
)); fi; 
 153     if $hasSeconds && [[ $precision -gt 0 ]]; then 
 154         displaySeconds
="true"; 
 155         witherPrecision
="true"; 
 157         displaySeconds
="false"; 
 159     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 160         displaySeconds
="true"; 
 162     if $witherPrecision; then ((precision--
)); fi; 
 166     ## Determine whether or not the "T" separator is needed to separate date and time elements 
 167     if ( $displayHours || 
$displayMinutes || 
$displaySeconds); then 
 168         displayDateTime
="true"; else displayDateTime
="false"; fi 
 170     ## Construct duration output string 
 172     if $displayYears; then 
 173         OUTPUT
=$OUTPUT$fullYears"Y"; fi 
 174     if $displayMonths; then 
 175         OUTPUT
=$OUTPUT$fullMonths"M"; fi 
 176     if $displayDays; then 
 177         OUTPUT
=$OUTPUT$fullDays"D"; fi 
 178     if $displayDateTime; then 
 179         OUTPUT
=$OUTPUT"T"; fi 
 180     if $displayHours; then 
 181         OUTPUT
=$OUTPUT$fullHours"H"; fi 
 182     if $displayMinutes; then 
 183         OUTPUT
=$OUTPUT$fullMinutes"M"; fi 
 184     if $displaySeconds; then 
 185         OUTPUT
=$OUTPUT$fullSeconds"S"; fi 
 187     ## Output duration string to stdout 
 188     if [[ "$returnState" = "true" ]]; then echo "$OUTPUT"; fi 
 190     #===Determine function return code=== 
 191     if [ "$returnState" = "true" ]; then 
 194         echo "$returnState" 1>&2; 
 198 } # Get duration (ex: PT10M4S ) 
 200 #==BEGIN sample code== 
 201 echo "Precision 6 duration:$(timeDuration "$
(date +%s
)" 6)" 
 202 echo "Precision 5 duration:$(timeDuration "$
(date +%s
)" 5)" 
 203 echo "Precision 4 duration:$(timeDuration "$
(date +%s
)" 4)" 
 204 echo "Precision 3 duration:$(timeDuration "$
(date +%s
)" 3)" 
 205 echo "Precision 2 duration:$(timeDuration "$
(date +%s
)" 2)" 
 206 echo "Precision 1 duration:$(timeDuration "$
(date +%s
)" 1)" 
 207 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)" 
 211 # Author: Steven Baltakatei Sandoval